]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit '7c21f91b15b7604f818565646b686d90f99d1baf' into clippyup
authorflip1995 <philipp.krones@embecosm.com>
Thu, 5 May 2022 14:12:52 +0000 (15:12 +0100)
committerflip1995 <philipp.krones@embecosm.com>
Thu, 5 May 2022 14:12:52 +0000 (15:12 +0100)
292 files changed:
1  2 
src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.yml
src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.yml
src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.yml
src/tools/clippy/.github/workflows/clippy.yml
src/tools/clippy/.github/workflows/clippy_bors.yml
src/tools/clippy/.github/workflows/clippy_dev.yml
src/tools/clippy/.github/workflows/deploy.yml
src/tools/clippy/.github/workflows/remark.yml
src/tools/clippy/CHANGELOG.md
src/tools/clippy/clippy_dev/Cargo.toml
src/tools/clippy/clippy_dev/src/lib.rs
src/tools/clippy/clippy_dev/src/main.rs
src/tools/clippy/clippy_dev/src/new_lint.rs
src/tools/clippy/clippy_dev/src/setup/mod.rs
src/tools/clippy/clippy_dev/src/update_lints.rs
src/tools/clippy/clippy_lints/src/attrs.rs
src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
src/tools/clippy/clippy_lints/src/bytes_count_to_len.rs
src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs
src/tools/clippy/clippy_lints/src/casts/mod.rs
src/tools/clippy/clippy_lints/src/collapsible_if.rs
src/tools/clippy/clippy_lints/src/doc.rs
src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
src/tools/clippy/clippy_lints/src/empty_drop.rs
src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs
src/tools/clippy/clippy_lints/src/eta_reduction.rs
src/tools/clippy/clippy_lints/src/format_push_string.rs
src/tools/clippy/clippy_lints/src/functions/must_use.rs
src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs
src/tools/clippy/clippy_lints/src/identity_op.rs
src/tools/clippy/clippy_lints/src/implicit_hasher.rs
src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
src/tools/clippy/clippy_lints/src/inherent_impl.rs
src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
src/tools/clippy/clippy_lints/src/large_include_file.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_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/lifetimes.rs
src/tools/clippy/clippy_lints/src/literal_representation.rs
src/tools/clippy/clippy_lints/src/loops/never_loop.rs
src/tools/clippy/clippy_lints/src/manual_bits.rs
src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
src/tools/clippy/clippy_lints/src/map_clone.rs
src/tools/clippy/clippy_lints/src/match_result_ok.rs
src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs
src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
src/tools/clippy/clippy_lints/src/matches/mod.rs
src/tools/clippy/clippy_lints/src/matches/needless_match.rs
src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs
src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs
src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs
src/tools/clippy/clippy_lints/src/misc_early/mod.rs
src/tools/clippy/clippy_lints/src/missing_inline.rs
src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs
src/tools/clippy/clippy_lints/src/needless_late_init.rs
src/tools/clippy/clippy_lints/src/new_without_default.rs
src/tools/clippy/clippy_lints/src/non_expressive_names.rs
src/tools/clippy/clippy_lints/src/octal_escapes.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/ptr.rs
src/tools/clippy/clippy_lints/src/pub_use.rs
src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
src/tools/clippy/clippy_lints/src/renamed_lints.rs
src/tools/clippy/clippy_lints/src/same_name_method.rs
src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs
src/tools/clippy/clippy_lints/src/strings.rs
src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
src/tools/clippy/clippy_lints/src/trait_bounds.rs
src/tools/clippy/clippy_lints/src/transmute/utils.rs
src/tools/clippy/clippy_lints/src/types/mod.rs
src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
src/tools/clippy/clippy_lints/src/unit_types/mod.rs
src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
src/tools/clippy/clippy_lints/src/use_self.rs
src/tools/clippy/clippy_lints/src/utils/author.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/utils/dump_hir.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/utils/mod.rs
src/tools/clippy/clippy_utils/src/consts.rs
src/tools/clippy/clippy_utils/src/hir_utils.rs
src/tools/clippy/clippy_utils/src/macros.rs
src/tools/clippy/clippy_utils/src/msrvs.rs
src/tools/clippy/clippy_utils/src/numeric_literal.rs
src/tools/clippy/clippy_utils/src/paths.rs
src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
src/tools/clippy/clippy_utils/src/source.rs
src/tools/clippy/clippy_utils/src/sugg.rs
src/tools/clippy/clippy_utils/src/ty.rs
src/tools/clippy/clippy_utils/src/usage.rs
src/tools/clippy/clippy_utils/src/visitors.rs
src/tools/clippy/doc/adding_lints.md
src/tools/clippy/lintcheck/src/main.rs
src/tools/clippy/rust-toolchain
src/tools/clippy/src/driver.rs
src/tools/clippy/tests/dogfood.rs
src/tools/clippy/tests/lint_message_convention.rs
src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr
src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed
src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs
src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs
src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr
src/tools/clippy/tests/ui-toml/await_holding_invalid_type/clippy.toml
src/tools/clippy/tests/ui-toml/functions_maxlines/test.rs
src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr
src/tools/clippy/tests/ui-toml/large_include_file/clippy.toml
src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs
src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr
src/tools/clippy/tests/ui-toml/large_include_file/too_big.txt
src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.stderr
src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
src/tools/clippy/tests/ui/assertions_on_constants.rs
src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
src/tools/clippy/tests/ui/auxiliary/proc_macro_with_span.rs
src/tools/clippy/tests/ui/bytes_count_to_len.fixed
src/tools/clippy/tests/ui/bytes_count_to_len.rs
src/tools/clippy/tests/ui/bytes_count_to_len.stderr
src/tools/clippy/tests/ui/cast.rs
src/tools/clippy/tests/ui/cast.stderr
src/tools/clippy/tests/ui/cast_alignment.rs
src/tools/clippy/tests/ui/cast_slice_different_sizes.rs
src/tools/clippy/tests/ui/cast_slice_different_sizes.stderr
src/tools/clippy/tests/ui/collapsible_else_if.fixed
src/tools/clippy/tests/ui/collapsible_else_if.rs
src/tools/clippy/tests/ui/collapsible_else_if.stderr
src/tools/clippy/tests/ui/crashes/auxiliary/ice-8681-aux.rs
src/tools/clippy/tests/ui/crashes/ice-2865.rs
src/tools/clippy/tests/ui/crashes/ice-3151.rs
src/tools/clippy/tests/ui/crashes/ice-5944.rs
src/tools/clippy/tests/ui/crashes/ice-8250.stderr
src/tools/clippy/tests/ui/crashes/ice-8681.rs
src/tools/clippy/tests/ui/crashes/ice-96721.rs
src/tools/clippy/tests/ui/crashes/ice-96721.stderr
src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr
src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr
src/tools/clippy/tests/ui/deprecated.rs
src/tools/clippy/tests/ui/deprecated.stderr
src/tools/clippy/tests/ui/doc_unsafe.rs
src/tools/clippy/tests/ui/doc_unsafe.stderr
src/tools/clippy/tests/ui/drop_non_drop.stderr
src/tools/clippy/tests/ui/empty_drop.fixed
src/tools/clippy/tests/ui/empty_drop.rs
src/tools/clippy/tests/ui/empty_drop.stderr
src/tools/clippy/tests/ui/eta.fixed
src/tools/clippy/tests/ui/eta.rs
src/tools/clippy/tests/ui/eta.stderr
src/tools/clippy/tests/ui/extra_unused_lifetimes.rs
src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr
src/tools/clippy/tests/ui/format_push_string.rs
src/tools/clippy/tests/ui/format_push_string.stderr
src/tools/clippy/tests/ui/identity_op.rs
src/tools/clippy/tests/ui/identity_op.stderr
src/tools/clippy/tests/ui/impl.rs
src/tools/clippy/tests/ui/is_digit_ascii_radix.fixed
src/tools/clippy/tests/ui/is_digit_ascii_radix.rs
src/tools/clippy/tests/ui/is_digit_ascii_radix.stderr
src/tools/clippy/tests/ui/iter_overeager_cloned.fixed
src/tools/clippy/tests/ui/iter_overeager_cloned.rs
src/tools/clippy/tests/ui/iter_with_drain.fixed
src/tools/clippy/tests/ui/iter_with_drain.rs
src/tools/clippy/tests/ui/let_underscore_drop.rs
src/tools/clippy/tests/ui/let_underscore_drop.stderr
src/tools/clippy/tests/ui/let_unit.fixed
src/tools/clippy/tests/ui/let_unit.rs
src/tools/clippy/tests/ui/let_unit.stderr
src/tools/clippy/tests/ui/manual_bits.fixed
src/tools/clippy/tests/ui/manual_bits.rs
src/tools/clippy/tests/ui/manual_bits.stderr
src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs
src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr
src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs
src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr
src/tools/clippy/tests/ui/manual_split_once.fixed
src/tools/clippy/tests/ui/manual_split_once.rs
src/tools/clippy/tests/ui/manual_split_once.stderr
src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed
src/tools/clippy/tests/ui/mistyped_literal_suffix.rs
src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr
src/tools/clippy/tests/ui/mut_from_ref.rs
src/tools/clippy/tests/ui/mut_from_ref.stderr
src/tools/clippy/tests/ui/needless_for_each_fixable.fixed
src/tools/clippy/tests/ui/needless_for_each_fixable.rs
src/tools/clippy/tests/ui/needless_for_each_fixable.stderr
src/tools/clippy/tests/ui/needless_late_init.rs
src/tools/clippy/tests/ui/needless_late_init.stderr
src/tools/clippy/tests/ui/needless_late_init_fixable.fixed
src/tools/clippy/tests/ui/needless_late_init_fixable.rs
src/tools/clippy/tests/ui/needless_late_init_fixable.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_option_as_deref.fixed
src/tools/clippy/tests/ui/needless_option_as_deref.rs
src/tools/clippy/tests/ui/needless_option_take.fixed
src/tools/clippy/tests/ui/needless_option_take.rs
src/tools/clippy/tests/ui/needless_option_take.stderr
src/tools/clippy/tests/ui/needless_splitn.fixed
src/tools/clippy/tests/ui/needless_splitn.rs
src/tools/clippy/tests/ui/needless_splitn.stderr
src/tools/clippy/tests/ui/new_without_default.rs
src/tools/clippy/tests/ui/non_expressive_names.rs
src/tools/clippy/tests/ui/numbered_fields.fixed
src/tools/clippy/tests/ui/numbered_fields.rs
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/option_take_on_temporary.fixed
src/tools/clippy/tests/ui/or_then_unwrap.fixed
src/tools/clippy/tests/ui/or_then_unwrap.rs
src/tools/clippy/tests/ui/panicking_macros.rs
src/tools/clippy/tests/ui/pub_use.rs
src/tools/clippy/tests/ui/pub_use.stderr
src/tools/clippy/tests/ui/redundant_pub_crate.fixed
src/tools/clippy/tests/ui/redundant_pub_crate.rs
src/tools/clippy/tests/ui/rename.fixed
src/tools/clippy/tests/ui/rename.rs
src/tools/clippy/tests/ui/rename.stderr
src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs
src/tools/clippy/tests/ui/same_functions_in_if_condition.rs
src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr
src/tools/clippy/tests/ui/shadow.rs
src/tools/clippy/tests/ui/shadow.stderr
src/tools/clippy/tests/ui/similar_names.rs
src/tools/clippy/tests/ui/similar_names.stderr
src/tools/clippy/tests/ui/single_char_lifetime_names.rs
src/tools/clippy/tests/ui/single_char_lifetime_names.stderr
src/tools/clippy/tests/ui/single_match_else.rs
src/tools/clippy/tests/ui/single_match_else.stderr
src/tools/clippy/tests/ui/stable_sort_primitive.stderr
src/tools/clippy/tests/ui/suspicious_else_formatting.rs
src/tools/clippy/tests/ui/to_digit_is_some.fixed
src/tools/clippy/tests/ui/to_digit_is_some.rs
src/tools/clippy/tests/ui/to_digit_is_some.stderr
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/transmute_collection.rs
src/tools/clippy/tests/ui/trim_split_whitespace.fixed
src/tools/clippy/tests/ui/trim_split_whitespace.rs
src/tools/clippy/tests/ui/trim_split_whitespace.stderr
src/tools/clippy/tests/ui/type_repetition_in_bounds.rs
src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr
src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
src/tools/clippy/tests/ui/uninit.rs
src/tools/clippy/tests/ui/uninit.stderr
src/tools/clippy/tests/ui/unit_arg.rs
src/tools/clippy/tests/ui/unit_arg.stderr
src/tools/clippy/tests/ui/unit_hash.rs
src/tools/clippy/tests/ui/unit_hash.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/unnecessary_to_owned.stderr
src/tools/clippy/tests/ui/unnested_or_patterns.fixed
src/tools/clippy/tests/ui/unnested_or_patterns.rs
src/tools/clippy/tests/ui/unnested_or_patterns.stderr
src/tools/clippy/tests/ui/useless_attribute.fixed
src/tools/clippy/tests/ui/useless_attribute.rs
src/tools/clippy/tests/ui/useless_attribute.stderr
src/tools/clippy/tests/ui/wildcard_imports.fixed
src/tools/clippy/tests/ui/wildcard_imports.rs
src/tools/clippy/tests/ui/wildcard_imports.stderr
src/tools/clippy/tests/ui/wrong_self_convention.rs
src/tools/clippy/tests/ui/wrong_self_convention.stderr
src/tools/clippy/tests/ui/wrong_self_convention2.rs

index d610e8c7bc478ee1fc9b98ed6e0bef9debe1355a,0000000000000000000000000000000000000000..89884bfc85902ee4cf825d2d5130a6d5b9935d69
mode 100644,000000..100644
--- /dev/null
@@@ -1,44 -1,0 +1,44 @@@
-         Please provide a discription of the issue, along with any information
 +name: Blank Issue
 +description: Create a blank issue.
 +body:
 +  - type: markdown
 +    attributes:
 +      value: Thank you for filing an issue!
 +  - type: textarea
 +    id: problem
 +    attributes:
 +      label: Description
 +      description: >
++        Please provide a description of the issue, along with any information
 +        you feel relevant to replicate it.
 +    validations:
 +      required: true
 +  - type: textarea
 +    id: version
 +    attributes:
 +      label: Version
 +      description: "Rust version (`rustc -Vv`)"
 +      placeholder: |
 +        rustc 1.46.0-nightly (f455e46ea 2020-06-20)
 +        binary: rustc
 +        commit-hash: f455e46eae1a227d735091091144601b467e1565
 +        commit-date: 2020-06-20
 +        host: x86_64-unknown-linux-gnu
 +        release: 1.46.0-nightly
 +        LLVM version: 10.0
 +      render: text
 +  - type: textarea
 +    id: labels
 +    attributes:
 +      label: Additional Labels
 +      description: >
 +        Additional labels can be added to this issue by including the following
 +        command
 +      placeholder: |
 +        @rustbot label +<label>
 +
 +        Common labels for this issue type are:
 +        * C-an-interesting-project
 +        * C-enhancement
 +        * C-question
 +        * C-tracking-issue
index 9357ccc4f4e76f26d013ac115fae3fba71a175c6,0000000000000000000000000000000000000000..25e436d30b97dc42dacee379ad90f9d069c462d5
mode 100644,000000..100644
--- /dev/null
@@@ -1,50 -1,0 +1,50 @@@
-       description: Please provide the code and steps to repoduce the bug
 +name: Bug Report (False Negative)
 +description: Create a bug report about missing warnings from a lint
 +labels: ["C-bug", "I-false-negative"]
 +body:
 +  - type: markdown
 +    attributes:
 +      value: Thank you for filing a bug report! 🐛
 +  - type: textarea
 +    id: problem
 +    attributes:
 +      label: Summary
 +      description: >
 +        Please provide a short summary of the bug, along with any information
 +        you feel relevant to replicate the bug.
 +    validations:
 +      required: true
 +  - type: input
 +    id: lint-name
 +    attributes:
 +      label: Lint Name
 +      description: Please provide the lint name.
 +  - type: textarea
 +    id: reproducer
 +    attributes:
 +      label: Reproducer
++      description: Please provide the code and steps to reproduce the bug
 +      value: |
 +        I tried this code:
 +
 +        ```rust
 +        <code>
 +        ```
 +
 +        I expected to see this happen:
 +
 +        Instead, this happened:
 +  - type: textarea
 +    id: version
 +    attributes:
 +      label: Version
 +      description: "Rust version (`rustc -Vv`)"
 +      placeholder: |
 +        rustc 1.46.0-nightly (f455e46ea 2020-06-20)
 +        binary: rustc
 +        commit-hash: f455e46eae1a227d735091091144601b467e1565
 +        commit-date: 2020-06-20
 +        host: x86_64-unknown-linux-gnu
 +        release: 1.46.0-nightly
 +        LLVM version: 10.0
 +      render: text
index b7dd400ee7333efa6ea35295abb6e2624519128d,0000000000000000000000000000000000000000..561b65c93a7f9329799d2be796c2590b1ae215dd
mode 100644,000000..100644
--- /dev/null
@@@ -1,68 -1,0 +1,68 @@@
-         Please provide the code and steps to repoduce the bug together with the
 +name: Bug Report (False Positive)
 +description: Create a bug report about a wrongly emitted lint warning
 +labels: ["C-bug", "I-false-positive"]
 +body:
 +  - type: markdown
 +    attributes:
 +      value: Thank you for filing a bug report! 🐛
 +  - type: textarea
 +    id: problem
 +    attributes:
 +      label: Summary
 +      description: >
 +        Please provide a short summary of the bug, along with any information
 +        you feel relevant to replicate the bug.
 +    validations:
 +      required: true
 +  - type: input
 +    id: lint-name
 +    attributes:
 +      label: Lint Name
 +      description: Please provide the lint name.
 +  - type: textarea
 +    id: reproducer
 +    attributes:
 +      label: Reproducer
 +      description: >
++        Please provide the code and steps to reproduce the bug together with the
 +        output from Clippy.
 +      value: |
 +        I tried this code:
 +
 +        ```rust
 +        <code>
 +        ```
 +
 +        I saw this happen:
 +
 +        ```
 +        <output>
 +        ```
 +
 +        I expected to see this happen:
 +  - type: textarea
 +    id: version
 +    attributes:
 +      label: Version
 +      description: "Rust version (`rustc -Vv`)"
 +      placeholder: |
 +        rustc 1.46.0-nightly (f455e46ea 2020-06-20)
 +        binary: rustc
 +        commit-hash: f455e46eae1a227d735091091144601b467e1565
 +        commit-date: 2020-06-20
 +        host: x86_64-unknown-linux-gnu
 +        release: 1.46.0-nightly
 +        LLVM version: 10.0
 +      render: text
 +  - type: textarea
 +    id: labels
 +    attributes:
 +      label: Additional Labels
 +      description: >
 +        Additional labels can be added to this issue by including the following
 +        command
 +      placeholder: |
 +        @rustbot label +<label>
 +
 +        Common labels for this issue type are:
 +        * `I-suggestion-causes-error`
index cd83bc9642b60c73ee1529c492512b993f251fab,0000000000000000000000000000000000000000..0e27cc927acea4c010f810cd350f047eebece429
mode 100644,000000..100644
--- /dev/null
@@@ -1,76 -1,0 +1,76 @@@
-     # Don't run Clippy tests, when only textfiles were modified
 +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 textfiles were modified
++    # Don't run Clippy tests, when only text files were modified
 +    paths-ignore:
 +    - 'COPYRIGHT'
 +    - 'LICENSE-*'
 +    - '**.md'
 +    - '**.txt'
 +  pull_request:
-       uses: actions/checkout@v2.3.3
++    # 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
 +
 +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 f571485e6d389befdf909d2f9ab5fa7bf25db69a,0000000000000000000000000000000000000000..9b3fd3ddfeb38cc357c20e0c818b2fcad5cd9977
mode 100644,000000..100644
--- /dev/null
@@@ -1,262 -1,0 +1,262 @@@
-       uses: actions/checkout@v2.3.3
 +name: Clippy Test (bors)
 +
 +on:
 +  push:
 +    branches:
 +      - auto
 +      - try
 +
 +env:
 +  RUST_BACKTRACE: 1
 +  CARGO_TARGET_DIR: '${{ github.workspace }}/target'
 +  NO_FMT_TEST: 1
 +
 +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@v2.3.3
++      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@v2.3.3
++      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 }}
 +
 +  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@v2.3.3
++      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, 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, integration_build, integration]
 +
 +    steps:
 +      - name: Mark the job as a failure
 +        run: exit 1
index 5dfab1d2ebc402495477e11fd70daa13372d38ce,0000000000000000000000000000000000000000..22051093c9cf960992c3a80923e71f72ef4414f1
mode 100644,000000..100644
--- /dev/null
@@@ -1,70 -1,0 +1,70 @@@
-       uses: actions/checkout@v2.3.3
 +name: Clippy Dev Test
 +
 +on:
 +  push:
 +    branches:
 +      - auto
 +      - try
 +  pull_request:
 +    # Only run on paths, that get checked by the clippy_dev tool
 +    paths:
 +    - 'CHANGELOG.md'
 +    - 'README.md'
 +    - '**.stderr'
 +    - '**.rs'
 +
 +env:
 +  RUST_BACKTRACE: 1
 +
 +jobs:
 +  clippy_dev:
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +    # Setup
 +    - name: Checkout
++      uses: actions/checkout@v3.0.2
 +
 +    # Run
 +    - name: Build
 +      run: cargo build --features deny-warnings
 +      working-directory: clippy_dev
 +
 +    - name: Test update_lints
 +      run: cargo dev update_lints --check
 +
 +    - name: Test fmt
 +      run: cargo dev fmt --check
 +
 +    - name: Test cargo dev new lint
 +      run: |
 +        cargo dev new_lint --name new_early_pass --pass early
 +        cargo dev new_lint --name new_late_pass --pass late
 +        cargo check
 +        git reset --hard HEAD
 +
 +  # 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 dev test finished
 +    if: github.event.pusher.name == 'bors' && success()
 +    runs-on: ubuntu-latest
 +    needs: [clippy_dev]
 +
 +    steps:
 +      - name: Mark the job as successful
 +        run: exit 0
 +
 +  end-failure:
 +    name: bors dev test finished
 +    if: github.event.pusher.name == 'bors' && (failure() || cancelled())
 +    runs-on: ubuntu-latest
 +    needs: [clippy_dev]
 +
 +    steps:
 +      - name: Mark the job as a failure
 +        run: exit 1
index b8be730be32b00644ec1ed70658a832bf2284397,0000000000000000000000000000000000000000..71d71d10359e7e76b4f2cbc1b8843247d266659c
mode 100644,000000..100644
--- /dev/null
@@@ -1,64 -1,0 +1,64 @@@
-       uses: actions/checkout@v2.3.3
 +name: Deploy
 +
 +on:
 +  push:
 +    branches:
 +      - master
 +      - beta
 +    tags:
 +      - rust-1.**
 +
 +env:
 +  TARGET_BRANCH: 'gh-pages'
 +  SHA: '${{ github.sha }}'
 +  SSH_REPO: 'git@github.com:${{ github.repository }}.git'
 +
 +jobs:
 +  deploy:
 +    runs-on: ubuntu-latest
 +    if: github.repository == 'rust-lang/rust-clippy'
 +
 +    steps:
 +    # Setup
 +    - name: Checkout
-       uses: actions/checkout@v2.3.3
++      uses: actions/checkout@v3.0.2
 +
 +    - name: Checkout
++      uses: actions/checkout@v3.0.2
 +      with:
 +        ref: ${{ env.TARGET_BRANCH }}
 +        path: 'out'
 +
 +    # Run
 +    - name: Set tag name
 +      if: startswith(github.ref, 'refs/tags/')
 +      run: |
 +        TAG=$(basename ${{ github.ref }})
 +        echo "TAG_NAME=$TAG" >> $GITHUB_ENV
 +    - name: Set beta to true
 +      if: github.ref == 'refs/heads/beta'
 +      run: echo "BETA=true" >> $GITHUB_ENV
 +
 +    # We need to check out all files that (transitively) depend on the
 +    # structure of the gh-pages branch, so that we're able to change that
 +    # structure without breaking the deployment.
 +    - name: Use deploy files from master branch
 +      run: |
 +        git fetch --no-tags --prune --depth=1 origin master
 +        git checkout origin/master -- .github/deploy.sh util/versions.py util/gh-pages/versions.html
 +
 +    # Generate lockfile for caching to avoid build problems with cached deps
 +    - name: cargo generate-lockfile
 +      run: cargo generate-lockfile
 +
 +    - name: Cache
 +      uses: Swatinem/rust-cache@v1.3.0
 +
 +    - name: cargo collect-metadata
 +      run: cargo collect-metadata
 +
 +    - name: Deploy
 +      run: |
 +        eval "$(ssh-agent -s)"
 +        ssh-add - <<< "${{ secrets.DEPLOY_KEY }}"
 +        bash .github/deploy.sh
index 56c00544c93a7892d7aa80a0ec05e6c70ad0734d,0000000000000000000000000000000000000000..a179bfa72617213735d50473ae66a73dc2355ecc
mode 100644,000000..100644
--- /dev/null
@@@ -1,57 -1,0 +1,57 @@@
-       uses: actions/checkout@v2.3.3
 +name: Remark
 +
 +on:
 +  push:
 +    branches:
 +      - auto
 +      - try
 +  pull_request:
 +    paths:
 +    - '**.md'
 +
 +jobs:
 +  remark:
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +    # Setup
 +    - name: Checkout
++      uses: actions/checkout@v3.0.2
 +
 +    - name: Setup Node.js
 +      uses: actions/setup-node@v1.4.4
 +      with:
 +        node-version: '12.x'
 +
 +    - name: Install remark
 +      run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended remark-gfm
 +
 +    # Run
 +    - name: Check *.md files
 +      run: git ls-files -z '*.md' | xargs -0 -n 1 -I {} ./node_modules/.bin/remark {} -u lint -f > /dev/null
 +
 +  # 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 remark test finished
 +    if: github.event.pusher.name == 'bors' && success()
 +    runs-on: ubuntu-latest
 +    needs: [remark]
 +
 +    steps:
 +      - name: Mark the job as successful
 +        run: exit 0
 +
 +  end-failure:
 +    name: bors remark test finished
 +    if: github.event.pusher.name == 'bors' && (failure() || cancelled())
 +    runs-on: ubuntu-latest
 +    needs: [remark]
 +
 +    steps:
 +      - name: Mark the job as a failure
 +        run: exit 1
index b4097ea86a5185ce492067bad0ffb086733ad4c1,0000000000000000000000000000000000000000..751f9fccd88d4389866d835eb08ebd0a3d3547a7
mode 100644,000000..100644
--- /dev/null
@@@ -1,3714 -1,0 +1,3825 @@@
- [57b3c4b...master](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...master)
 +# Changelog
 +
 +All notable changes to this project will be documented in this file.
 +See [Changelog Update](doc/changelog_update.md) if you want to update this
 +document.
 +
 +## Unreleased / In Rust Nightly
 +
++[d0cf3481...master](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...master)
++
++## Rust 1.61 (beta)
++
++Current beta, 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
 +
 +Current stable, released 2022-04-07
 +
 +[0eff589...57b3c4b](https://github.com/rust-lang/rust-clippy/compare/0eff589...57b3c4b)
 +
 +### New Lints
 +
 +* [`single_char_lifetime_names`]
 +  [#8236](https://github.com/rust-lang/rust-clippy/pull/8236)
 +* [`iter_overeager_cloned`]
 +  [#8203](https://github.com/rust-lang/rust-clippy/pull/8203)
 +* [`transmute_undefined_repr`]
 +  [#8398](https://github.com/rust-lang/rust-clippy/pull/8398)
 +* [`default_union_representation`]
 +  [#8289](https://github.com/rust-lang/rust-clippy/pull/8289)
 +* [`manual_bits`]
 +  [#8213](https://github.com/rust-lang/rust-clippy/pull/8213)
 +* [`borrow_as_ptr`]
 +  [#8210](https://github.com/rust-lang/rust-clippy/pull/8210)
 +
 +### Moves and Deprecations
 +
 +* Moved [`disallowed_methods`] and [`disallowed_types`] to `style` (now warn-by-default)
 +  [#8261](https://github.com/rust-lang/rust-clippy/pull/8261)
 +* Rename `ref_in_deref` to [`needless_borrow`]
 +  [#8217](https://github.com/rust-lang/rust-clippy/pull/8217)
 +* Moved [`mutex_atomic`] to `nursery` (now allow-by-default)
 +  [#8260](https://github.com/rust-lang/rust-clippy/pull/8260)
 +
 +### Enhancements
 +
 +* [`ptr_arg`]: Now takes the argument usage into account and lints for mutable references
 +  [#8271](https://github.com/rust-lang/rust-clippy/pull/8271)
 +* [`unused_io_amount`]: Now supports async read and write traits
 +  [#8179](https://github.com/rust-lang/rust-clippy/pull/8179)
 +* [`while_let_on_iterator`]: Improved detection to catch more cases
 +  [#8221](https://github.com/rust-lang/rust-clippy/pull/8221)
 +* [`trait_duplication_in_bounds`]: Now covers trait functions with `Self` bounds
 +  [#8252](https://github.com/rust-lang/rust-clippy/pull/8252)
 +* [`unwrap_used`]: Now works for `.get(i).unwrap()` and `.get_mut(i).unwrap()`
 +  [#8372](https://github.com/rust-lang/rust-clippy/pull/8372)
 +* [`map_clone`]: The suggestion takes `msrv` into account
 +  [#8280](https://github.com/rust-lang/rust-clippy/pull/8280)
 +* [`manual_bits`] and [`borrow_as_ptr`]: Now track the `clippy::msrv` attribute
 +  [#8280](https://github.com/rust-lang/rust-clippy/pull/8280)
 +* [`disallowed_methods`]: Now works for methods on primitive types
 +  [#8112](https://github.com/rust-lang/rust-clippy/pull/8112)
 +* [`not_unsafe_ptr_arg_deref`]: Now works for type aliases
 +  [#8273](https://github.com/rust-lang/rust-clippy/pull/8273)
 +* [`needless_question_mark`]: Now works for async functions
 +  [#8311](https://github.com/rust-lang/rust-clippy/pull/8311)
 +* [`iter_not_returning_iterator`]: Now handles type projections
 +  [#8228](https://github.com/rust-lang/rust-clippy/pull/8228)
 +* [`wrong_self_convention`]: Now detects wrong `self` references in more cases
 +  [#8208](https://github.com/rust-lang/rust-clippy/pull/8208)
 +* [`single_match`]: Now works for `match` statements with tuples
 +  [#8322](https://github.com/rust-lang/rust-clippy/pull/8322)
 +
 +### False Positive Fixes
 +
 +* [`erasing_op`]: No longer triggers if the output type changes
 +  [#8204](https://github.com/rust-lang/rust-clippy/pull/8204)
 +* [`if_same_then_else`]: No longer triggers for `if let` statements
 +  [#8297](https://github.com/rust-lang/rust-clippy/pull/8297)
 +* [`manual_memcpy`]: No longer lints on `VecDeque`
 +  [#8226](https://github.com/rust-lang/rust-clippy/pull/8226)
 +* [`trait_duplication_in_bounds`]: Now takes path segments into account
 +  [#8315](https://github.com/rust-lang/rust-clippy/pull/8315)
 +* [`deref_addrof`]: No longer lints when the dereference or borrow occurs in different a context
 +  [#8268](https://github.com/rust-lang/rust-clippy/pull/8268)
 +* [`type_repetition_in_bounds`]: Now checks for full equality to prevent false positives
 +  [#8224](https://github.com/rust-lang/rust-clippy/pull/8224)
 +* [`ptr_arg`]: No longer lint for mutable references in traits
 +  [#8369](https://github.com/rust-lang/rust-clippy/pull/8369)
 +* [`implicit_clone`]: No longer lints for double references
 +  [#8231](https://github.com/rust-lang/rust-clippy/pull/8231)
 +* [`needless_lifetimes`]: No longer lints lifetimes for explicit `self` types
 +  [#8278](https://github.com/rust-lang/rust-clippy/pull/8278)
 +* [`op_ref`]: No longer lints in `BinOp` impl if that can cause recursion
 +  [#8298](https://github.com/rust-lang/rust-clippy/pull/8298)
 +* [`enum_variant_names`]: No longer triggers for empty variant names
 +  [#8329](https://github.com/rust-lang/rust-clippy/pull/8329)
 +* [`redundant_closure`]: No longer lints for `Arc<T>` or `Rc<T>`
 +  [#8193](https://github.com/rust-lang/rust-clippy/pull/8193)
 +* [`iter_not_returning_iterator`]: No longer lints on trait implementations but therefore on trait definitions
 +  [#8228](https://github.com/rust-lang/rust-clippy/pull/8228)
 +* [`single_match`]: No longer lints on exhaustive enum patterns without a wildcard
 +  [#8322](https://github.com/rust-lang/rust-clippy/pull/8322)
 +* [`manual_swap`]: No longer lints on cases that involve automatic dereferences
 +  [#8220](https://github.com/rust-lang/rust-clippy/pull/8220)
 +* [`useless_format`]: Now works for implicit named arguments
 +  [#8295](https://github.com/rust-lang/rust-clippy/pull/8295)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`needless_borrow`]: Prevent mutable borrows being moved and suggest removing the borrow on method calls
 +  [#8217](https://github.com/rust-lang/rust-clippy/pull/8217)
 +* [`chars_next_cmp`]: Correctly excapes the suggestion
 +  [#8376](https://github.com/rust-lang/rust-clippy/pull/8376)
 +* [`explicit_write`]: Add suggestions for `write!`s with format arguments
 +  [#8365](https://github.com/rust-lang/rust-clippy/pull/8365)
 +* [`manual_memcpy`]: Suggests `copy_from_slice` when applicable
 +  [#8226](https://github.com/rust-lang/rust-clippy/pull/8226)
 +* [`or_fun_call`]: Improved suggestion display for long arguments
 +  [#8292](https://github.com/rust-lang/rust-clippy/pull/8292)
 +* [`unnecessary_cast`]: Now correctly includes the sign
 +  [#8350](https://github.com/rust-lang/rust-clippy/pull/8350)
 +* [`cmp_owned`]: No longer flips the comparison order
 +  [#8299](https://github.com/rust-lang/rust-clippy/pull/8299)
 +* [`explicit_counter_loop`]: Now correctly suggests `iter()` on references
 +  [#8382](https://github.com/rust-lang/rust-clippy/pull/8382)
 +
 +### ICE Fixes
 +
 +* [`manual_split_once`]
 +  [#8250](https://github.com/rust-lang/rust-clippy/pull/8250)
 +
 +### Documentation Improvements
 +
 +* [`map_flatten`]: Add documentation for the `Option` type
 +  [#8354](https://github.com/rust-lang/rust-clippy/pull/8354)
 +* Document that Clippy's driver might use a different code generation than rustc
 +  [#8037](https://github.com/rust-lang/rust-clippy/pull/8037)
 +* Clippy's lint list will now automatically focus the search box
 +  [#8343](https://github.com/rust-lang/rust-clippy/pull/8343)
 +
 +### Others
 +
 +* Clippy now warns if we find multiple Clippy config files exist
 +  [#8326](https://github.com/rust-lang/rust-clippy/pull/8326)
 +
 +## Rust 1.59
 +
 +Released 2022-02-24
 +
 +[e181011...0eff589](https://github.com/rust-lang/rust-clippy/compare/e181011...0eff589)
 +
 +### New Lints
 +
 +* [`index_refutable_slice`]
 +  [#7643](https://github.com/rust-lang/rust-clippy/pull/7643)
 +* [`needless_splitn`]
 +  [#7896](https://github.com/rust-lang/rust-clippy/pull/7896)
 +* [`unnecessary_to_owned`]
 +  [#7978](https://github.com/rust-lang/rust-clippy/pull/7978)
 +* [`needless_late_init`]
 +  [#7995](https://github.com/rust-lang/rust-clippy/pull/7995)
 +* [`octal_escapes`] [#8007](https://github.com/rust-lang/rust-clippy/pull/8007)
 +* [`return_self_not_must_use`]
 +  [#8071](https://github.com/rust-lang/rust-clippy/pull/8071)
 +* [`init_numbered_fields`]
 +  [#8170](https://github.com/rust-lang/rust-clippy/pull/8170)
 +
 +### Moves and Deprecations
 +
 +* Move `if_then_panic` to `pedantic` and rename to [`manual_assert`] (now
 +  allow-by-default) [#7810](https://github.com/rust-lang/rust-clippy/pull/7810)
 +* Rename `disallow_type` to [`disallowed_types`] and `disallowed_method` to
 +  [`disallowed_methods`]
 +  [#7984](https://github.com/rust-lang/rust-clippy/pull/7984)
 +* Move [`map_flatten`] to `complexity` (now warn-by-default)
 +  [#8054](https://github.com/rust-lang/rust-clippy/pull/8054)
 +
 +### Enhancements
 +
 +* [`match_overlapping_arm`]: Fix false negative where after included ranges,
 +  overlapping ranges weren't linted anymore
 +  [#7909](https://github.com/rust-lang/rust-clippy/pull/7909)
 +* [`deprecated_cfg_attr`]: Now takes the specified MSRV into account
 +  [#7944](https://github.com/rust-lang/rust-clippy/pull/7944)
 +* [`cast_lossless`]: Now also lints for `bool` to integer casts
 +  [#7948](https://github.com/rust-lang/rust-clippy/pull/7948)
 +* [`let_underscore_lock`]: Also emit lints for the `parking_lot` crate
 +  [#7957](https://github.com/rust-lang/rust-clippy/pull/7957)
 +* [`needless_borrow`]
 +  [#7977](https://github.com/rust-lang/rust-clippy/pull/7977)
 +    * Lint when a borrow is auto-dereffed more than once
 +    * Lint in the trailing expression of a block for a match arm
 +* [`strlen_on_c_strings`]
 +  [8001](https://github.com/rust-lang/rust-clippy/pull/8001)
 +    * Lint when used without a fully-qualified path
 +    * Suggest removing the surrounding unsafe block when possible
 +* [`non_ascii_literal`]: Now also lints on `char`s, not just `string`s
 +  [#8034](https://github.com/rust-lang/rust-clippy/pull/8034)
 +* [`single_char_pattern`]: Now also lints on `split_inclusive`, `split_once`,
 +  `rsplit_once`, `replace`, and `replacen`
 +  [#8077](https://github.com/rust-lang/rust-clippy/pull/8077)
 +* [`unwrap_or_else_default`]: Now also lints on `std` constructors like
 +  `Vec::new`, `HashSet::new`, and `HashMap::new`
 +  [#8163](https://github.com/rust-lang/rust-clippy/pull/8163)
 +* [`shadow_reuse`]: Now also lints on shadowed `if let` bindings, instead of
 +  [`shadow_unrelated`]
 +  [#8165](https://github.com/rust-lang/rust-clippy/pull/8165)
 +
 +### False Positive Fixes
 +
 +* [`or_fun_call`], [`unnecessary_lazy_evaluations`]: Improve heuristics, so that
 +  cheap functions (e.g. calling `.len()` on a `Vec`) won't get linted anymore
 +  [#7639](https://github.com/rust-lang/rust-clippy/pull/7639)
 +* [`manual_split_once`]: No longer suggests code changing the original behavior
 +  [#7896](https://github.com/rust-lang/rust-clippy/pull/7896)
 +* Don't show [`no_effect`] or [`unnecessary_operation`] warning for unit struct
 +  implementing `FnOnce`
 +  [#7898](https://github.com/rust-lang/rust-clippy/pull/7898)
 +* [`semicolon_if_nothing_returned`]: Fixed a bug, where the lint wrongly
 +  triggered on `let-else` statements
 +  [#7955](https://github.com/rust-lang/rust-clippy/pull/7955)
 +* [`if_then_some_else_none`]: No longer lints if there is an early return
 +  [#7980](https://github.com/rust-lang/rust-clippy/pull/7980)
 +* [`needless_collect`]: No longer suggests removal of `collect` when removal
 +  would create code requiring mutably borrowing a value multiple times
 +  [#7982](https://github.com/rust-lang/rust-clippy/pull/7982)
 +* [`shadow_same`]: Fix false positive for `async` function's params
 +  [#7997](https://github.com/rust-lang/rust-clippy/pull/7997)
 +* [`suboptimal_flops`]: No longer triggers in constant functions
 +  [#8009](https://github.com/rust-lang/rust-clippy/pull/8009)
 +* [`type_complexity`]: No longer lints on associated types in traits
 +  [#8030](https://github.com/rust-lang/rust-clippy/pull/8030)
 +* [`question_mark`]: No longer lints if returned object is not local
 +  [#8080](https://github.com/rust-lang/rust-clippy/pull/8080)
 +* [`option_if_let_else`]: No longer lint on complex sub-patterns
 +  [#8086](https://github.com/rust-lang/rust-clippy/pull/8086)
 +* [`blocks_in_if_conditions`]: No longer lints on empty closures
 +  [#8100](https://github.com/rust-lang/rust-clippy/pull/8100)
 +* [`enum_variant_names`]: No longer lint when first prefix is only a substring
 +  of a camel-case word
 +  [#8127](https://github.com/rust-lang/rust-clippy/pull/8127)
 +* [`identity_op`]: Only lint on integral operands
 +  [#8183](https://github.com/rust-lang/rust-clippy/pull/8183)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`search_is_some`]: Fix suggestion for `any()` not taking item by reference
 +  [#7463](https://github.com/rust-lang/rust-clippy/pull/7463)
 +* [`almost_swapped`]: Now detects if there is a `no_std` or `no_core` attribute
 +  and adapts the suggestion accordingly
 +  [#7877](https://github.com/rust-lang/rust-clippy/pull/7877)
 +* [`redundant_pattern_matching`]: Fix suggestion for deref expressions
 +  [#7949](https://github.com/rust-lang/rust-clippy/pull/7949)
 +* [`explicit_counter_loop`]: Now also produces a suggestion for non-`usize`
 +  types [#7950](https://github.com/rust-lang/rust-clippy/pull/7950)
 +* [`manual_map`]: Fix suggestion when used with unsafe functions and blocks
 +  [#7968](https://github.com/rust-lang/rust-clippy/pull/7968)
 +* [`option_map_or_none`]: Suggest `map` over `and_then` when possible
 +  [#7971](https://github.com/rust-lang/rust-clippy/pull/7971)
 +* [`option_if_let_else`]: No longer expands macros in the suggestion
 +  [#7974](https://github.com/rust-lang/rust-clippy/pull/7974)
 +* [`iter_cloned_collect`]: Suggest `copied` over `cloned` when possible
 +  [#8006](https://github.com/rust-lang/rust-clippy/pull/8006)
 +* [`doc_markdown`]: No longer uses inline hints to improve readability of
 +  suggestion [#8011](https://github.com/rust-lang/rust-clippy/pull/8011)
 +* [`needless_question_mark`]: Now better explains the suggestion
 +  [#8028](https://github.com/rust-lang/rust-clippy/pull/8028)
 +* [`single_char_pattern`]: Escape backslash `\` in suggestion
 +  [#8067](https://github.com/rust-lang/rust-clippy/pull/8067)
 +* [`needless_bool`]: Suggest `a != b` over `!(a == b)`
 +  [#8117](https://github.com/rust-lang/rust-clippy/pull/8117)
 +* [`iter_skip_next`]: Suggest to add a `mut` if it is necessary in order to
 +  apply this lints suggestion
 +  [#8133](https://github.com/rust-lang/rust-clippy/pull/8133)
 +* [`neg_multiply`]: Now produces a suggestion
 +  [#8144](https://github.com/rust-lang/rust-clippy/pull/8144)
 +* [`needless_return`]: Now suggests the unit type `()` over an empty block `{}`
 +  in match arms [#8185](https://github.com/rust-lang/rust-clippy/pull/8185)
 +* [`suboptimal_flops`]: Now gives a syntactically correct suggestion for
 +  `to_radians` and `to_degrees`
 +  [#8187](https://github.com/rust-lang/rust-clippy/pull/8187)
 +
 +### ICE Fixes
 +
 +* [`undocumented_unsafe_blocks`]
 +  [#7945](https://github.com/rust-lang/rust-clippy/pull/7945)
 +  [#7988](https://github.com/rust-lang/rust-clippy/pull/7988)
 +* [`unnecessary_cast`]
 +  [#8167](https://github.com/rust-lang/rust-clippy/pull/8167)
 +
 +### Documentation Improvements
 +
 +* [`print_stdout`], [`print_stderr`], [`dbg_macro`]: Document how the lint level
 +  can be changed crate-wide
 +  [#8040](https://github.com/rust-lang/rust-clippy/pull/8040)
 +* Added a note to the `README` that config changes don't apply to already
 +  compiled code [#8175](https://github.com/rust-lang/rust-clippy/pull/8175)
 +
 +### Others
 +
 +* [Clippy's lint
 +  list](https://rust-lang.github.io/rust-clippy/master/index.html) now displays
 +  the version a lint was added. :tada:
 +  [#7813](https://github.com/rust-lang/rust-clippy/pull/7813)
 +* New and improved issue templates
 +  [#8032](https://github.com/rust-lang/rust-clippy/pull/8032)
 +* _Dev:_ Add `cargo dev lint` command, to run your modified Clippy version on a
 +  file [#7917](https://github.com/rust-lang/rust-clippy/pull/7917)
 +
 +## Rust 1.58
 +
 +Released 2022-01-13
 +
 +[00e31fa...e181011](https://github.com/rust-lang/rust-clippy/compare/00e31fa...e181011)
 +
 +### Rust 1.58.1
 +
 +* Move [`non_send_fields_in_send_ty`] to `nursery` (now allow-by-default)
 +  [#8075](https://github.com/rust-lang/rust-clippy/pull/8075)
 +* [`useless_format`]: Handle implicit named arguments
 +  [#8295](https://github.com/rust-lang/rust-clippy/pull/8295)
 +
 +### New lints
 +
 +* [`transmute_num_to_bytes`]
 +  [#7805](https://github.com/rust-lang/rust-clippy/pull/7805)
 +* [`match_str_case_mismatch`]
 +  [#7806](https://github.com/rust-lang/rust-clippy/pull/7806)
 +* [`format_in_format_args`], [`to_string_in_format_args`]
 +  [#7743](https://github.com/rust-lang/rust-clippy/pull/7743)
 +* [`uninit_vec`]
 +  [#7682](https://github.com/rust-lang/rust-clippy/pull/7682)
 +* [`fn_to_numeric_cast_any`]
 +  [#7705](https://github.com/rust-lang/rust-clippy/pull/7705)
 +* [`undocumented_unsafe_blocks`]
 +  [#7748](https://github.com/rust-lang/rust-clippy/pull/7748)
 +* [`trailing_empty_array`]
 +  [#7838](https://github.com/rust-lang/rust-clippy/pull/7838)
 +* [`string_slice`]
 +  [#7878](https://github.com/rust-lang/rust-clippy/pull/7878)
 +
 +### Moves or deprecations of lints
 +
 +* Move [`non_send_fields_in_send_ty`] to `suspicious`
 +  [#7874](https://github.com/rust-lang/rust-clippy/pull/7874)
 +* Move [`non_ascii_literal`] to `restriction`
 +  [#7907](https://github.com/rust-lang/rust-clippy/pull/7907)
 +
 +### Changes that expand what code existing lints cover
 +
 +* [`question_mark`] now covers `Result`
 +  [#7840](https://github.com/rust-lang/rust-clippy/pull/7840)
 +* Make [`useless_format`] recognize bare `format!("")`
 +  [#7801](https://github.com/rust-lang/rust-clippy/pull/7801)
 +* Lint on underscored variables with no side effects in [`no_effect`]
 +  [#7775](https://github.com/rust-lang/rust-clippy/pull/7775)
 +* Expand [`match_ref_pats`] to check for multiple reference patterns
 +  [#7800](https://github.com/rust-lang/rust-clippy/pull/7800)
 +
 +### False positive fixes
 +
 +* Fix false positive of [`implicit_saturating_sub`] with `else` clause
 +  [#7832](https://github.com/rust-lang/rust-clippy/pull/7832)
 +* Fix [`question_mark`] when there is call in conditional predicate
 +  [#7860](https://github.com/rust-lang/rust-clippy/pull/7860)
 +* [`mut_mut`] no longer lints when type is defined in external macros
 +  [#7795](https://github.com/rust-lang/rust-clippy/pull/7795)
 +* Avoid [`eq_op`] in test functions
 +  [#7811](https://github.com/rust-lang/rust-clippy/pull/7811)
 +* [`cast_possible_truncation`] no longer lints when cast is coming from `signum`
 +  method call [#7850](https://github.com/rust-lang/rust-clippy/pull/7850)
 +* [`match_str_case_mismatch`] no longer lints on uncased characters
 +  [#7865](https://github.com/rust-lang/rust-clippy/pull/7865)
 +* [`ptr_arg`] no longer lints references to type aliases
 +  [#7890](https://github.com/rust-lang/rust-clippy/pull/7890)
 +* [`missing_safety_doc`] now also accepts "implementation safety" headers
 +  [#7856](https://github.com/rust-lang/rust-clippy/pull/7856)
 +* [`missing_safety_doc`] no longer lints if any parent has `#[doc(hidden)]`
 +  attribute [#7849](https://github.com/rust-lang/rust-clippy/pull/7849)
 +* [`if_not_else`] now ignores else-if statements
 +  [#7895](https://github.com/rust-lang/rust-clippy/pull/7895)
 +* Avoid linting [`cast_possible_truncation`] on bit-reducing operations
 +  [#7819](https://github.com/rust-lang/rust-clippy/pull/7819)
 +* Avoid linting [`field_reassign_with_default`] when `Drop` and `Copy` are
 +  involved [#7794](https://github.com/rust-lang/rust-clippy/pull/7794)
 +* [`unnecessary_sort_by`] now checks if argument implements `Ord` trait
 +  [#7824](https://github.com/rust-lang/rust-clippy/pull/7824)
 +* Fix false positive in [`match_overlapping_arm`]
 +  [#7847](https://github.com/rust-lang/rust-clippy/pull/7847)
 +* Prevent [`needless_lifetimes`] false positive in `async` function definition
 +  [#7901](https://github.com/rust-lang/rust-clippy/pull/7901)
 +
 +### Suggestion fixes/improvements
 +
 +* Keep an initial `::` when [`doc_markdown`] suggests to use ticks
 +  [#7916](https://github.com/rust-lang/rust-clippy/pull/7916)
 +* Add a machine applicable suggestion for the [`doc_markdown`] missing backticks
 +  lint [#7904](https://github.com/rust-lang/rust-clippy/pull/7904)
 +* [`equatable_if_let`] no longer expands macros in the suggestion
 +  [#7788](https://github.com/rust-lang/rust-clippy/pull/7788)
 +* Make [`shadow_reuse`] suggestion less verbose
 +  [#7782](https://github.com/rust-lang/rust-clippy/pull/7782)
 +
 +### ICE fixes
 +
 +* Fix ICE in [`enum_variant_names`]
 +  [#7873](https://github.com/rust-lang/rust-clippy/pull/7873)
 +* Fix ICE in [`undocumented_unsafe_blocks`]
 +  [#7891](https://github.com/rust-lang/rust-clippy/pull/7891)
 +
 +### Documentation improvements
 +
 +* Fixed naive doc formatting for `#[must_use]` lints ([`must_use_unit`],
 +  [`double_must_use`], [`must_use_candidate`], [`let_underscore_must_use`])
 +  [#7827](https://github.com/rust-lang/rust-clippy/pull/7827)
 +* Fix typo in example for [`match_result_ok`]
 +  [#7815](https://github.com/rust-lang/rust-clippy/pull/7815)
 +
 +### Others
 +
 +* Allow giving reasons for [`disallowed_types`]
 +  [#7791](https://github.com/rust-lang/rust-clippy/pull/7791)
 +* Fix [`manual_assert`] and [`match_wild_err_arm`] for `#![no_std]` and Rust
 +  2021. [#7851](https://github.com/rust-lang/rust-clippy/pull/7851)
 +* Fix regression in [`semicolon_if_nothing_returned`] on macros containing while
 +  loops [#7789](https://github.com/rust-lang/rust-clippy/pull/7789)
 +* Added a new configuration `literal-suffix-style` to enforce a certain style
 +  writing [`unseparated_literal_suffix`]
 +  [#7726](https://github.com/rust-lang/rust-clippy/pull/7726)
 +
 +## Rust 1.57
 +
 +Released 2021-12-02
 +
 +[7bfc26e...00e31fa](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...00e31fa)
 +
 +### New Lints
 +
 +* [`negative_feature_names`]
 +  [#7539](https://github.com/rust-lang/rust-clippy/pull/7539)
 +* [`redundant_feature_names`]
 +  [#7539](https://github.com/rust-lang/rust-clippy/pull/7539)
 +* [`mod_module_files`]
 +  [#7543](https://github.com/rust-lang/rust-clippy/pull/7543)
 +* [`self_named_module_files`]
 +  [#7543](https://github.com/rust-lang/rust-clippy/pull/7543)
 +* [`manual_split_once`]
 +  [#7565](https://github.com/rust-lang/rust-clippy/pull/7565)
 +* [`derivable_impls`]
 +  [#7570](https://github.com/rust-lang/rust-clippy/pull/7570)
 +* [`needless_option_as_deref`]
 +  [#7596](https://github.com/rust-lang/rust-clippy/pull/7596)
 +* [`iter_not_returning_iterator`]
 +  [#7610](https://github.com/rust-lang/rust-clippy/pull/7610)
 +* [`same_name_method`]
 +  [#7653](https://github.com/rust-lang/rust-clippy/pull/7653)
 +* [`manual_assert`] [#7669](https://github.com/rust-lang/rust-clippy/pull/7669)
 +* [`non_send_fields_in_send_ty`]
 +  [#7709](https://github.com/rust-lang/rust-clippy/pull/7709)
 +* [`equatable_if_let`]
 +  [#7762](https://github.com/rust-lang/rust-clippy/pull/7762)
 +
 +### Moves and Deprecations
 +
 +* Move [`shadow_unrelated`] to `restriction`
 +  [#7338](https://github.com/rust-lang/rust-clippy/pull/7338)
 +* Move [`option_if_let_else`] to `nursery`
 +  [#7568](https://github.com/rust-lang/rust-clippy/pull/7568)
 +* Move [`branches_sharing_code`] to `nursery`
 +  [#7595](https://github.com/rust-lang/rust-clippy/pull/7595)
 +* Rename `if_let_some_result` to [`match_result_ok`] which now also handles
 +  `while let` cases [#7608](https://github.com/rust-lang/rust-clippy/pull/7608)
 +* Move [`many_single_char_names`] to `pedantic`
 +  [#7671](https://github.com/rust-lang/rust-clippy/pull/7671)
 +* Move [`float_cmp`] to `pedantic`
 +  [#7692](https://github.com/rust-lang/rust-clippy/pull/7692)
 +* Rename `box_vec` to [`box_collection`] and lint on more general cases
 +  [#7693](https://github.com/rust-lang/rust-clippy/pull/7693)
 +* Uplift `invalid_atomic_ordering` to rustc
 +  [rust-lang/rust#84039](https://github.com/rust-lang/rust/pull/84039)
 +
 +### Enhancements
 +
 +* Rewrite the `shadow*` lints, so that they find a lot more shadows and are not
 +  limited to certain patterns
 +  [#7338](https://github.com/rust-lang/rust-clippy/pull/7338)
 +* The `avoid-breaking-exported-api` configuration now also works for
 +  [`box_collection`], [`redundant_allocation`], [`rc_buffer`], [`vec_box`],
 +  [`option_option`], [`linkedlist`], [`rc_mutex`]
 +  [#7560](https://github.com/rust-lang/rust-clippy/pull/7560)
 +* [`unnecessary_unwrap`]: Now also checks for `expect`s
 +  [#7584](https://github.com/rust-lang/rust-clippy/pull/7584)
 +* [`disallowed_methods`]: Allow adding a reason that will be displayed with the
 +  lint message
 +  [#7621](https://github.com/rust-lang/rust-clippy/pull/7621)
 +* [`approx_constant`]: Now checks the MSRV for `LOG10_2` and `LOG2_10`
 +  [#7629](https://github.com/rust-lang/rust-clippy/pull/7629)
 +* [`approx_constant`]: Add `TAU`
 +  [#7642](https://github.com/rust-lang/rust-clippy/pull/7642)
 +* [`needless_borrow`]: Now also lints on needless mutable borrows
 +  [#7657](https://github.com/rust-lang/rust-clippy/pull/7657)
 +* [`missing_safety_doc`]: Now also lints on unsafe traits
 +  [#7734](https://github.com/rust-lang/rust-clippy/pull/7734)
 +
 +### False Positive Fixes
 +
 +* [`manual_map`]: No longer lints when the option is borrowed in the match and
 +  also consumed in the arm
 +  [#7531](https://github.com/rust-lang/rust-clippy/pull/7531)
 +* [`filter_next`]: No longer lints if `filter` method is not the
 +  `Iterator::filter` method
 +  [#7562](https://github.com/rust-lang/rust-clippy/pull/7562)
 +* [`manual_flatten`]: No longer lints if expression is used after `if let`
 +  [#7566](https://github.com/rust-lang/rust-clippy/pull/7566)
 +* [`option_if_let_else`]: Multiple fixes
 +  [#7573](https://github.com/rust-lang/rust-clippy/pull/7573)
 +    * `break` and `continue` statements local to the would-be closure are
 +      allowed
 +    * Don't lint in const contexts
 +    * Don't lint when yield expressions are used
 +    * Don't lint when the captures made by the would-be closure conflict with
 +      the other branch
 +    * Don't lint when a field of a local is used when the type could be
 +      potentially moved from
 +    * In some cases, don't lint when scrutinee expression conflicts with the
 +      captures of the would-be closure
 +* [`redundant_allocation`]: No longer lints on `Box<Box<dyn T>>` which replaces
 +  wide pointers with thin pointers
 +  [#7592](https://github.com/rust-lang/rust-clippy/pull/7592)
 +* [`bool_assert_comparison`]: No longer lints on types that do not implement the
 +  `Not` trait with `Output = bool`
 +  [#7605](https://github.com/rust-lang/rust-clippy/pull/7605)
 +* [`mut_range_bound`]: No longer lints on range bound mutations, that are
 +  immediately followed by a `break;`
 +  [#7607](https://github.com/rust-lang/rust-clippy/pull/7607)
 +* [`mutable_key_type`]: Improve accuracy and document remaining false positives
 +  and false negatives
 +  [#7640](https://github.com/rust-lang/rust-clippy/pull/7640)
 +* [`redundant_closure`]: Rewrite the lint to fix various false positives and
 +  false negatives [#7661](https://github.com/rust-lang/rust-clippy/pull/7661)
 +* [`large_enum_variant`]: No longer wrongly identifies the second largest
 +  variant [#7677](https://github.com/rust-lang/rust-clippy/pull/7677)
 +* [`needless_return`]: No longer lints on let-else expressions
 +  [#7685](https://github.com/rust-lang/rust-clippy/pull/7685)
 +* [`suspicious_else_formatting`]: No longer lints in proc-macros
 +  [#7707](https://github.com/rust-lang/rust-clippy/pull/7707)
 +* [`excessive_precision`]: No longer lints when in some cases the float was
 +  already written in the shortest form
 +  [#7722](https://github.com/rust-lang/rust-clippy/pull/7722)
 +* [`doc_markdown`]: No longer lints on intra-doc links
 +  [#7772](https://github.com/rust-lang/rust-clippy/pull/7772)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`unnecessary_operation`]: Recommend using an `assert!` instead of using a
 +  function call in an indexing operation
 +  [#7453](https://github.com/rust-lang/rust-clippy/pull/7453)
 +* [`manual_split_once`]: Produce semantically equivalent suggestion when
 +  `rsplitn` is used [#7663](https://github.com/rust-lang/rust-clippy/pull/7663)
 +* [`while_let_on_iterator`]: Produce correct suggestion when using `&mut`
 +  [#7690](https://github.com/rust-lang/rust-clippy/pull/7690)
 +* [`manual_assert`]: No better handles complex conditions
 +  [#7741](https://github.com/rust-lang/rust-clippy/pull/7741)
 +* Correctly handle signs in exponents in numeric literals lints
 +  [#7747](https://github.com/rust-lang/rust-clippy/pull/7747)
 +* [`suspicious_map`]: Now also suggests to use `inspect` as an alternative
 +  [#7770](https://github.com/rust-lang/rust-clippy/pull/7770)
 +* Drop exponent from suggestion if it is 0 in numeric literals lints
 +  [#7774](https://github.com/rust-lang/rust-clippy/pull/7774)
 +
 +### ICE Fixes
 +
 +* [`implicit_hasher`]
 +  [#7761](https://github.com/rust-lang/rust-clippy/pull/7761)
 +
 +### Others
 +
 +* Clippy now uses the 2021
 +  [Edition!](https://www.youtube.com/watch?v=q0aNduqb2Ro)
 +  [#7664](https://github.com/rust-lang/rust-clippy/pull/7664)
 +
 +## Rust 1.56
 +
 +Released 2021-10-21
 +
 +[74d1561...7bfc26e](https://github.com/rust-lang/rust-clippy/compare/74d1561...7bfc26e)
 +
 +### New Lints
 +
 +* [`unwrap_or_else_default`]
 +  [#7516](https://github.com/rust-lang/rust-clippy/pull/7516)
 +
 +### Enhancements
 +
 +* [`needless_continue`]: Now also lints in `loop { continue; }` case
 +  [#7477](https://github.com/rust-lang/rust-clippy/pull/7477)
 +* [`disallowed_types`]: Now also primitive types can be disallowed
 +  [#7488](https://github.com/rust-lang/rust-clippy/pull/7488)
 +* [`manual_swap`]: Now also lints on xor swaps
 +  [#7506](https://github.com/rust-lang/rust-clippy/pull/7506)
 +* [`map_flatten`]: Now also lints on the `Result` type
 +  [#7522](https://github.com/rust-lang/rust-clippy/pull/7522)
 +* [`no_effect`]: Now also lints on inclusive ranges
 +  [#7556](https://github.com/rust-lang/rust-clippy/pull/7556)
 +
 +### False Positive Fixes
 +
 +* [`nonstandard_macro_braces`]: No longer lints on similar named nested macros
 +  [#7478](https://github.com/rust-lang/rust-clippy/pull/7478)
 +* [`too_many_lines`]: No longer lints in closures to avoid duplicated diagnostics
 +  [#7534](https://github.com/rust-lang/rust-clippy/pull/7534)
 +* [`similar_names`]: No longer complains about `iter` and `item` being too
 +  similar [#7546](https://github.com/rust-lang/rust-clippy/pull/7546)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`similar_names`]: No longer suggests to insert or add an underscore as a fix
 +  [#7221](https://github.com/rust-lang/rust-clippy/pull/7221)
 +* [`new_without_default`]: No longer shows the full qualified type path when
 +  suggesting adding a `Default` implementation
 +  [#7493](https://github.com/rust-lang/rust-clippy/pull/7493)
 +* [`while_let_on_iterator`]: Now suggests re-borrowing mutable references
 +  [#7520](https://github.com/rust-lang/rust-clippy/pull/7520)
 +* [`extend_with_drain`]: Improve code suggestion for mutable and immutable
 +  references [#7533](https://github.com/rust-lang/rust-clippy/pull/7533)
 +* [`trivially_copy_pass_by_ref`]: Now properly handles `Self` type
 +  [#7535](https://github.com/rust-lang/rust-clippy/pull/7535)
 +* [`never_loop`]: Now suggests using `if let` instead of a `for` loop when
 +  applicable [#7541](https://github.com/rust-lang/rust-clippy/pull/7541)
 +
 +### Documentation Improvements
 +
 +* Clippy now uses a lint to generate its lint documentation. [Lints all the way
 +  down](https://en.wikipedia.org/wiki/Turtles_all_the_way_down).
 +  [#7502](https://github.com/rust-lang/rust-clippy/pull/7502)
 +* Reworked Clippy's website:
 +  [#7172](https://github.com/rust-lang/rust-clippy/issues/7172)
 +  [#7279](https://github.com/rust-lang/rust-clippy/pull/7279)
 +  * Added applicability information about lints
 +  * Added a link to jump into the implementation
 +  * Improved loading times
 +  * Adapted some styling
 +* `cargo clippy --help` now also explains the `--fix` and `--no-deps` flag
 +  [#7492](https://github.com/rust-lang/rust-clippy/pull/7492)
 +* [`unnested_or_patterns`]: Removed `or_patterns` feature gate in the code
 +  example [#7507](https://github.com/rust-lang/rust-clippy/pull/7507)
 +
 +## Rust 1.55
 +
 +Released 2021-09-09
 +
 +[3ae8faf...74d1561](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...74d1561)
 +
 +### Important Changes
 +
 +* Stabilized `cargo clippy --fix` :tada:
 +  [#7405](https://github.com/rust-lang/rust-clippy/pull/7405)
 +
 +### New Lints
 +
 +* [`rc_mutex`]
 +  [#7316](https://github.com/rust-lang/rust-clippy/pull/7316)
 +* [`nonstandard_macro_braces`]
 +  [#7299](https://github.com/rust-lang/rust-clippy/pull/7299)
 +* [`strlen_on_c_strings`]
 +  [#7243](https://github.com/rust-lang/rust-clippy/pull/7243)
 +* [`self_named_constructors`]
 +  [#7403](https://github.com/rust-lang/rust-clippy/pull/7403)
 +* [`disallowed_script_idents`]
 +  [#7400](https://github.com/rust-lang/rust-clippy/pull/7400)
 +* [`disallowed_types`]
 +  [#7315](https://github.com/rust-lang/rust-clippy/pull/7315)
 +* [`missing_enforced_import_renames`]
 +  [#7300](https://github.com/rust-lang/rust-clippy/pull/7300)
 +* [`extend_with_drain`]
 +  [#7270](https://github.com/rust-lang/rust-clippy/pull/7270)
 +
 +### Moves and Deprecations
 +
 +* Moved [`from_iter_instead_of_collect`] to `pedantic`
 +  [#7375](https://github.com/rust-lang/rust-clippy/pull/7375)
 +* Added `suspicious` as a new lint group for *code that is most likely wrong or useless*
 +  [#7350](https://github.com/rust-lang/rust-clippy/pull/7350)
 +  * Moved [`blanket_clippy_restriction_lints`] to `suspicious`
 +  * Moved [`empty_loop`] to `suspicious`
 +  * Moved [`eval_order_dependence`] to `suspicious`
 +  * Moved [`float_equality_without_abs`] to `suspicious`
 +  * Moved [`for_loops_over_fallibles`] to `suspicious`
 +  * Moved [`misrefactored_assign_op`] to `suspicious`
 +  * Moved [`mut_range_bound`] to `suspicious`
 +  * Moved [`mutable_key_type`] to `suspicious`
 +  * Moved [`suspicious_arithmetic_impl`] to `suspicious`
 +  * Moved [`suspicious_assignment_formatting`] to `suspicious`
 +  * Moved [`suspicious_else_formatting`] to `suspicious`
 +  * Moved [`suspicious_map`] to `suspicious`
 +  * Moved [`suspicious_op_assign_impl`] to `suspicious`
 +  * Moved [`suspicious_unary_op_formatting`] to `suspicious`
 +
 +### Enhancements
 +
 +* [`while_let_on_iterator`]: Now suggests `&mut iter` inside closures
 +  [#7262](https://github.com/rust-lang/rust-clippy/pull/7262)
 +* [`doc_markdown`]:
 +  * Now detects unbalanced ticks
 +    [#7357](https://github.com/rust-lang/rust-clippy/pull/7357)
 +  * Add `FreeBSD` to the default configuration as an allowed identifier
 +    [#7334](https://github.com/rust-lang/rust-clippy/pull/7334)
 +* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]: Now allows wildcards for enums with unstable
 +  or hidden variants
 +  [#7407](https://github.com/rust-lang/rust-clippy/pull/7407)
 +* [`redundant_allocation`]: Now additionally supports the `Arc<>` type
 +  [#7308](https://github.com/rust-lang/rust-clippy/pull/7308)
 +* [`blacklisted_name`]: Now allows blacklisted names in test code
 +  [#7379](https://github.com/rust-lang/rust-clippy/pull/7379)
 +* [`redundant_closure`]: Suggests `&mut` for `FnMut`
 +  [#7437](https://github.com/rust-lang/rust-clippy/pull/7437)
 +* [`disallowed_methods`], [`disallowed_types`]: The configuration values `disallowed-method` and `disallowed-type`
 +  no longer require fully qualified paths
 +  [#7345](https://github.com/rust-lang/rust-clippy/pull/7345)
 +* [`zst_offset`]: Fixed lint invocation after it was accidentally suppressed
 +  [#7396](https://github.com/rust-lang/rust-clippy/pull/7396)
 +
 +### False Positive Fixes
 +
 +* [`default_numeric_fallback`]: No longer lints on float literals as function arguments
 +  [#7446](https://github.com/rust-lang/rust-clippy/pull/7446)
 +* [`use_self`]: No longer lints on type parameters
 +  [#7288](https://github.com/rust-lang/rust-clippy/pull/7288)
 +* [`unimplemented`]: Now ignores the `assert` and `debug_assert` macros
 +  [#7439](https://github.com/rust-lang/rust-clippy/pull/7439)
 +* [`branches_sharing_code`]: Now always checks for block expressions
 +  [#7462](https://github.com/rust-lang/rust-clippy/pull/7462)
 +* [`field_reassign_with_default`]: No longer triggers in macros
 +  [#7160](https://github.com/rust-lang/rust-clippy/pull/7160)
 +* [`redundant_clone`]: No longer lints on required clones for borrowed data
 +  [#7346](https://github.com/rust-lang/rust-clippy/pull/7346)
 +* [`default_numeric_fallback`]: No longer triggers in external macros
 +  [#7325](https://github.com/rust-lang/rust-clippy/pull/7325)
 +* [`needless_bool`]: No longer lints in macros
 +  [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
 +* [`useless_format`]: No longer triggers when additional text is being appended
 +  [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
 +* [`assertions_on_constants`]: `cfg!(...)` is no longer considered to be a constant
 +  [#7319](https://github.com/rust-lang/rust-clippy/pull/7319)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`needless_collect`]: Now show correct lint messages for shadowed values
 +  [#7289](https://github.com/rust-lang/rust-clippy/pull/7289)
 +* [`wrong_pub_self_convention`]: The deprecated message now suggest the correct configuration value
 +  [#7382](https://github.com/rust-lang/rust-clippy/pull/7382)
 +* [`semicolon_if_nothing_returned`]: Allow missing semicolon in blocks with only one expression
 +  [#7326](https://github.com/rust-lang/rust-clippy/pull/7326)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#7470](https://github.com/rust-lang/rust-clippy/pull/7470)
 +* [`redundant_pattern_matching`]
 +  [#7471](https://github.com/rust-lang/rust-clippy/pull/7471)
 +* [`modulo_one`]
 +  [#7473](https://github.com/rust-lang/rust-clippy/pull/7473)
 +* [`use_self`]
 +  [#7428](https://github.com/rust-lang/rust-clippy/pull/7428)
 +
 +## Rust 1.54
 +
 +Released 2021-07-29
 +
 +[7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
 +
 +### New Lints
 +
 +- [`ref_binding_to_reference`]
 +  [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
 +- [`needless_bitwise_bool`]
 +  [#7133](https://github.com/rust-lang/rust-clippy/pull/7133)
 +- [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225)
 +- [`manual_str_repeat`]
 +  [#7265](https://github.com/rust-lang/rust-clippy/pull/7265)
 +- [`suspicious_splitn`]
 +  [#7292](https://github.com/rust-lang/rust-clippy/pull/7292)
 +
 +### Moves and Deprecations
 +
 +- Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of
 +  the new `avoid-breaking-exported-api` config option (see
 +  [Enhancements](#1-54-enhancements))
 +  [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
 +- Move [`inconsistent_struct_constructor`] to `pedantic`
 +  [#7193](https://github.com/rust-lang/rust-clippy/pull/7193)
 +- Move [`needless_borrow`] to `style` (now warn-by-default)
 +  [#7254](https://github.com/rust-lang/rust-clippy/pull/7254)
 +- Move [`suspicious_operation_groupings`] to `nursery`
 +  [#7266](https://github.com/rust-lang/rust-clippy/pull/7266)
 +- Move [`semicolon_if_nothing_returned`] to `pedantic`
 +  [#7268](https://github.com/rust-lang/rust-clippy/pull/7268)
 +
 +### Enhancements <a name="1-54-enhancements"></a>
 +
 +- [`while_let_on_iterator`]: Now also lints in nested loops
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix`
 +  [#7156](https://github.com/rust-lang/rust-clippy/pull/7156)
 +- [`needless_collect`]: Now also lints on assignments with type annotations
 +  [#7163](https://github.com/rust-lang/rust-clippy/pull/7163)
 +- [`if_then_some_else_none`]: Now works with the MSRV config
 +  [#7177](https://github.com/rust-lang/rust-clippy/pull/7177)
 +- Add `avoid-breaking-exported-api` config option for the lints
 +  [`enum_variant_names`], [`large_types_passed_by_value`],
 +  [`trivially_copy_pass_by_ref`], [`unnecessary_wraps`],
 +  [`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set
 +  this configuration option to `false` before a major release (1.0/2.0/...) to
 +  clean up the API [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
 +- [`needless_collect`]: Now lints on even more data structures
 +  [#7188](https://github.com/rust-lang/rust-clippy/pull/7188)
 +- [`missing_docs_in_private_items`]: No longer sees `#[<name> = "<value>"]` like
 +  attributes as sufficient documentation
 +  [#7281](https://github.com/rust-lang/rust-clippy/pull/7281)
 +- [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]:
 +  Now work as expected when used with `allow`
 +  [#7282](https://github.com/rust-lang/rust-clippy/pull/7282)
 +
 +### False Positive Fixes
 +
 +- [`implicit_return`]: Now takes all diverging functions in account to avoid
 +  false positives [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
 +- [`while_let_on_iterator`]: No longer lints when the iterator is a struct field
 +  and the struct is used in the loop
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`multiple_inherent_impl`]: No longer lints with generic arguments
 +  [#7089](https://github.com/rust-lang/rust-clippy/pull/7089)
 +- [`comparison_chain`]: No longer lints in a `const` context
 +  [#7118](https://github.com/rust-lang/rust-clippy/pull/7118)
 +- [`while_immutable_condition`]: Fix false positive where mutation in the loop
 +  variable wasn't picked up
 +  [#7144](https://github.com/rust-lang/rust-clippy/pull/7144)
 +- [`default_trait_access`]: No longer lints in macros
 +  [#7150](https://github.com/rust-lang/rust-clippy/pull/7150)
 +- [`needless_question_mark`]: No longer lints when the inner value is implicitly
 +  dereferenced [#7165](https://github.com/rust-lang/rust-clippy/pull/7165)
 +- [`unused_unit`]: No longer lints when multiple macro contexts are involved
 +  [#7167](https://github.com/rust-lang/rust-clippy/pull/7167)
 +- [`eval_order_dependence`]: Fix false positive in async context
 +  [#7174](https://github.com/rust-lang/rust-clippy/pull/7174)
 +- [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the
 +  type [#7175](https://github.com/rust-lang/rust-clippy/pull/7175)
 +- [`wrong_self_convention`]: No longer lints in trait implementations of
 +  non-`Copy` types [#7182](https://github.com/rust-lang/rust-clippy/pull/7182)
 +- [`suboptimal_flops`]: No longer lints on `powi(2)`
 +  [#7201](https://github.com/rust-lang/rust-clippy/pull/7201)
 +- [`wrong_self_convention`]: No longer lints if there is no implicit `self`
 +  [#7215](https://github.com/rust-lang/rust-clippy/pull/7215)
 +- [`option_if_let_else`]: No longer lints on `else if let` pattern
 +  [#7216](https://github.com/rust-lang/rust-clippy/pull/7216)
 +- [`use_self`], [`useless_conversion`]: Fix false positives when generic
 +  arguments are involved
 +  [#7223](https://github.com/rust-lang/rust-clippy/pull/7223)
 +- [`manual_unwrap_or`]: Fix false positive with deref coercion
 +  [#7233](https://github.com/rust-lang/rust-clippy/pull/7233)
 +- [`similar_names`]: No longer lints on `wparam`/`lparam`
 +  [#7255](https://github.com/rust-lang/rust-clippy/pull/7255)
 +- [`redundant_closure`]: No longer lints on using the `vec![]` macro in a
 +  closure [#7263](https://github.com/rust-lang/rust-clippy/pull/7263)
 +
 +### Suggestion Fixes/Improvements
 +
 +- [`implicit_return`]
 +  [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
 +    - Fix suggestion for async functions
 +    - Improve suggestion with macros
 +    - Suggest to change `break` to `return` when appropriate
 +- [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`match_single_binding`]: Improve suggestion when match scrutinee has side
 +  effects [#7095](https://github.com/rust-lang/rust-clippy/pull/7095)
 +- [`needless_borrow`]: Now suggests to also change usage sites as needed
 +  [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
 +- [`write_with_newline`]: Improve suggestion when only `\n` is written to the
 +  buffer [#7183](https://github.com/rust-lang/rust-clippy/pull/7183)
 +- [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also
 +  when a `<_ as Trait>::_` is involved
 +  [#7264](https://github.com/rust-lang/rust-clippy/pull/7264)
 +- [`not_unsafe_ptr_arg_deref`]: Improved error message
 +  [#7294](https://github.com/rust-lang/rust-clippy/pull/7294)
 +
 +### ICE Fixes
 +
 +- Fix ICE when running Clippy on `libstd`
 +  [#7140](https://github.com/rust-lang/rust-clippy/pull/7140)
 +- [`implicit_return`]
 +  [#7242](https://github.com/rust-lang/rust-clippy/pull/7242)
 +
 +## Rust 1.53
 +
 +Released 2021-06-17
 +
 +[6ed6f1e...7c7683c](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...7c7683c)
 +
 +### New Lints
 +
 +* [`option_filter_map`]
 +  [#6342](https://github.com/rust-lang/rust-clippy/pull/6342)
 +* [`branches_sharing_code`]
 +  [#6463](https://github.com/rust-lang/rust-clippy/pull/6463)
 +* [`needless_for_each`]
 +  [#6706](https://github.com/rust-lang/rust-clippy/pull/6706)
 +* [`if_then_some_else_none`]
 +  [#6859](https://github.com/rust-lang/rust-clippy/pull/6859)
 +* [`non_octal_unix_permissions`]
 +  [#7001](https://github.com/rust-lang/rust-clippy/pull/7001)
 +* [`unnecessary_self_imports`]
 +  [#7072](https://github.com/rust-lang/rust-clippy/pull/7072)
 +* [`bool_assert_comparison`]
 +  [#7083](https://github.com/rust-lang/rust-clippy/pull/7083)
 +* [`cloned_instead_of_copied`]
 +  [#7098](https://github.com/rust-lang/rust-clippy/pull/7098)
 +* [`flat_map_option`]
 +  [#7101](https://github.com/rust-lang/rust-clippy/pull/7101)
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`filter_map`] lint
 +  [#7059](https://github.com/rust-lang/rust-clippy/pull/7059)
 +* Move [`transmute_ptr_to_ptr`] to `pedantic`
 +  [#7102](https://github.com/rust-lang/rust-clippy/pull/7102)
 +
 +### Enhancements
 +
 +* [`mem_replace_with_default`]: Also lint on common std constructors
 +  [#6820](https://github.com/rust-lang/rust-clippy/pull/6820)
 +* [`wrong_self_convention`]: Also lint on `to_*_mut` methods
 +  [#6828](https://github.com/rust-lang/rust-clippy/pull/6828)
 +* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]:
 +  [#6863](https://github.com/rust-lang/rust-clippy/pull/6863)
 +    * Attempt to find a common path prefix in suggestion
 +    * Don't lint on `Option` and `Result`
 +    * Consider `Self` prefix
 +* [`explicit_deref_methods`]: Also lint on chained `deref` calls
 +  [#6865](https://github.com/rust-lang/rust-clippy/pull/6865)
 +* [`or_fun_call`]: Also lint on `unsafe` blocks
 +  [#6928](https://github.com/rust-lang/rust-clippy/pull/6928)
 +* [`vec_box`], [`linkedlist`], [`option_option`]: Also lint in `const` and
 +  `static` items [#6938](https://github.com/rust-lang/rust-clippy/pull/6938)
 +* [`search_is_some`]: Also check for `is_none`
 +  [#6942](https://github.com/rust-lang/rust-clippy/pull/6942)
 +* [`string_lit_as_bytes`]: Also lint on `into_bytes`
 +  [#6959](https://github.com/rust-lang/rust-clippy/pull/6959)
 +* [`len_without_is_empty`]: Also lint if function signatures of `len` and
 +  `is_empty` don't match
 +  [#6980](https://github.com/rust-lang/rust-clippy/pull/6980)
 +* [`redundant_pattern_matching`]: Also lint if the pattern is a `&` pattern
 +  [#6991](https://github.com/rust-lang/rust-clippy/pull/6991)
 +* [`clone_on_copy`]: Also lint on chained method calls taking `self` by value
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`missing_panics_doc`]: Also lint on `assert_eq!` and `assert_ne!`
 +  [#7029](https://github.com/rust-lang/rust-clippy/pull/7029)
 +* [`needless_return`]: Also lint in `async` functions
 +  [#7067](https://github.com/rust-lang/rust-clippy/pull/7067)
 +* [`unused_io_amount`]: Also lint on expressions like `_.read().ok()?`
 +  [#7100](https://github.com/rust-lang/rust-clippy/pull/7100)
 +* [`iter_cloned_collect`]: Also lint on large arrays, since const-generics are
 +  now stable [#7138](https://github.com/rust-lang/rust-clippy/pull/7138)
 +
 +### False Positive Fixes
 +
 +* [`upper_case_acronyms`]: No longer lints on public items
 +  [#6805](https://github.com/rust-lang/rust-clippy/pull/6805)
 +* [`suspicious_map`]: No longer lints when side effects may occur inside the
 +  `map` call [#6831](https://github.com/rust-lang/rust-clippy/pull/6831)
 +* [`manual_map`], [`manual_unwrap_or`]: No longer lints in `const` functions
 +  [#6917](https://github.com/rust-lang/rust-clippy/pull/6917)
 +* [`wrong_self_convention`]: Now respects `Copy` types
 +  [#6924](https://github.com/rust-lang/rust-clippy/pull/6924)
 +* [`needless_question_mark`]: No longer lints if the `?` and the `Some(..)` come
 +  from different macro contexts [#6935](https://github.com/rust-lang/rust-clippy/pull/6935)
 +* [`map_entry`]: Better detect if the entry API can be used
 +  [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
 +* [`or_fun_call`]: No longer lints on some `len` function calls
 +  [#6950](https://github.com/rust-lang/rust-clippy/pull/6950)
 +* [`new_ret_no_self`]: No longer lints when `Self` is returned with different
 +  generic arguments [#6952](https://github.com/rust-lang/rust-clippy/pull/6952)
 +* [`upper_case_acronyms`]: No longer lints on public items
 +  [#6981](https://github.com/rust-lang/rust-clippy/pull/6981)
 +* [`explicit_into_iter_loop`]: Only lint when `into_iter` is an implementation
 +  of `IntoIterator` [#6982](https://github.com/rust-lang/rust-clippy/pull/6982)
 +* [`expl_impl_clone_on_copy`]: Take generic constraints into account before
 +  suggesting to use `derive` instead
 +  [#6993](https://github.com/rust-lang/rust-clippy/pull/6993)
 +* [`missing_panics_doc`]: No longer lints when only debug-assertions are used
 +  [#6996](https://github.com/rust-lang/rust-clippy/pull/6996)
 +* [`clone_on_copy`]: Only lint when using the `Clone` trait
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`wrong_self_convention`]: No longer lints inside a trait implementation
 +  [#7002](https://github.com/rust-lang/rust-clippy/pull/7002)
 +* [`redundant_clone`]: No longer lints when the cloned value is modified while
 +  the clone is in use
 +  [#7011](https://github.com/rust-lang/rust-clippy/pull/7011)
 +* [`same_item_push`]: No longer lints if the `Vec` is used in the loop body
 +  [#7018](https://github.com/rust-lang/rust-clippy/pull/7018)
 +* [`cargo_common_metadata`]: Remove author requirement
 +  [#7026](https://github.com/rust-lang/rust-clippy/pull/7026)
 +* [`panic_in_result_fn`]: No longer lints on `debug_assert` family
 +  [#7060](https://github.com/rust-lang/rust-clippy/pull/7060)
 +* [`panic`]: No longer wrongfully lints on `debug_assert` with message
 +  [#7063](https://github.com/rust-lang/rust-clippy/pull/7063)
 +* [`wrong_self_convention`]: No longer lints in trait implementations where no
 +  `self` is involved [#7064](https://github.com/rust-lang/rust-clippy/pull/7064)
 +* [`missing_const_for_fn`]: No longer lints when unstable `const` function is
 +  involved [#7076](https://github.com/rust-lang/rust-clippy/pull/7076)
 +* [`suspicious_else_formatting`]: Allow Allman style braces
 +  [#7087](https://github.com/rust-lang/rust-clippy/pull/7087)
 +* [`inconsistent_struct_constructor`]: No longer lints in macros
 +  [#7097](https://github.com/rust-lang/rust-clippy/pull/7097)
 +* [`single_component_path_imports`]: No longer lints on macro re-exports
 +  [#7120](https://github.com/rust-lang/rust-clippy/pull/7120)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`redundant_pattern_matching`]: Add a note when applying this lint would
 +  change the drop order
 +  [#6568](https://github.com/rust-lang/rust-clippy/pull/6568)
 +* [`write_literal`], [`print_literal`]: Add auto-applicable suggestion
 +  [#6821](https://github.com/rust-lang/rust-clippy/pull/6821)
 +* [`manual_map`]: Fix suggestion for complex `if let ... else` chains
 +  [#6856](https://github.com/rust-lang/rust-clippy/pull/6856)
 +* [`inconsistent_struct_constructor`]: Make lint description and message clearer
 +  [#6892](https://github.com/rust-lang/rust-clippy/pull/6892)
 +* [`map_entry`]: Now suggests `or_insert`, `insert_with` or `match _.entry(_)`
 +  as appropriate [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
 +* [`manual_flatten`]: Suggest to insert `copied` if necessary
 +  [#6962](https://github.com/rust-lang/rust-clippy/pull/6962)
 +* [`redundant_slicing`]: Fix suggestion when a re-borrow might be required or
 +  when the value is from a macro call
 +  [#6975](https://github.com/rust-lang/rust-clippy/pull/6975)
 +* [`match_wildcard_for_single_variants`]: Fix suggestion for hidden variant
 +  [#6988](https://github.com/rust-lang/rust-clippy/pull/6988)
 +* [`clone_on_copy`]: Correct suggestion when the cloned value is a macro call
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`manual_map`]: Fix suggestion at the end of an if chain
 +  [#7004](https://github.com/rust-lang/rust-clippy/pull/7004)
 +* Fix needless parenthesis output in multiple lint suggestions
 +  [#7013](https://github.com/rust-lang/rust-clippy/pull/7013)
 +* [`needless_collect`]: Better explanation in the lint message
 +  [#7020](https://github.com/rust-lang/rust-clippy/pull/7020)
 +* [`useless_vec`]: Now considers mutability
 +  [#7036](https://github.com/rust-lang/rust-clippy/pull/7036)
 +* [`useless_format`]: Wrap the content in braces if necessary
 +  [#7092](https://github.com/rust-lang/rust-clippy/pull/7092)
 +* [`single_match`]: Don't suggest an equality check for types which don't
 +  implement `PartialEq`
 +  [#7093](https://github.com/rust-lang/rust-clippy/pull/7093)
 +* [`from_over_into`]: Mention type in help message
 +  [#7099](https://github.com/rust-lang/rust-clippy/pull/7099)
 +* [`manual_unwrap_or`]: Fix invalid code suggestion due to a macro call
 +  [#7136](https://github.com/rust-lang/rust-clippy/pull/7136)
 +
 +### ICE Fixes
 +
 +* [`macro_use_imports`]
 +  [#7022](https://github.com/rust-lang/rust-clippy/pull/7022)
 +* [`missing_panics_doc`]
 +  [#7034](https://github.com/rust-lang/rust-clippy/pull/7034)
 +* [`tabs_in_doc_comments`]
 +  [#7039](https://github.com/rust-lang/rust-clippy/pull/7039)
 +* [`missing_const_for_fn`]
 +  [#7128](https://github.com/rust-lang/rust-clippy/pull/7128)
 +
 +### Others
 +
 +* [Clippy's lint
 +  list](https://rust-lang.github.io/rust-clippy/master/index.html) now supports
 +  themes [#7030](https://github.com/rust-lang/rust-clippy/pull/7030)
 +* Lints that were uplifted to `rustc` now mention the new `rustc` name in the
 +  deprecation warning
 +  [#7056](https://github.com/rust-lang/rust-clippy/pull/7056)
 +
 +## Rust 1.52
 +
 +Released 2021-05-06
 +
 +[3e41797...6ed6f1e](https://github.com/rust-lang/rust-clippy/compare/3e41797...6ed6f1e)
 +
 +### New Lints
 +
 +* [`from_str_radix_10`]
 +  [#6717](https://github.com/rust-lang/rust-clippy/pull/6717)
 +* [`implicit_clone`]
 +  [#6730](https://github.com/rust-lang/rust-clippy/pull/6730)
 +* [`semicolon_if_nothing_returned`]
 +  [#6681](https://github.com/rust-lang/rust-clippy/pull/6681)
 +* [`manual_flatten`]
 +  [#6646](https://github.com/rust-lang/rust-clippy/pull/6646)
 +* [`inconsistent_struct_constructor`]
 +  [#6769](https://github.com/rust-lang/rust-clippy/pull/6769)
 +* [`iter_count`]
 +  [#6791](https://github.com/rust-lang/rust-clippy/pull/6791)
 +* [`default_numeric_fallback`]
 +  [#6662](https://github.com/rust-lang/rust-clippy/pull/6662)
 +* [`bytes_nth`]
 +  [#6695](https://github.com/rust-lang/rust-clippy/pull/6695)
 +* [`filter_map_identity`]
 +  [#6685](https://github.com/rust-lang/rust-clippy/pull/6685)
 +* [`manual_map`]
 +  [#6573](https://github.com/rust-lang/rust-clippy/pull/6573)
 +
 +### Moves and Deprecations
 +
 +* Moved [`upper_case_acronyms`] to `pedantic`
 +  [#6775](https://github.com/rust-lang/rust-clippy/pull/6775)
 +* Moved [`manual_map`] to `nursery`
 +  [#6796](https://github.com/rust-lang/rust-clippy/pull/6796)
 +* Moved [`unnecessary_wraps`] to `pedantic`
 +  [#6765](https://github.com/rust-lang/rust-clippy/pull/6765)
 +* Moved [`trivial_regex`] to `nursery`
 +  [#6696](https://github.com/rust-lang/rust-clippy/pull/6696)
 +* Moved [`naive_bytecount`] to `pedantic`
 +  [#6825](https://github.com/rust-lang/rust-clippy/pull/6825)
 +* Moved [`upper_case_acronyms`] to `style`
 +  [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
 +* Moved [`manual_map`] to `style`
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +
 +### Enhancements
 +
 +* [`disallowed_methods`]: Now supports functions in addition to methods
 +  [#6674](https://github.com/rust-lang/rust-clippy/pull/6674)
 +* [`upper_case_acronyms`]: Added a new configuration `upper-case-acronyms-aggressive` to
 +  trigger the lint if there is more than one uppercase character next to each other
 +  [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
 +* [`collapsible_match`]: Now supports block comparison with different value names
 +  [#6754](https://github.com/rust-lang/rust-clippy/pull/6754)
 +* [`unnecessary_wraps`]: Will now suggest removing unnecessary wrapped return unit type, like `Option<()>`
 +  [#6665](https://github.com/rust-lang/rust-clippy/pull/6665)
 +* Improved value usage detection in closures
 +  [#6698](https://github.com/rust-lang/rust-clippy/pull/6698)
 +
 +### False Positive Fixes
 +
 +* [`use_self`]: No longer lints in macros
 +  [#6833](https://github.com/rust-lang/rust-clippy/pull/6833)
 +* [`use_self`]: Fixed multiple false positives for: generics, associated types and derive implementations
 +  [#6179](https://github.com/rust-lang/rust-clippy/pull/6179)
 +* [`missing_inline_in_public_items`]: No longer lints for procedural macros
 +  [#6814](https://github.com/rust-lang/rust-clippy/pull/6814)
 +* [`inherent_to_string`]: No longer lints on functions with function generics
 +  [#6771](https://github.com/rust-lang/rust-clippy/pull/6771)
 +* [`doc_markdown`]: Add `OpenDNS` to the default configuration as an allowed identifier
 +  [#6783](https://github.com/rust-lang/rust-clippy/pull/6783)
 +* [`missing_panics_doc`]: No longer lints on [`unreachable!`](https://doc.rust-lang.org/std/macro.unreachable.html)
 +  [#6700](https://github.com/rust-lang/rust-clippy/pull/6700)
 +* [`collapsible_if`]: No longer lints on if statements with attributes
 +  [#6701](https://github.com/rust-lang/rust-clippy/pull/6701)
 +* [`match_same_arms`]: Only considers empty blocks as equal if the tokens contained are the same
 +  [#6843](https://github.com/rust-lang/rust-clippy/pull/6843)
 +* [`redundant_closure`]: Now ignores macros
 +  [#6871](https://github.com/rust-lang/rust-clippy/pull/6871)
 +* [`manual_map`]: Fixed false positives when control flow statements like `return`, `break` etc. are used
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +* [`vec_init_then_push`]: Fixed false positives for loops and if statements
 +  [#6697](https://github.com/rust-lang/rust-clippy/pull/6697)
 +* [`len_without_is_empty`]: Will now consider multiple impl blocks and `#[allow]` on
 +  the `len` method as well as the type definition.
 +  [#6853](https://github.com/rust-lang/rust-clippy/pull/6853)
 +* [`let_underscore_drop`]: Only lints on types which implement `Drop`
 +  [#6682](https://github.com/rust-lang/rust-clippy/pull/6682)
 +* [`unit_arg`]: No longer lints on unit arguments when they come from a path expression.
 +  [#6601](https://github.com/rust-lang/rust-clippy/pull/6601)
 +* [`cargo_common_metadata`]: No longer lints if
 +  [`publish = false`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field)
 +  is defined in the manifest
 +  [#6650](https://github.com/rust-lang/rust-clippy/pull/6650)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`collapsible_match`]: Fixed lint message capitalization
 +  [#6766](https://github.com/rust-lang/rust-clippy/pull/6766)
 +* [`or_fun_call`]: Improved suggestions for `or_insert(vec![])`
 +  [#6790](https://github.com/rust-lang/rust-clippy/pull/6790)
 +* [`manual_map`]: No longer expands macros in the suggestions
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +* Aligned Clippy's lint messages with the rustc dev guide
 +  [#6787](https://github.com/rust-lang/rust-clippy/pull/6787)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#6866](https://github.com/rust-lang/rust-clippy/pull/6866)
 +
 +### Documentation Improvements
 +
 +* [`useless_format`]: Improved the documentation example
 +  [#6854](https://github.com/rust-lang/rust-clippy/pull/6854)
 +* Clippy's [`README.md`]: Includes a new subsection on running Clippy as a rustc wrapper
 +  [#6782](https://github.com/rust-lang/rust-clippy/pull/6782)
 +
 +### Others
 +* Running `cargo clippy` after `cargo check` now works as expected
 +  (`cargo clippy` and `cargo check` no longer shares the same build cache)
 +  [#6687](https://github.com/rust-lang/rust-clippy/pull/6687)
 +* Cargo now re-runs Clippy if arguments after `--` provided to `cargo clippy` are changed.
 +  [#6834](https://github.com/rust-lang/rust-clippy/pull/6834)
 +* Extracted Clippy's `utils` module into the new `clippy_utils` crate
 +  [#6756](https://github.com/rust-lang/rust-clippy/pull/6756)
 +* Clippy lintcheck tool improvements
 +  [#6800](https://github.com/rust-lang/rust-clippy/pull/6800)
 +  [#6735](https://github.com/rust-lang/rust-clippy/pull/6735)
 +  [#6764](https://github.com/rust-lang/rust-clippy/pull/6764)
 +  [#6708](https://github.com/rust-lang/rust-clippy/pull/6708)
 +  [#6780](https://github.com/rust-lang/rust-clippy/pull/6780)
 +  [#6686](https://github.com/rust-lang/rust-clippy/pull/6686)
 +
 +## Rust 1.51
 +
 +Released 2021-03-25
 +
 +[4911ab1...3e41797](https://github.com/rust-lang/rust-clippy/compare/4911ab1...3e41797)
 +
 +### New Lints
 +
 +* [`upper_case_acronyms`]
 +  [#6475](https://github.com/rust-lang/rust-clippy/pull/6475)
 +* [`from_over_into`] [#6476](https://github.com/rust-lang/rust-clippy/pull/6476)
 +* [`case_sensitive_file_extension_comparisons`]
 +  [#6500](https://github.com/rust-lang/rust-clippy/pull/6500)
 +* [`needless_question_mark`]
 +  [#6507](https://github.com/rust-lang/rust-clippy/pull/6507)
 +* [`missing_panics_doc`]
 +  [#6523](https://github.com/rust-lang/rust-clippy/pull/6523)
 +* [`redundant_slicing`]
 +  [#6528](https://github.com/rust-lang/rust-clippy/pull/6528)
 +* [`vec_init_then_push`]
 +  [#6538](https://github.com/rust-lang/rust-clippy/pull/6538)
 +* [`ptr_as_ptr`] [#6542](https://github.com/rust-lang/rust-clippy/pull/6542)
 +* [`collapsible_else_if`] (split out from `collapsible_if`)
 +  [#6544](https://github.com/rust-lang/rust-clippy/pull/6544)
 +* [`inspect_for_each`] [#6577](https://github.com/rust-lang/rust-clippy/pull/6577)
 +* [`manual_filter_map`]
 +  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
 +* [`exhaustive_enums`]
 +  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
 +* [`exhaustive_structs`]
 +  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
 +
 +### Moves and Deprecations
 +
 +* Replace [`find_map`] with [`manual_find_map`]
 +  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
 +* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint
 +  [#6653](https://github.com/rust-lang/rust-clippy/pull/6653)
 +
 +### Enhancements
 +
 +* [`ptr_arg`] Now also suggests to use `&Path` instead of `&PathBuf`
 +  [#6506](https://github.com/rust-lang/rust-clippy/pull/6506)
 +* [`cast_ptr_alignment`] Also lint when the `pointer::cast` method is used
 +  [#6557](https://github.com/rust-lang/rust-clippy/pull/6557)
 +* [`collapsible_match`] Now also deals with `&` and `*` operators in the `match`
 +  scrutinee [#6619](https://github.com/rust-lang/rust-clippy/pull/6619)
 +
 +### False Positive Fixes
 +
 +* [`similar_names`] Ignore underscore prefixed names
 +  [#6403](https://github.com/rust-lang/rust-clippy/pull/6403)
 +* [`print_literal`] and [`write_literal`] No longer lint numeric literals
 +  [#6408](https://github.com/rust-lang/rust-clippy/pull/6408)
 +* [`large_enum_variant`] No longer lints in external macros
 +  [#6485](https://github.com/rust-lang/rust-clippy/pull/6485)
 +* [`empty_enum`] Only lint if `never_type` feature is enabled
 +  [#6513](https://github.com/rust-lang/rust-clippy/pull/6513)
 +* [`field_reassign_with_default`] No longer lints in macros
 +  [#6553](https://github.com/rust-lang/rust-clippy/pull/6553)
 +* [`size_of_in_element_count`] No longer lints when dividing by element size
 +  [#6578](https://github.com/rust-lang/rust-clippy/pull/6578)
 +* [`needless_return`] No longer lints in macros
 +  [#6586](https://github.com/rust-lang/rust-clippy/pull/6586)
 +* [`match_overlapping_arm`] No longer lint when first arm is completely included
 +  in second arm [#6603](https://github.com/rust-lang/rust-clippy/pull/6603)
 +* [`doc_markdown`] Add `WebGL` to the default configuration as an allowed
 +  identifier [#6605](https://github.com/rust-lang/rust-clippy/pull/6605)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`field_reassign_with_default`] Don't expand macro in lint suggestion
 +  [#6531](https://github.com/rust-lang/rust-clippy/pull/6531)
 +* [`match_like_matches_macro`] Strip references in suggestion
 +  [#6532](https://github.com/rust-lang/rust-clippy/pull/6532)
 +* [`single_match`] Suggest `if` over `if let` when possible
 +  [#6574](https://github.com/rust-lang/rust-clippy/pull/6574)
 +* `ref_in_deref` Use parentheses correctly in suggestion
 +  [#6609](https://github.com/rust-lang/rust-clippy/pull/6609)
 +* [`stable_sort_primitive`] Clarify error message
 +  [#6611](https://github.com/rust-lang/rust-clippy/pull/6611)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#6582](https://github.com/rust-lang/rust-clippy/pull/6582)
 +
 +### Documentation Improvements
 +
 +* Improve search performance on the Clippy website and make it possible to
 +  directly search for lints on the GitHub issue tracker
 +  [#6483](https://github.com/rust-lang/rust-clippy/pull/6483)
 +* Clean up `README.md` by removing outdated paragraph
 +  [#6488](https://github.com/rust-lang/rust-clippy/pull/6488)
 +* [`await_holding_refcell_ref`] and [`await_holding_lock`]
 +  [#6585](https://github.com/rust-lang/rust-clippy/pull/6585)
 +* [`as_conversions`] [#6608](https://github.com/rust-lang/rust-clippy/pull/6608)
 +
 +### Others
 +
 +* Clippy now has a [Roadmap] for 2021. If you like to get involved in a bigger
 +  project, take a look at the [Roadmap project page]. All issues listed there
 +  are actively mentored
 +  [#6462](https://github.com/rust-lang/rust-clippy/pull/6462)
 +* The Clippy version number now corresponds to the Rust version number
 +  [#6526](https://github.com/rust-lang/rust-clippy/pull/6526)
 +* Fix oversight which caused Clippy to lint deps in some environments, where
 +  `CLIPPY_TESTS=true` was set somewhere
 +  [#6575](https://github.com/rust-lang/rust-clippy/pull/6575)
 +* Add `cargo dev-lintcheck` tool to the Clippy Dev Tool
 +  [#6469](https://github.com/rust-lang/rust-clippy/pull/6469)
 +
 +[Roadmap]: https://github.com/rust-lang/rust-clippy/blob/master/doc/roadmap-2021.md
 +[Roadmap project page]: https://github.com/rust-lang/rust-clippy/projects/3
 +
 +## Rust 1.50
 +
 +Released 2021-02-11
 +
 +[b20d4c1...4bd77a1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4bd77a1)
 +
 +### New Lints
 +
 +* [`suspicious_operation_groupings`] [#6086](https://github.com/rust-lang/rust-clippy/pull/6086)
 +* [`size_of_in_element_count`] [#6394](https://github.com/rust-lang/rust-clippy/pull/6394)
 +* [`unnecessary_wraps`] [#6070](https://github.com/rust-lang/rust-clippy/pull/6070)
 +* [`let_underscore_drop`] [#6305](https://github.com/rust-lang/rust-clippy/pull/6305)
 +* [`collapsible_match`] [#6402](https://github.com/rust-lang/rust-clippy/pull/6402)
 +* [`redundant_else`] [#6330](https://github.com/rust-lang/rust-clippy/pull/6330)
 +* [`zero_sized_map_values`] [#6218](https://github.com/rust-lang/rust-clippy/pull/6218)
 +* [`print_stderr`] [#6367](https://github.com/rust-lang/rust-clippy/pull/6367)
 +* [`string_from_utf8_as_bytes`] [#6134](https://github.com/rust-lang/rust-clippy/pull/6134)
 +
 +### Moves and Deprecations
 +
 +* Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated
 +  as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333)
 +* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panics`
 +  [#6351](https://github.com/rust-lang/rust-clippy/pull/6351)
 +* Move [`map_err_ignore`] to `restriction`
 +  [#6416](https://github.com/rust-lang/rust-clippy/pull/6416)
 +* Move [`await_holding_refcell_ref`] to `pedantic`
 +  [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
 +* Move [`await_holding_lock`] to `pedantic`
 +  [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
 +
 +### Enhancements
 +
 +* Add the `unreadable-literal-lint-fractions` configuration to disable
 +  the `unreadable_literal` lint for fractions
 +  [#6421](https://github.com/rust-lang/rust-clippy/pull/6421)
 +* [`clone_on_copy`]: Now shows the type in the lint message
 +  [#6443](https://github.com/rust-lang/rust-clippy/pull/6443)
 +* [`redundant_pattern_matching`]: Now also lints on `std::task::Poll`
 +  [#6339](https://github.com/rust-lang/rust-clippy/pull/6339)
 +* [`redundant_pattern_matching`]: Additionally also lints on `std::net::IpAddr`
 +  [#6377](https://github.com/rust-lang/rust-clippy/pull/6377)
 +* [`search_is_some`]: Now suggests `contains` instead of `find(foo).is_some()`
 +  [#6119](https://github.com/rust-lang/rust-clippy/pull/6119)
 +* [`clone_double_ref`]: Now prints the reference type in the lint message
 +  [#6442](https://github.com/rust-lang/rust-clippy/pull/6442)
 +* [`modulo_one`]: Now also lints on -1.
 +  [#6360](https://github.com/rust-lang/rust-clippy/pull/6360)
 +* [`empty_loop`]: Now lints no_std crates, too
 +  [#6205](https://github.com/rust-lang/rust-clippy/pull/6205)
 +* [`or_fun_call`]: Now also lints when indexing `HashMap` or `BTreeMap`
 +  [#6267](https://github.com/rust-lang/rust-clippy/pull/6267)
 +* [`wrong_self_convention`]: Now also lints in trait definitions
 +  [#6316](https://github.com/rust-lang/rust-clippy/pull/6316)
 +* [`needless_borrow`]: Print the type in the lint message
 +  [#6449](https://github.com/rust-lang/rust-clippy/pull/6449)
 +
 +[msrv_readme]: https://github.com/rust-lang/rust-clippy#specifying-the-minimum-supported-rust-version
 +
 +### False Positive Fixes
 +
 +* [`manual_range_contains`]: No longer lints in `const fn`
 +  [#6382](https://github.com/rust-lang/rust-clippy/pull/6382)
 +* [`unnecessary_lazy_evaluations`]: No longer lints if closure argument is used
 +  [#6370](https://github.com/rust-lang/rust-clippy/pull/6370)
 +* [`match_single_binding`]: Now ignores cases with `#[cfg()]` macros
 +  [#6435](https://github.com/rust-lang/rust-clippy/pull/6435)
 +* [`match_like_matches_macro`]: No longer lints on arms with attributes
 +  [#6290](https://github.com/rust-lang/rust-clippy/pull/6290)
 +* [`map_clone`]: No longer lints with deref and clone
 +  [#6269](https://github.com/rust-lang/rust-clippy/pull/6269)
 +* [`map_clone`]: No longer lints in the case of &mut
 +  [#6301](https://github.com/rust-lang/rust-clippy/pull/6301)
 +* [`needless_update`]: Now ignores `non_exhaustive` structs
 +  [#6464](https://github.com/rust-lang/rust-clippy/pull/6464)
 +* [`needless_collect`]: No longer lints when a collect is needed multiple times
 +  [#6313](https://github.com/rust-lang/rust-clippy/pull/6313)
 +* [`unnecessary_cast`] No longer lints cfg-dependent types
 +  [#6369](https://github.com/rust-lang/rust-clippy/pull/6369)
 +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]:
 +  Both now ignore enums with frozen variants
 +  [#6110](https://github.com/rust-lang/rust-clippy/pull/6110)
 +* [`field_reassign_with_default`] No longer lint for private fields
 +  [#6537](https://github.com/rust-lang/rust-clippy/pull/6537)
 +
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`vec_box`]: Provide correct type scope suggestion
 +  [#6271](https://github.com/rust-lang/rust-clippy/pull/6271)
 +* [`manual_range_contains`]: Give correct suggestion when using floats
 +  [#6320](https://github.com/rust-lang/rust-clippy/pull/6320)
 +* [`unnecessary_lazy_evaluations`]: Don't always mark suggestion as MachineApplicable
 +  [#6272](https://github.com/rust-lang/rust-clippy/pull/6272)
 +* [`manual_async_fn`]: Improve suggestion formatting
 +  [#6294](https://github.com/rust-lang/rust-clippy/pull/6294)
 +* [`unnecessary_cast`]: Fix incorrectly formatted float literal suggestion
 +  [#6362](https://github.com/rust-lang/rust-clippy/pull/6362)
 +
 +### ICE Fixes
 +
 +* Fix a crash in [`from_iter_instead_of_collect`]
 +  [#6304](https://github.com/rust-lang/rust-clippy/pull/6304)
 +* Fix a silent crash when parsing doc comments in [`needless_doctest_main`]
 +  [#6458](https://github.com/rust-lang/rust-clippy/pull/6458)
 +
 +### Documentation Improvements
 +
 +* The lint website search has been improved ([#6477](https://github.com/rust-lang/rust-clippy/pull/6477)):
 +  * Searching for lints with dashes and spaces is possible now. For example
 +    `missing-errors-doc` and `missing errors doc` are now valid aliases for lint names
 +  * Improved fuzzy search in lint descriptions
 +* Various README improvements
 +  [#6287](https://github.com/rust-lang/rust-clippy/pull/6287)
 +* Add known problems to [`comparison_chain`] documentation
 +  [#6390](https://github.com/rust-lang/rust-clippy/pull/6390)
 +* Fix example used in [`cargo_common_metadata`]
 +  [#6293](https://github.com/rust-lang/rust-clippy/pull/6293)
 +* Improve [`map_clone`] documentation
 +  [#6340](https://github.com/rust-lang/rust-clippy/pull/6340)
 +
 +### Others
 +
 +* You can now tell Clippy about the MSRV your project supports. Please refer to
 +  the specific README section to learn more about MSRV support [here][msrv_readme]
 +  [#6201](https://github.com/rust-lang/rust-clippy/pull/6201)
 +* Add `--no-deps` option to avoid running on path dependencies in workspaces
 +  [#6188](https://github.com/rust-lang/rust-clippy/pull/6188)
 +
 +## Rust 1.49
 +
 +Released 2020-12-31
 +
 +[e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1)
 +
 +### New Lints
 +
 +* [`field_reassign_with_default`] [#5911](https://github.com/rust-lang/rust-clippy/pull/5911)
 +* [`await_holding_refcell_ref`] [#6029](https://github.com/rust-lang/rust-clippy/pull/6029)
 +* [`disallowed_methods`] [#6081](https://github.com/rust-lang/rust-clippy/pull/6081)
 +* [`inline_asm_x86_att_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 +* [`inline_asm_x86_intel_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 +* [`from_iter_instead_of_collect`] [#6101](https://github.com/rust-lang/rust-clippy/pull/6101)
 +* [`mut_mutex_lock`] [#6103](https://github.com/rust-lang/rust-clippy/pull/6103)
 +* [`single_element_loop`] [#6109](https://github.com/rust-lang/rust-clippy/pull/6109)
 +* [`manual_unwrap_or`] [#6123](https://github.com/rust-lang/rust-clippy/pull/6123)
 +* [`large_types_passed_by_value`] [#6135](https://github.com/rust-lang/rust-clippy/pull/6135)
 +* [`result_unit_err`] [#6157](https://github.com/rust-lang/rust-clippy/pull/6157)
 +* [`ref_option_ref`] [#6165](https://github.com/rust-lang/rust-clippy/pull/6165)
 +* [`manual_range_contains`] [#6177](https://github.com/rust-lang/rust-clippy/pull/6177)
 +* [`unusual_byte_groupings`] [#6183](https://github.com/rust-lang/rust-clippy/pull/6183)
 +* [`comparison_to_empty`] [#6226](https://github.com/rust-lang/rust-clippy/pull/6226)
 +* [`map_collect_result_unit`] [#6227](https://github.com/rust-lang/rust-clippy/pull/6227)
 +* [`manual_ok_or`] [#6233](https://github.com/rust-lang/rust-clippy/pull/6233)
 +
 +### Moves and Deprecations
 +
 +* Rename `single_char_push_str` to [`single_char_add_str`]
 +  [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 +* Rename `zero_width_space` to [`invisible_characters`]
 +  [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
 +* Deprecate `drop_bounds` (uplifted)
 +  [#6111](https://github.com/rust-lang/rust-clippy/pull/6111)
 +* Move [`string_lit_as_bytes`] to `nursery`
 +  [#6117](https://github.com/rust-lang/rust-clippy/pull/6117)
 +* Move [`rc_buffer`] to `restriction`
 +  [#6128](https://github.com/rust-lang/rust-clippy/pull/6128)
 +
 +### Enhancements
 +
 +* [`manual_memcpy`]: Also lint when there are loop counters (and produce a
 +  reliable suggestion)
 +  [#5727](https://github.com/rust-lang/rust-clippy/pull/5727)
 +* [`single_char_add_str`]: Also lint on `String::insert_str`
 +  [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 +* [`invisible_characters`]: Also lint the characters `\u{AD}` and `\u{2060}`
 +  [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
 +* [`eq_op`]: Also lint on the `assert_*!` macro family
 +  [#6167](https://github.com/rust-lang/rust-clippy/pull/6167)
 +* [`items_after_statements`]: Also lint in local macro expansions
 +  [#6176](https://github.com/rust-lang/rust-clippy/pull/6176)
 +* [`unnecessary_cast`]: Also lint casts on integer and float literals
 +  [#6187](https://github.com/rust-lang/rust-clippy/pull/6187)
 +* [`manual_unwrap_or`]: Also lint `Result::unwrap_or`
 +  [#6190](https://github.com/rust-lang/rust-clippy/pull/6190)
 +* [`match_like_matches_macro`]: Also lint when `match` has more than two arms
 +  [#6216](https://github.com/rust-lang/rust-clippy/pull/6216)
 +* [`integer_arithmetic`]: Better handle `/` an `%` operators
 +  [#6229](https://github.com/rust-lang/rust-clippy/pull/6229)
 +
 +### False Positive Fixes
 +
 +* [`needless_lifetimes`]: Bail out if the function has a `where` clause with the
 +  lifetime [#5978](https://github.com/rust-lang/rust-clippy/pull/5978)
 +* [`explicit_counter_loop`]: No longer lints, when loop counter is used after it
 +  is incremented [#6076](https://github.com/rust-lang/rust-clippy/pull/6076)
 +* [`or_fun_call`]: Revert changes addressing the handling of `const fn`
 +  [#6077](https://github.com/rust-lang/rust-clippy/pull/6077)
 +* [`needless_range_loop`]: No longer lints, when the iterable is used in the
 +  range [#6102](https://github.com/rust-lang/rust-clippy/pull/6102)
 +* [`inconsistent_digit_grouping`]: Fix bug when using floating point exponent
 +  [#6104](https://github.com/rust-lang/rust-clippy/pull/6104)
 +* [`mistyped_literal_suffixes`]: No longer lints on the fractional part of a
 +  float (e.g. `713.32_64`)
 +  [#6114](https://github.com/rust-lang/rust-clippy/pull/6114)
 +* [`invalid_regex`]: No longer lint on unicode characters within `bytes::Regex`
 +  [#6132](https://github.com/rust-lang/rust-clippy/pull/6132)
 +* [`boxed_local`]: No longer lints on `extern fn` arguments
 +  [#6133](https://github.com/rust-lang/rust-clippy/pull/6133)
 +* [`needless_lifetimes`]: Fix regression, where lifetime is used in `where`
 +  clause [#6198](https://github.com/rust-lang/rust-clippy/pull/6198)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`unnecessary_sort_by`]: Avoid dereferencing the suggested closure parameter
 +  [#6078](https://github.com/rust-lang/rust-clippy/pull/6078)
 +* [`needless_arbitrary_self_type`]: Correctly handle expanded code
 +  [#6093](https://github.com/rust-lang/rust-clippy/pull/6093)
 +* [`useless_format`]: Preserve raw strings in suggestion
 +  [#6151](https://github.com/rust-lang/rust-clippy/pull/6151)
 +* [`empty_loop`]: Suggest alternatives
 +  [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
 +* [`borrowed_box`]: Correctly add parentheses in suggestion
 +  [#6200](https://github.com/rust-lang/rust-clippy/pull/6200)
 +* [`unused_unit`]: Improve suggestion formatting
 +  [#6247](https://github.com/rust-lang/rust-clippy/pull/6247)
 +
 +### Documentation Improvements
 +
 +* Some doc improvements:
 +    * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090)
 +    * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
 +* [`doc_markdown`]: Document problematic link text style
 +  [#6107](https://github.com/rust-lang/rust-clippy/pull/6107)
 +
 +## Rust 1.48
 +
 +Released 2020-11-19
 +
 +[09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88)
 +
 +### New lints
 +
 +* [`self_assignment`] [#5894](https://github.com/rust-lang/rust-clippy/pull/5894)
 +* [`unnecessary_lazy_evaluations`] [#5720](https://github.com/rust-lang/rust-clippy/pull/5720)
 +* [`manual_strip`] [#6038](https://github.com/rust-lang/rust-clippy/pull/6038)
 +* [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998)
 +* [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044)
 +* `to_string_in_display` [#5831](https://github.com/rust-lang/rust-clippy/pull/5831)
 +* `single_char_push_str` [#5881](https://github.com/rust-lang/rust-clippy/pull/5881)
 +
 +### Moves and Deprecations
 +
 +* Downgrade [`verbose_bit_mask`] to pedantic
 +  [#6036](https://github.com/rust-lang/rust-clippy/pull/6036)
 +
 +### Enhancements
 +
 +* Extend [`precedence`] to handle chains of methods combined with unary negation
 +  [#5928](https://github.com/rust-lang/rust-clippy/pull/5928)
 +* [`useless_vec`]: add a configuration value for the maximum allowed size on the stack
 +  [#5907](https://github.com/rust-lang/rust-clippy/pull/5907)
 +* [`suspicious_arithmetic_impl`]: extend to implementations of `BitAnd`, `BitOr`, `BitXor`, `Rem`, `Shl`, and `Shr`
 +  [#5884](https://github.com/rust-lang/rust-clippy/pull/5884)
 +* `invalid_atomic_ordering`: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update`
 +  [#6025](https://github.com/rust-lang/rust-clippy/pull/6025)
 +* Avoid [`redundant_pattern_matching`] triggering in macros
 +  [#6069](https://github.com/rust-lang/rust-clippy/pull/6069)
 +* [`option_if_let_else`]: distinguish pure from impure `else` expressions
 +  [#5937](https://github.com/rust-lang/rust-clippy/pull/5937)
 +* [`needless_doctest_main`]: parse doctests instead of using textual search
 +  [#5912](https://github.com/rust-lang/rust-clippy/pull/5912)
 +* [`wildcard_imports`]: allow `prelude` to appear in any segment of an import
 +  [#5929](https://github.com/rust-lang/rust-clippy/pull/5929)
 +* Re-enable [`len_zero`] for ranges now that `range_is_empty` is stable
 +  [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
 +* [`option_as_ref_deref`]: catch fully-qualified calls to `Deref::deref` and `DerefMut::deref_mut`
 +  [#5933](https://github.com/rust-lang/rust-clippy/pull/5933)
 +
 +### False Positive Fixes
 +
 +* [`useless_attribute`]: permit allowing [`wildcard_imports`] and [`enum_glob_use`]
 +  [#5994](https://github.com/rust-lang/rust-clippy/pull/5994)
 +* [`transmute_ptr_to_ptr`]: avoid suggesting dereferencing raw pointers in const contexts
 +  [#5999](https://github.com/rust-lang/rust-clippy/pull/5999)
 +* [`redundant_closure_call`]: take into account usages of the closure in nested functions and closures
 +  [#5920](https://github.com/rust-lang/rust-clippy/pull/5920)
 +* Fix false positive in [`borrow_interior_mutable_const`] when referencing a field behind a pointer
 +  [#5949](https://github.com/rust-lang/rust-clippy/pull/5949)
 +* [`doc_markdown`]: allow using "GraphQL" without backticks
 +  [#5996](https://github.com/rust-lang/rust-clippy/pull/5996)
 +* `to_string_in_display`: avoid linting when calling `to_string()` on anything that is not `self`
 +  [#5971](https://github.com/rust-lang/rust-clippy/pull/5971)
 +* [`indexing_slicing`] and [`out_of_bounds_indexing`] treat references to arrays as arrays
 +  [#6034](https://github.com/rust-lang/rust-clippy/pull/6034)
 +* [`should_implement_trait`]: ignore methods with lifetime parameters
 +  [#5725](https://github.com/rust-lang/rust-clippy/pull/5725)
 +* [`needless_return`]: avoid linting if a temporary borrows a local variable
 +  [#5903](https://github.com/rust-lang/rust-clippy/pull/5903)
 +* Restrict [`unnecessary_sort_by`] to non-reference, Copy types
 +  [#6006](https://github.com/rust-lang/rust-clippy/pull/6006)
 +* Avoid suggesting `from_bits`/`to_bits` in const contexts in [`transmute_int_to_float`]
 +  [#5919](https://github.com/rust-lang/rust-clippy/pull/5919)
 +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]: improve detection of interior mutable types
 +  [#6046](https://github.com/rust-lang/rust-clippy/pull/6046)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`let_and_return`]: add a cast to the suggestion when the return expression has adjustments
 +  [#5946](https://github.com/rust-lang/rust-clippy/pull/5946)
 +* [`useless_conversion`]: show the type in the error message
 +  [#6035](https://github.com/rust-lang/rust-clippy/pull/6035)
 +* [`unnecessary_mut_passed`]: discriminate between functions and methods in the error message
 +  [#5892](https://github.com/rust-lang/rust-clippy/pull/5892)
 +* [`float_cmp`] and [`float_cmp_const`]: change wording to make margin of error less ambiguous
 +  [#6043](https://github.com/rust-lang/rust-clippy/pull/6043)
 +* [`default_trait_access`]: do not use unnecessary type parameters in the suggestion
 +  [#5993](https://github.com/rust-lang/rust-clippy/pull/5993)
 +* [`collapsible_if`]: don't use expanded code in the suggestion
 +  [#5992](https://github.com/rust-lang/rust-clippy/pull/5992)
 +* Do not suggest empty format strings in [`print_with_newline`] and [`write_with_newline`]
 +  [#6042](https://github.com/rust-lang/rust-clippy/pull/6042)
 +* [`unit_arg`]: improve the readability of the suggestion
 +  [#5931](https://github.com/rust-lang/rust-clippy/pull/5931)
 +* [`stable_sort_primitive`]: print the type that is being sorted in the lint message
 +  [#5935](https://github.com/rust-lang/rust-clippy/pull/5935)
 +* Show line count and max lines in [`too_many_lines`] lint message
 +  [#6009](https://github.com/rust-lang/rust-clippy/pull/6009)
 +* Keep parentheses in the suggestion of [`useless_conversion`] where applicable
 +  [#5900](https://github.com/rust-lang/rust-clippy/pull/5900)
 +* [`option_map_unit_fn`] and [`result_map_unit_fn`]: print the unit type `()` explicitly
 +  [#6024](https://github.com/rust-lang/rust-clippy/pull/6024)
 +* [`redundant_allocation`]: suggest replacing `Rc<Box<T>>` with `Rc<T>`
 +  [#5899](https://github.com/rust-lang/rust-clippy/pull/5899)
 +* Make lint messages adhere to rustc dev guide conventions
 +  [#5893](https://github.com/rust-lang/rust-clippy/pull/5893)
 +
 +### ICE Fixes
 +
 +* Fix ICE in [`repeat_once`]
 +  [#5948](https://github.com/rust-lang/rust-clippy/pull/5948)
 +
 +### Documentation Improvements
 +
 +* [`mutable_key_type`]: explain potential for false positives when the interior mutable type is not accessed in the `Hash` implementation
 +  [#6019](https://github.com/rust-lang/rust-clippy/pull/6019)
 +* [`unnecessary_mut_passed`]: fix typo
 +  [#5913](https://github.com/rust-lang/rust-clippy/pull/5913)
 +* Add example of false positive to [`ptr_arg`] docs.
 +  [#5885](https://github.com/rust-lang/rust-clippy/pull/5885)
 +* [`box_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection), [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
 +  [#6023](https://github.com/rust-lang/rust-clippy/pull/6023)
 +
 +## Rust 1.47
 +
 +Released 2020-10-08
 +
 +[c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400)
 +
 +### New lints
 +
 +* [`derive_ord_xor_partial_ord`] [#5848](https://github.com/rust-lang/rust-clippy/pull/5848)
 +* [`trait_duplication_in_bounds`] [#5852](https://github.com/rust-lang/rust-clippy/pull/5852)
 +* [`map_identity`] [#5694](https://github.com/rust-lang/rust-clippy/pull/5694)
 +* [`unit_return_expecting_ord`] [#5737](https://github.com/rust-lang/rust-clippy/pull/5737)
 +* [`pattern_type_mismatch`] [#4841](https://github.com/rust-lang/rust-clippy/pull/4841)
 +* [`repeat_once`] [#5773](https://github.com/rust-lang/rust-clippy/pull/5773)
 +* [`same_item_push`] [#5825](https://github.com/rust-lang/rust-clippy/pull/5825)
 +* [`needless_arbitrary_self_type`] [#5869](https://github.com/rust-lang/rust-clippy/pull/5869)
 +* [`match_like_matches_macro`] [#5769](https://github.com/rust-lang/rust-clippy/pull/5769)
 +* [`stable_sort_primitive`] [#5809](https://github.com/rust-lang/rust-clippy/pull/5809)
 +* [`blanket_clippy_restriction_lints`] [#5750](https://github.com/rust-lang/rust-clippy/pull/5750)
 +* [`option_if_let_else`] [#5301](https://github.com/rust-lang/rust-clippy/pull/5301)
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`regex_macro`] lint
 +  [#5760](https://github.com/rust-lang/rust-clippy/pull/5760)
 +* Move [`range_minus_one`] to `pedantic`
 +  [#5752](https://github.com/rust-lang/rust-clippy/pull/5752)
 +
 +### Enhancements
 +
 +* Improve [`needless_collect`] by catching `collect` calls followed by `iter` or `into_iter` calls
 +  [#5837](https://github.com/rust-lang/rust-clippy/pull/5837)
 +* [`panic`], [`todo`], [`unimplemented`] and [`unreachable`] now detect calls with formatting
 +  [#5811](https://github.com/rust-lang/rust-clippy/pull/5811)
 +* Detect more cases of [`suboptimal_flops`] and [`imprecise_flops`]
 +  [#5443](https://github.com/rust-lang/rust-clippy/pull/5443)
 +* Handle asymmetrical implementations of `PartialEq` in [`cmp_owned`]
 +  [#5701](https://github.com/rust-lang/rust-clippy/pull/5701)
 +* Make it possible to allow [`unsafe_derive_deserialize`]
 +  [#5870](https://github.com/rust-lang/rust-clippy/pull/5870)
 +* Catch `ord.min(a).max(b)` where a < b in [`min_max`]
 +  [#5871](https://github.com/rust-lang/rust-clippy/pull/5871)
 +* Make [`clone_on_copy`] suggestion machine applicable
 +  [#5745](https://github.com/rust-lang/rust-clippy/pull/5745)
 +* Enable [`len_zero`] on ranges now that `is_empty` is stable on them
 +  [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
 +
 +### False Positive Fixes
 +
 +* Avoid triggering [`or_fun_call`] with const fns that take no arguments
 +  [#5889](https://github.com/rust-lang/rust-clippy/pull/5889)
 +* Fix [`redundant_closure_call`] false positive for closures that have multiple calls
 +  [#5800](https://github.com/rust-lang/rust-clippy/pull/5800)
 +* Don't lint cases involving `ManuallyDrop` in [`redundant_clone`]
 +  [#5824](https://github.com/rust-lang/rust-clippy/pull/5824)
 +* Treat a single expression the same as a single statement in the 2nd arm of a match in [`single_match_else`]
 +  [#5771](https://github.com/rust-lang/rust-clippy/pull/5771)
 +* Don't trigger [`unnested_or_patterns`] if the feature `or_patterns` is not enabled
 +  [#5758](https://github.com/rust-lang/rust-clippy/pull/5758)
 +* Avoid linting if key borrows in [`unnecessary_sort_by`]
 +  [#5756](https://github.com/rust-lang/rust-clippy/pull/5756)
 +* Consider `Try` impl for `Poll` when generating suggestions in [`try_err`]
 +  [#5857](https://github.com/rust-lang/rust-clippy/pull/5857)
 +* Take input lifetimes into account in `manual_async_fn`
 +  [#5859](https://github.com/rust-lang/rust-clippy/pull/5859)
 +* Fix multiple false positives in [`type_repetition_in_bounds`] and add a configuration option
 +  [#5761](https://github.com/rust-lang/rust-clippy/pull/5761)
 +* Limit the [`suspicious_arithmetic_impl`] lint to one binary operation
 +  [#5820](https://github.com/rust-lang/rust-clippy/pull/5820)
 +
 +### Suggestion Fixes/Improvements
 +
 +* Improve readability of [`shadow_unrelated`] suggestion by truncating the RHS snippet
 +  [#5788](https://github.com/rust-lang/rust-clippy/pull/5788)
 +* Suggest `filter_map` instead of `flat_map` when mapping to `Option` in [`map_flatten`]
 +  [#5846](https://github.com/rust-lang/rust-clippy/pull/5846)
 +* Ensure suggestion is shown correctly for long method call chains in [`iter_nth_zero`]
 +  [#5793](https://github.com/rust-lang/rust-clippy/pull/5793)
 +* Drop borrow operator in suggestions of [`redundant_pattern_matching`]
 +  [#5815](https://github.com/rust-lang/rust-clippy/pull/5815)
 +* Add suggestion for [`iter_skip_next`]
 +  [#5843](https://github.com/rust-lang/rust-clippy/pull/5843)
 +* Improve [`collapsible_if`] fix suggestion
 +  [#5732](https://github.com/rust-lang/rust-clippy/pull/5732)
 +
 +### ICE Fixes
 +
 +* Fix ICE caused by [`needless_collect`]
 +  [#5877](https://github.com/rust-lang/rust-clippy/pull/5877)
 +* Fix ICE caused by [`unnested_or_patterns`]
 +  [#5784](https://github.com/rust-lang/rust-clippy/pull/5784)
 +
 +### Documentation Improvements
 +
 +* Fix grammar of [`await_holding_lock`] documentation
 +  [#5748](https://github.com/rust-lang/rust-clippy/pull/5748)
 +
 +### Others
 +
 +* Make lints adhere to the rustc dev guide
 +  [#5888](https://github.com/rust-lang/rust-clippy/pull/5888)
 +
 +## Rust 1.46
 +
 +Released 2020-08-27
 +
 +[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa)
 +
 +### New lints
 +
 +* [`unnested_or_patterns`] [#5378](https://github.com/rust-lang/rust-clippy/pull/5378)
 +* [`iter_next_slice`] [#5597](https://github.com/rust-lang/rust-clippy/pull/5597)
 +* [`unnecessary_sort_by`] [#5623](https://github.com/rust-lang/rust-clippy/pull/5623)
 +* [`vec_resize_to_zero`] [#5637](https://github.com/rust-lang/rust-clippy/pull/5637)
 +
 +### Moves and Deprecations
 +
 +* Move [`cast_ptr_alignment`] to pedantic [#5667](https://github.com/rust-lang/rust-clippy/pull/5667)
 +
 +### Enhancements
 +
 +* Improve [`mem_replace_with_uninit`] lint [#5695](https://github.com/rust-lang/rust-clippy/pull/5695)
 +
 +### False Positive Fixes
 +
 +* [`len_zero`]: Avoid linting ranges when the `range_is_empty` feature is not enabled
 +  [#5656](https://github.com/rust-lang/rust-clippy/pull/5656)
 +* [`let_and_return`]: Don't lint if a temporary borrow is involved
 +  [#5680](https://github.com/rust-lang/rust-clippy/pull/5680)
 +* [`reversed_empty_ranges`]: Avoid linting `N..N` in for loop arguments in
 +  [#5692](https://github.com/rust-lang/rust-clippy/pull/5692)
 +* [`if_same_then_else`]: Don't assume multiplication is always commutative
 +  [#5702](https://github.com/rust-lang/rust-clippy/pull/5702)
 +* [`blacklisted_name`]: Remove `bar` from the default configuration
 +  [#5712](https://github.com/rust-lang/rust-clippy/pull/5712)
 +* [`redundant_pattern_matching`]: Avoid suggesting non-`const fn` calls in const contexts
 +  [#5724](https://github.com/rust-lang/rust-clippy/pull/5724)
 +
 +### Suggestion Fixes/Improvements
 +
 +* Fix suggestion of [`unit_arg`] lint, so that it suggest semantic equivalent code
 +  [#4455](https://github.com/rust-lang/rust-clippy/pull/4455)
 +* Add auto applicable suggestion to [`macro_use_imports`]
 +  [#5279](https://github.com/rust-lang/rust-clippy/pull/5279)
 +
 +### ICE Fixes
 +
 +* Fix ICE in the `consts` module of Clippy [#5709](https://github.com/rust-lang/rust-clippy/pull/5709)
 +
 +### Documentation Improvements
 +
 +* Improve code examples across multiple lints [#5664](https://github.com/rust-lang/rust-clippy/pull/5664)
 +
 +### Others
 +
 +* Introduce a `--rustc` flag to `clippy-driver`, which turns `clippy-driver`
 +  into `rustc` and passes all the given arguments to `rustc`. This is especially
 +  useful for tools that need the `rustc` version Clippy was compiled with,
 +  instead of the Clippy version. E.g. `clippy-driver --rustc --version` will
 +  print the output of `rustc --version`.
 +  [#5178](https://github.com/rust-lang/rust-clippy/pull/5178)
 +* New issue templates now make it easier to complain if Clippy is too annoying
 +  or not annoying enough! [#5735](https://github.com/rust-lang/rust-clippy/pull/5735)
 +
 +## Rust 1.45
 +
 +Released 2020-07-16
 +
 +[891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1)
 +
 +### New lints
 +
 +* [`match_wildcard_for_single_variants`] [#5582](https://github.com/rust-lang/rust-clippy/pull/5582)
 +* [`unsafe_derive_deserialize`] [#5493](https://github.com/rust-lang/rust-clippy/pull/5493)
 +* [`if_let_mutex`] [#5332](https://github.com/rust-lang/rust-clippy/pull/5332)
 +* [`mismatched_target_os`] [#5506](https://github.com/rust-lang/rust-clippy/pull/5506)
 +* [`await_holding_lock`] [#5439](https://github.com/rust-lang/rust-clippy/pull/5439)
 +* [`match_on_vec_items`] [#5522](https://github.com/rust-lang/rust-clippy/pull/5522)
 +* [`manual_async_fn`] [#5576](https://github.com/rust-lang/rust-clippy/pull/5576)
 +* [`reversed_empty_ranges`] [#5583](https://github.com/rust-lang/rust-clippy/pull/5583)
 +* [`manual_non_exhaustive`] [#5550](https://github.com/rust-lang/rust-clippy/pull/5550)
 +
 +### Moves and Deprecations
 +
 +* Downgrade [`match_bool`] to pedantic [#5408](https://github.com/rust-lang/rust-clippy/pull/5408)
 +* Downgrade [`match_wild_err_arm`] to pedantic and update help messages. [#5622](https://github.com/rust-lang/rust-clippy/pull/5622)
 +* Downgrade [`useless_let_if_seq`] to nursery. [#5599](https://github.com/rust-lang/rust-clippy/pull/5599)
 +* Generalize `option_and_then_some` and rename to [`bind_instead_of_map`]. [#5529](https://github.com/rust-lang/rust-clippy/pull/5529)
 +* Rename `identity_conversion` to [`useless_conversion`]. [#5568](https://github.com/rust-lang/rust-clippy/pull/5568)
 +* Merge `block_in_if_condition_expr` and `block_in_if_condition_stmt` into [`blocks_in_if_conditions`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_map_unwrap_or`, `option_map_unwrap_or_else` and `result_map_unwrap_or_else` into [`map_unwrap_or`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_unwrap_used` and `result_unwrap_used` into [`unwrap_used`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_expect_used` and `result_expect_used` into [`expect_used`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `for_loop_over_option` and `for_loop_over_result` into [`for_loops_over_fallibles`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +
 +### Enhancements
 +
 +* Avoid running cargo lints when not enabled to improve performance. [#5505](https://github.com/rust-lang/rust-clippy/pull/5505)
 +* Extend [`useless_conversion`] with `TryFrom` and `TryInto`. [#5631](https://github.com/rust-lang/rust-clippy/pull/5631)
 +* Lint also in type parameters and where clauses in [`unused_unit`]. [#5592](https://github.com/rust-lang/rust-clippy/pull/5592)
 +* Do not suggest deriving `Default` in [`new_without_default`]. [#5616](https://github.com/rust-lang/rust-clippy/pull/5616)
 +
 +### False Positive Fixes
 +
 +* [`while_let_on_iterator`] [#5525](https://github.com/rust-lang/rust-clippy/pull/5525)
 +* [`empty_line_after_outer_attr`] [#5609](https://github.com/rust-lang/rust-clippy/pull/5609)
 +* [`unnecessary_unwrap`] [#5558](https://github.com/rust-lang/rust-clippy/pull/5558)
 +* [`comparison_chain`] [#5596](https://github.com/rust-lang/rust-clippy/pull/5596)
 +* Don't trigger [`used_underscore_binding`] in await desugaring. [#5535](https://github.com/rust-lang/rust-clippy/pull/5535)
 +* Don't trigger [`borrowed_box`] on mutable references. [#5491](https://github.com/rust-lang/rust-clippy/pull/5491)
 +* Allow `1 << 0` in [`identity_op`]. [#5602](https://github.com/rust-lang/rust-clippy/pull/5602)
 +* Allow `use super::*;` glob imports in [`wildcard_imports`]. [#5564](https://github.com/rust-lang/rust-clippy/pull/5564)
 +* Whitelist more words in [`doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611)
 +* Skip dev and build deps in [`multiple_crate_versions`]. [#5636](https://github.com/rust-lang/rust-clippy/pull/5636)
 +* Honor `allow` attribute on arguments in [`ptr_arg`]. [#5647](https://github.com/rust-lang/rust-clippy/pull/5647)
 +* Honor lint level attributes for [`redundant_field_names`], [`just_underscores_and_digits`], [`many_single_char_names`]
 +and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/5651)
 +* Ignore calls to `len` in [`or_fun_call`]. [#4429](https://github.com/rust-lang/rust-clippy/pull/4429)
 +
 +### Suggestion Improvements
 +
 +* Simplify suggestions in [`manual_memcpy`]. [#5536](https://github.com/rust-lang/rust-clippy/pull/5536)
 +* Fix suggestion in [`redundant_pattern_matching`] for macros. [#5511](https://github.com/rust-lang/rust-clippy/pull/5511)
 +* Avoid suggesting `copied()` for mutable references in [`map_clone`]. [#5530](https://github.com/rust-lang/rust-clippy/pull/5530)
 +* Improve help message for [`clone_double_ref`]. [#5547](https://github.com/rust-lang/rust-clippy/pull/5547)
 +
 +### ICE Fixes
 +
 +* Fix ICE caused in unwrap module. [#5590](https://github.com/rust-lang/rust-clippy/pull/5590)
 +* Fix ICE on rustc test issue-69020-assoc-const-arith-overflow.rs [#5499](https://github.com/rust-lang/rust-clippy/pull/5499)
 +
 +### Documentation
 +
 +* Clarify the documentation of [`unnecessary_mut_passed`]. [#5639](https://github.com/rust-lang/rust-clippy/pull/5639)
 +* Extend example for [`unneeded_field_pattern`]. [#5541](https://github.com/rust-lang/rust-clippy/pull/5541)
 +
 +## Rust 1.44
 +
 +Released 2020-06-04
 +
 +[204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8)
 +
 +### New lints
 +
 +* [`explicit_deref_methods`] [#5226](https://github.com/rust-lang/rust-clippy/pull/5226)
 +* [`implicit_saturating_sub`] [#5427](https://github.com/rust-lang/rust-clippy/pull/5427)
 +* [`macro_use_imports`] [#5230](https://github.com/rust-lang/rust-clippy/pull/5230)
 +* [`verbose_file_reads`] [#5272](https://github.com/rust-lang/rust-clippy/pull/5272)
 +* [`future_not_send`] [#5423](https://github.com/rust-lang/rust-clippy/pull/5423)
 +* [`redundant_pub_crate`] [#5319](https://github.com/rust-lang/rust-clippy/pull/5319)
 +* [`large_const_arrays`] [#5248](https://github.com/rust-lang/rust-clippy/pull/5248)
 +* [`result_map_or_into_option`] [#5415](https://github.com/rust-lang/rust-clippy/pull/5415)
 +* [`redundant_allocation`] [#5349](https://github.com/rust-lang/rust-clippy/pull/5349)
 +* [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
 +* [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
 +
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380)
 +* Move [`cognitive_complexity`] to nursery [#5428](https://github.com/rust-lang/rust-clippy/pull/5428)
 +* Move [`useless_transmute`] to nursery [#5364](https://github.com/rust-lang/rust-clippy/pull/5364)
 +* Downgrade [`inefficient_to_string`] to pedantic [#5412](https://github.com/rust-lang/rust-clippy/pull/5412)
 +* Downgrade [`option_option`] to pedantic [#5401](https://github.com/rust-lang/rust-clippy/pull/5401)
 +* Downgrade [`unreadable_literal`] to pedantic [#5419](https://github.com/rust-lang/rust-clippy/pull/5419)
 +* Downgrade [`let_unit_value`] to pedantic [#5409](https://github.com/rust-lang/rust-clippy/pull/5409)
 +* Downgrade [`trivially_copy_pass_by_ref`] to pedantic [#5410](https://github.com/rust-lang/rust-clippy/pull/5410)
 +* Downgrade [`implicit_hasher`] to pedantic [#5411](https://github.com/rust-lang/rust-clippy/pull/5411)
 +
 +### Enhancements
 +
 +* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to
 +  auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363)
 +* Make [`redundant_clone`] also trigger on cases where the cloned value is not
 +  consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304)
 +* Expand [`integer_arithmetic`] to also disallow bit-shifting [#5430](https://github.com/rust-lang/rust-clippy/pull/5430)
 +* [`option_as_ref_deref`] now detects more deref cases [#5425](https://github.com/rust-lang/rust-clippy/pull/5425)
 +* [`large_enum_variant`] now report the sizes of the largest and second-largest variants [#5466](https://github.com/rust-lang/rust-clippy/pull/5466)
 +* [`bool_comparison`] now also checks for inequality comparisons that can be
 +  written more concisely [#5365](https://github.com/rust-lang/rust-clippy/pull/5365)
 +* Expand [`clone_on_copy`] to work in method call arguments as well [#5441](https://github.com/rust-lang/rust-clippy/pull/5441)
 +* [`redundant_pattern_matching`] now also handles `while let` [#5483](https://github.com/rust-lang/rust-clippy/pull/5483)
 +* [`integer_arithmetic`] now also lints references of integers [#5329](https://github.com/rust-lang/rust-clippy/pull/5329)
 +* Expand [`float_cmp_const`] to also work on arrays [#5345](https://github.com/rust-lang/rust-clippy/pull/5345)
 +* Trigger [`map_flatten`] when map is called on an `Option` [#5473](https://github.com/rust-lang/rust-clippy/pull/5473)
 +
 +### False Positive Fixes
 +
 +* [`many_single_char_names`] [#5468](https://github.com/rust-lang/rust-clippy/pull/5468)
 +* [`should_implement_trait`] [#5437](https://github.com/rust-lang/rust-clippy/pull/5437)
 +* [`unused_self`] [#5387](https://github.com/rust-lang/rust-clippy/pull/5387)
 +* [`redundant_clone`] [#5453](https://github.com/rust-lang/rust-clippy/pull/5453)
 +* [`precedence`] [#5445](https://github.com/rust-lang/rust-clippy/pull/5445)
 +* [`suspicious_op_assign_impl`] [#5424](https://github.com/rust-lang/rust-clippy/pull/5424)
 +* [`needless_lifetimes`] [#5293](https://github.com/rust-lang/rust-clippy/pull/5293)
 +* [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287)
 +* [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451)
 +
 +
 +### Suggestion Improvements
 +
 +* Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481)
 +* Improve the suggested placeholder in [`option_map_unit_fn`] [#5292](https://github.com/rust-lang/rust-clippy/pull/5292)
 +* Improve suggestion for [`match_single_binding`] when triggered inside a closure [#5350](https://github.com/rust-lang/rust-clippy/pull/5350)
 +
 +### ICE Fixes
 +
 +* Handle the unstable `trivial_bounds` feature [#5296](https://github.com/rust-lang/rust-clippy/pull/5296)
 +* `shadow_*` lints [#5297](https://github.com/rust-lang/rust-clippy/pull/5297)
 +
 +### Documentation
 +
 +* Fix documentation generation for configurable lints [#5353](https://github.com/rust-lang/rust-clippy/pull/5353)
 +* Update documentation for [`new_ret_no_self`] [#5448](https://github.com/rust-lang/rust-clippy/pull/5448)
 +* The documentation for [`option_option`] now suggest using a tri-state enum [#5403](https://github.com/rust-lang/rust-clippy/pull/5403)
 +* Fix bit mask example in [`verbose_bit_mask`] documentation [#5454](https://github.com/rust-lang/rust-clippy/pull/5454)
 +* [`wildcard_imports`] documentation now mentions that `use ...::prelude::*` is
 +  not linted [#5312](https://github.com/rust-lang/rust-clippy/pull/5312)
 +
 +## Rust 1.43
 +
 +Released 2020-04-23
 +
 +[4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b)
 +
 +### New lints
 +
 +* [`imprecise_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
 +* [`suboptimal_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
 +* [`wildcard_imports`] [#5029](https://github.com/rust-lang/rust-clippy/pull/5029)
 +* [`single_component_path_imports`] [#5058](https://github.com/rust-lang/rust-clippy/pull/5058)
 +* [`match_single_binding`] [#5061](https://github.com/rust-lang/rust-clippy/pull/5061)
 +* [`let_underscore_lock`] [#5101](https://github.com/rust-lang/rust-clippy/pull/5101)
 +* [`struct_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
 +* [`fn_params_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
 +* [`option_env_unwrap`] [#5148](https://github.com/rust-lang/rust-clippy/pull/5148)
 +* [`lossy_float_literal`] [#5202](https://github.com/rust-lang/rust-clippy/pull/5202)
 +* [`rest_pat_in_fully_bound_structs`] [#5258](https://github.com/rust-lang/rust-clippy/pull/5258)
 +
 +### Moves and Deprecations
 +
 +* Move [`unneeded_field_pattern`] to pedantic group [#5200](https://github.com/rust-lang/rust-clippy/pull/5200)
 +
 +### Enhancements
 +
 +* Make [`missing_errors_doc`] lint also trigger on `async` functions
 +  [#5181](https://github.com/rust-lang/rust-clippy/pull/5181)
 +* Add more constants to [`approx_constant`] [#5193](https://github.com/rust-lang/rust-clippy/pull/5193)
 +* Extend [`question_mark`] lint [#5266](https://github.com/rust-lang/rust-clippy/pull/5266)
 +
 +### False Positive Fixes
 +
 +* [`use_debug`] [#5047](https://github.com/rust-lang/rust-clippy/pull/5047)
 +* [`unnecessary_unwrap`] [#5132](https://github.com/rust-lang/rust-clippy/pull/5132)
 +* [`zero_prefixed_literal`] [#5170](https://github.com/rust-lang/rust-clippy/pull/5170)
 +* [`missing_const_for_fn`] [#5216](https://github.com/rust-lang/rust-clippy/pull/5216)
 +
 +### Suggestion Improvements
 +
 +* Improve suggestion when blocks of code are suggested [#5134](https://github.com/rust-lang/rust-clippy/pull/5134)
 +
 +### ICE Fixes
 +
 +* `misc_early` lints [#5129](https://github.com/rust-lang/rust-clippy/pull/5129)
 +* [`missing_errors_doc`] [#5213](https://github.com/rust-lang/rust-clippy/pull/5213)
 +* Fix ICE when evaluating `usize`s [#5256](https://github.com/rust-lang/rust-clippy/pull/5256)
 +
 +### Documentation
 +
 +* Improve documentation of [`iter_nth_zero`]
 +* Add documentation pages for stable releases [#5171](https://github.com/rust-lang/rust-clippy/pull/5171)
 +
 +### Others
 +
 +* Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190)
 +
 +
 +## Rust 1.42
 +
 +Released 2020-03-12
 +
 +[69f99e7...4ee1206](https://github.com/rust-lang/rust-clippy/compare/69f99e7...4ee1206)
 +
 +### New lints
 +
 +* [`filetype_is_file`] [#4543](https://github.com/rust-lang/rust-clippy/pull/4543)
 +* [`let_underscore_must_use`] [#4823](https://github.com/rust-lang/rust-clippy/pull/4823)
 +* [`modulo_arithmetic`] [#4867](https://github.com/rust-lang/rust-clippy/pull/4867)
 +* [`mem_replace_with_default`] [#4881](https://github.com/rust-lang/rust-clippy/pull/4881)
 +* [`mutable_key_type`] [#4885](https://github.com/rust-lang/rust-clippy/pull/4885)
 +* [`option_as_ref_deref`] [#4945](https://github.com/rust-lang/rust-clippy/pull/4945)
 +* [`wildcard_in_or_patterns`] [#4960](https://github.com/rust-lang/rust-clippy/pull/4960)
 +* [`iter_nth_zero`] [#4966](https://github.com/rust-lang/rust-clippy/pull/4966)
 +* `invalid_atomic_ordering` [#4999](https://github.com/rust-lang/rust-clippy/pull/4999)
 +* [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067)
 +
 +### Moves and Deprecations
 +
 +* Move [`transmute_float_to_int`] from nursery to complexity group
 +  [#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
 +* Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
 +* Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
 +
 +### Enhancements
 +
 +* Lint vectored IO in [`unused_io_amount`] [#5027](https://github.com/rust-lang/rust-clippy/pull/5027)
 +* Make [`vec_box`] configurable by adding a size threshold [#5081](https://github.com/rust-lang/rust-clippy/pull/5081)
 +* Also lint constants in [`cmp_nan`] [#4910](https://github.com/rust-lang/rust-clippy/pull/4910)
 +* Fix false negative in [`expect_fun_call`] [#4915](https://github.com/rust-lang/rust-clippy/pull/4915)
 +* Fix false negative in [`redundant_clone`] [#5017](https://github.com/rust-lang/rust-clippy/pull/5017)
 +
 +### False Positive Fixes
 +
 +* [`map_clone`] [#4937](https://github.com/rust-lang/rust-clippy/pull/4937)
 +* [`replace_consts`] [#4977](https://github.com/rust-lang/rust-clippy/pull/4977)
 +* [`let_and_return`] [#5008](https://github.com/rust-lang/rust-clippy/pull/5008)
 +* [`eq_op`] [#5079](https://github.com/rust-lang/rust-clippy/pull/5079)
 +* [`possible_missing_comma`] [#5083](https://github.com/rust-lang/rust-clippy/pull/5083)
 +* [`debug_assert_with_mut_call`] [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Don't trigger [`let_underscore_must_use`] in external macros
 +  [#5082](https://github.com/rust-lang/rust-clippy/pull/5082)
 +* Don't trigger [`empty_loop`] in `no_std` crates [#5086](https://github.com/rust-lang/rust-clippy/pull/5086)
 +
 +### Suggestion Improvements
 +
 +* `option_map_unwrap_or` [#4634](https://github.com/rust-lang/rust-clippy/pull/4634)
 +* [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
 +* [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
 +* [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
 +* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
 +* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
 +* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
 +* `if_let_some_result` [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
 +
 +### ICE fixes
 +
 +* [`unsound_collection_transmute`] [#4975](https://github.com/rust-lang/rust-clippy/pull/4975)
 +
 +### Documentation
 +
 +* Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`]
 +
 +
 +## Rust 1.41
 +
 +Released 2020-01-30
 +
 +[c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7)
 +
 +* New Lints:
 +  * [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697)
 +  * [`to_digit_is_some`] [#4801](https://github.com/rust-lang/rust-clippy/pull/4801)
 +  * [`tabs_in_doc_comments`] [#4806](https://github.com/rust-lang/rust-clippy/pull/4806)
 +  * [`large_stack_arrays`] [#4807](https://github.com/rust-lang/rust-clippy/pull/4807)
 +  * [`same_functions_in_if_condition`] [#4814](https://github.com/rust-lang/rust-clippy/pull/4814)
 +  * [`zst_offset`] [#4816](https://github.com/rust-lang/rust-clippy/pull/4816)
 +  * [`as_conversions`] [#4821](https://github.com/rust-lang/rust-clippy/pull/4821)
 +  * [`missing_errors_doc`] [#4884](https://github.com/rust-lang/rust-clippy/pull/4884)
 +  * [`transmute_float_to_int`] [#4889](https://github.com/rust-lang/rust-clippy/pull/4889)
 +* Remove plugin interface, see
 +  [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
 +  details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
 +* Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
 +* Deprecate `into_iter_on_array` [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
 +* Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
 +  [#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
 +* Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
 +* Fix false positive in `while_immutable_condition` [#4730](https://github.com/rust-lang/rust-clippy/pull/4730)
 +* Fix false positive in `explicit_counter_loop` [#4803](https://github.com/rust-lang/rust-clippy/pull/4803)
 +* Fix false positive in `must_use_candidate` [#4794](https://github.com/rust-lang/rust-clippy/pull/4794)
 +* Fix false positive in `print_with_newline` and `write_with_newline`
 +  [#4769](https://github.com/rust-lang/rust-clippy/pull/4769)
 +* Fix false positive in `derive_hash_xor_eq` [#4766](https://github.com/rust-lang/rust-clippy/pull/4766)
 +* Fix false positive in `missing_inline_in_public_items` [#4870](https://github.com/rust-lang/rust-clippy/pull/4870)
 +* Fix false positive in `string_add` [#4880](https://github.com/rust-lang/rust-clippy/pull/4880)
 +* Fix false positive in `float_arithmetic` [#4851](https://github.com/rust-lang/rust-clippy/pull/4851)
 +* Fix false positive in `cast_sign_loss` [#4883](https://github.com/rust-lang/rust-clippy/pull/4883)
 +* Fix false positive in `manual_swap` [#4877](https://github.com/rust-lang/rust-clippy/pull/4877)
 +* Fix ICEs occurring while checking some block expressions [#4772](https://github.com/rust-lang/rust-clippy/pull/4772)
 +* Fix ICE in `use_self` [#4776](https://github.com/rust-lang/rust-clippy/pull/4776)
 +* Fix ICEs related to `const_generics` [#4780](https://github.com/rust-lang/rust-clippy/pull/4780)
 +* Display help when running `clippy-driver` without arguments, instead of ICEing
 +  [#4810](https://github.com/rust-lang/rust-clippy/pull/4810)
 +* Clippy has its own ICE message now [#4588](https://github.com/rust-lang/rust-clippy/pull/4588)
 +* Show deprecated lints in the documentation again [#4757](https://github.com/rust-lang/rust-clippy/pull/4757)
 +* Improve Documentation by adding positive examples to some lints
 +  [#4832](https://github.com/rust-lang/rust-clippy/pull/4832)
 +
 +## Rust 1.40
 +
 +Released 2019-12-19
 +
 +[4e7e71b...c8e3cfb](https://github.com/rust-lang/rust-clippy/compare/4e7e71b...c8e3cfb)
 +
 +* New Lints:
 +  * [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537)
 +  * [`needless_doctest_main`] [#4603](https://github.com/rust-lang/rust-clippy/pull/4603)
 +  * [`suspicious_unary_op_formatting`] [#4615](https://github.com/rust-lang/rust-clippy/pull/4615)
 +  * [`debug_assert_with_mut_call`] [#4680](https://github.com/rust-lang/rust-clippy/pull/4680)
 +  * [`unused_self`] [#4619](https://github.com/rust-lang/rust-clippy/pull/4619)
 +  * [`inefficient_to_string`] [#4683](https://github.com/rust-lang/rust-clippy/pull/4683)
 +  * [`must_use_unit`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`must_use_candidate`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`double_must_use`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`comparison_chain`] [#4569](https://github.com/rust-lang/rust-clippy/pull/4569)
 +  * [`unsound_collection_transmute`] [#4592](https://github.com/rust-lang/rust-clippy/pull/4592)
 +  * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `option_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `result_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +* Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736)
 +* Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613)
 +* Expand `integer_arithmetic` to also detect mutating arithmetic like `+=` [#4585](https://github.com/rust-lang/rust-clippy/pull/4585)
 +* Fix false positive in `nonminimal_bool` [#4568](https://github.com/rust-lang/rust-clippy/pull/4568)
 +* Fix false positive in `missing_safety_doc` [#4611](https://github.com/rust-lang/rust-clippy/pull/4611)
 +* Fix false positive in `cast_sign_loss` [#4614](https://github.com/rust-lang/rust-clippy/pull/4614)
 +* Fix false positive in `redundant_clone` [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Fix false positive in `try_err` [#4721](https://github.com/rust-lang/rust-clippy/pull/4721)
 +* Fix false positive in `toplevel_ref_arg` [#4570](https://github.com/rust-lang/rust-clippy/pull/4570)
 +* Fix false positive in `multiple_inherent_impl` [#4593](https://github.com/rust-lang/rust-clippy/pull/4593)
 +* Improve more suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4575](https://github.com/rust-lang/rust-clippy/pull/4575)
 +* Improve suggestion for `zero_ptr` [#4599](https://github.com/rust-lang/rust-clippy/pull/4599)
 +* Improve suggestion for `explicit_counter_loop` [#4691](https://github.com/rust-lang/rust-clippy/pull/4691)
 +* Improve suggestion for `mul_add` [#4602](https://github.com/rust-lang/rust-clippy/pull/4602)
 +* Improve suggestion for `assertions_on_constants` [#4635](https://github.com/rust-lang/rust-clippy/pull/4635)
 +* Fix ICE in `use_self` [#4671](https://github.com/rust-lang/rust-clippy/pull/4671)
 +* Fix ICE when encountering const casts [#4590](https://github.com/rust-lang/rust-clippy/pull/4590)
 +
 +## Rust 1.39
 +
 +Released 2019-11-07
 +
 +[3aea860...4e7e71b](https://github.com/rust-lang/rust-clippy/compare/3aea860...4e7e71b)
 +
 +* New Lints:
 +  * [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479)
 +  * [`flat_map_identity`] [#4231](https://github.com/rust-lang/rust-clippy/pull/4231)
 +  * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535)
 +  * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511)
 +  * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394)
 +  * `option_and_then_some` [#4386](https://github.com/rust-lang/rust-clippy/pull/4386)
 +  * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498)
 +* Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348)
 +* Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403)
 +* Move `cast_lossless` to pedantic group [#4539](https://github.com/rust-lang/rust-clippy/pull/4539)
 +* `temporary_cstring_as_ptr` now catches more cases [#4425](https://github.com/rust-lang/rust-clippy/pull/4425)
 +* `use_self` now works in constructors, too [#4525](https://github.com/rust-lang/rust-clippy/pull/4525)
 +* `cargo_common_metadata` now checks for license files [#4518](https://github.com/rust-lang/rust-clippy/pull/4518)
 +* `cognitive_complexity` now includes the measured complexity in the warning message [#4469](https://github.com/rust-lang/rust-clippy/pull/4469)
 +* Fix false positives in `block_in_if_*` lints [#4458](https://github.com/rust-lang/rust-clippy/pull/4458)
 +* Fix false positive in `cast_lossless` [#4473](https://github.com/rust-lang/rust-clippy/pull/4473)
 +* Fix false positive in `clone_on_copy` [#4411](https://github.com/rust-lang/rust-clippy/pull/4411)
 +* Fix false positive in `deref_addrof` [#4487](https://github.com/rust-lang/rust-clippy/pull/4487)
 +* Fix false positive in `too_many_lines` [#4490](https://github.com/rust-lang/rust-clippy/pull/4490)
 +* Fix false positive in `new_ret_no_self` [#4365](https://github.com/rust-lang/rust-clippy/pull/4365)
 +* Fix false positive in `manual_swap` [#4478](https://github.com/rust-lang/rust-clippy/pull/4478)
 +* Fix false positive in `missing_const_for_fn` [#4450](https://github.com/rust-lang/rust-clippy/pull/4450)
 +* Fix false positive in `extra_unused_lifetimes` [#4477](https://github.com/rust-lang/rust-clippy/pull/4477)
 +* Fix false positive in `inherent_to_string` [#4460](https://github.com/rust-lang/rust-clippy/pull/4460)
 +* Fix false positive in `map_entry` [#4495](https://github.com/rust-lang/rust-clippy/pull/4495)
 +* Fix false positive in `unused_unit` [#4445](https://github.com/rust-lang/rust-clippy/pull/4445)
 +* Fix false positive in `redundant_pattern` [#4489](https://github.com/rust-lang/rust-clippy/pull/4489)
 +* Fix false positive in `wrong_self_convention` [#4369](https://github.com/rust-lang/rust-clippy/pull/4369)
 +* Improve various suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4558](https://github.com/rust-lang/rust-clippy/pull/4558)
 +* Improve suggestions for `redundant_pattern_matching` [#4352](https://github.com/rust-lang/rust-clippy/pull/4352)
 +* Improve suggestions for `explicit_write` [#4544](https://github.com/rust-lang/rust-clippy/pull/4544)
 +* Improve suggestion for `or_fun_call` [#4522](https://github.com/rust-lang/rust-clippy/pull/4522)
 +* Improve suggestion for `match_as_ref` [#4446](https://github.com/rust-lang/rust-clippy/pull/4446)
 +* Improve suggestion for `unnecessary_fold_span` [#4382](https://github.com/rust-lang/rust-clippy/pull/4382)
 +* Add suggestions for `unseparated_literal_suffix` [#4401](https://github.com/rust-lang/rust-clippy/pull/4401)
 +* Add suggestions for `char_lit_as_u8` [#4418](https://github.com/rust-lang/rust-clippy/pull/4418)
 +
 +## Rust 1.38
 +
 +Released 2019-09-26
 +
 +[e3cb40e...3aea860](https://github.com/rust-lang/rust-clippy/compare/e3cb40e...3aea860)
 +
 +* New Lints:
 +  * [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203)
 +  * [`inherent_to_string`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`inherent_to_string_shadow_display`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`type_repetition_in_bounds`] [#3766](https://github.com/rust-lang/rust-clippy/pull/3766)
 +  * [`try_err`] [#4222](https://github.com/rust-lang/rust-clippy/pull/4222)
 +* Move `{unnnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
 +* Extend the `use_self` lint to suggest uses of `Self::Variant` [#4308](https://github.com/rust-lang/rust-clippy/pull/4308)
 +* Improve suggestion for needless return [#4262](https://github.com/rust-lang/rust-clippy/pull/4262)
 +* Add auto-fixable suggestion for `let_unit` [#4337](https://github.com/rust-lang/rust-clippy/pull/4337)
 +* Fix false positive in `pub_enum_variant_names` and `enum_variant_names` [#4345](https://github.com/rust-lang/rust-clippy/pull/4345)
 +* Fix false positive in `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
 +* Fix false positive in `string_lit_as_bytes` [#4233](https://github.com/rust-lang/rust-clippy/pull/4233)
 +* Fix false positive in `needless_lifetimes` [#4266](https://github.com/rust-lang/rust-clippy/pull/4266)
 +* Fix false positive in `float_cmp` [#4275](https://github.com/rust-lang/rust-clippy/pull/4275)
 +* Fix false positives in `needless_return` [#4274](https://github.com/rust-lang/rust-clippy/pull/4274)
 +* Fix false negative in `match_same_arms` [#4246](https://github.com/rust-lang/rust-clippy/pull/4246)
 +* Fix incorrect suggestion for `needless_bool` [#4335](https://github.com/rust-lang/rust-clippy/pull/4335)
 +* Improve suggestion for `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
 +* Improve suggestion for `single_char_literal` [#4361](https://github.com/rust-lang/rust-clippy/pull/4361)
 +* Improve suggestion for `len_zero` [#4314](https://github.com/rust-lang/rust-clippy/pull/4314)
 +* Fix ICE in `implicit_hasher` [#4268](https://github.com/rust-lang/rust-clippy/pull/4268)
 +* Fix allow bug in `trivially_copy_pass_by_ref` [#4250](https://github.com/rust-lang/rust-clippy/pull/4250)
 +
 +## Rust 1.37
 +
 +Released 2019-08-15
 +
 +[082cfa7...e3cb40e](https://github.com/rust-lang/rust-clippy/compare/082cfa7...e3cb40e)
 +
 +* New Lints:
 +  * [`checked_conversions`] [#4088](https://github.com/rust-lang/rust-clippy/pull/4088)
 +  * [`get_last_with_len`] [#3832](https://github.com/rust-lang/rust-clippy/pull/3832)
 +  * [`integer_division`] [#4195](https://github.com/rust-lang/rust-clippy/pull/4195)
 +* Renamed Lint: `const_static_lifetime` is now called [`redundant_static_lifetimes`].
 +  The lint now covers statics in addition to consts [#4162](https://github.com/rust-lang/rust-clippy/pull/4162)
 +* [`match_same_arms`] now warns for all identical arms, instead of only the first one [#4102](https://github.com/rust-lang/rust-clippy/pull/4102)
 +* [`needless_return`] now works with void functions [#4220](https://github.com/rust-lang/rust-clippy/pull/4220)
 +* Fix false positive in [`redundant_closure`] [#4190](https://github.com/rust-lang/rust-clippy/pull/4190)
 +* Fix false positive in [`useless_attribute`] [#4107](https://github.com/rust-lang/rust-clippy/pull/4107)
 +* Fix incorrect suggestion for [`float_cmp`] [#4214](https://github.com/rust-lang/rust-clippy/pull/4214)
 +* Add suggestions for [`print_with_newline`] and [`write_with_newline`] [#4136](https://github.com/rust-lang/rust-clippy/pull/4136)
 +* Improve suggestions for `option_map_unwrap_or_else` and `result_map_unwrap_or_else` [#4164](https://github.com/rust-lang/rust-clippy/pull/4164)
 +* Improve suggestions for [`non_ascii_literal`] [#4119](https://github.com/rust-lang/rust-clippy/pull/4119)
 +* Improve diagnostics for [`let_and_return`] [#4137](https://github.com/rust-lang/rust-clippy/pull/4137)
 +* Improve diagnostics for [`trivially_copy_pass_by_ref`] [#4071](https://github.com/rust-lang/rust-clippy/pull/4071)
 +* Add macro check for [`unreadable_literal`] [#4099](https://github.com/rust-lang/rust-clippy/pull/4099)
 +
 +## Rust 1.36
 +
 +Released 2019-07-04
 +
 +[eb9f9b1...082cfa7](https://github.com/rust-lang/rust-clippy/compare/eb9f9b1...082cfa7)
 +
 +* New lints: [`find_map`], [`filter_map_next`] [#4039](https://github.com/rust-lang/rust-clippy/pull/4039)
 +* New lint: [`path_buf_push_overwrite`] [#3954](https://github.com/rust-lang/rust-clippy/pull/3954)
 +* Move `path_buf_push_overwrite` to the nursery [#4013](https://github.com/rust-lang/rust-clippy/pull/4013)
 +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 +* Allow allowing of [`toplevel_ref_arg`] lint [#4007](https://github.com/rust-lang/rust-clippy/pull/4007)
 +* Fix false negative in [`or_fun_call`] pertaining to nested constructors [#4084](https://github.com/rust-lang/rust-clippy/pull/4084)
 +* Fix false positive in [`or_fun_call`] pertaining to enum variant constructors [#4018](https://github.com/rust-lang/rust-clippy/pull/4018)
 +* Fix false positive in [`useless_let_if_seq`] pertaining to interior mutability [#4035](https://github.com/rust-lang/rust-clippy/pull/4035)
 +* Fix false positive in [`redundant_closure`] pertaining to non-function types [#4008](https://github.com/rust-lang/rust-clippy/pull/4008)
 +* Fix false positive in [`let_and_return`] pertaining to attributes on `let`s [#4024](https://github.com/rust-lang/rust-clippy/pull/4024)
 +* Fix false positive in [`module_name_repetitions`] lint pertaining to attributes [#4006](https://github.com/rust-lang/rust-clippy/pull/4006)
 +* Fix false positive on [`assertions_on_constants`] pertaining to `debug_assert!` [#3989](https://github.com/rust-lang/rust-clippy/pull/3989)
 +* Improve suggestion in [`map_clone`] to suggest `.copied()` where applicable  [#3970](https://github.com/rust-lang/rust-clippy/pull/3970) [#4043](https://github.com/rust-lang/rust-clippy/pull/4043)
 +* Improve suggestion for [`search_is_some`] [#4049](https://github.com/rust-lang/rust-clippy/pull/4049)
 +* Improve suggestion applicability for [`naive_bytecount`] [#3984](https://github.com/rust-lang/rust-clippy/pull/3984)
 +* Improve suggestion applicability for [`while_let_loop`] [#3975](https://github.com/rust-lang/rust-clippy/pull/3975)
 +* Improve diagnostics for [`too_many_arguments`] [#4053](https://github.com/rust-lang/rust-clippy/pull/4053)
 +* Improve diagnostics for [`cast_lossless`] [#4021](https://github.com/rust-lang/rust-clippy/pull/4021)
 +* Deal with macro checks in desugarings better [#4082](https://github.com/rust-lang/rust-clippy/pull/4082)
 +* Add macro check for [`unnecessary_cast`]  [#4026](https://github.com/rust-lang/rust-clippy/pull/4026)
 +* Remove [`approx_constant`]'s documentation's "Known problems" section. [#4027](https://github.com/rust-lang/rust-clippy/pull/4027)
 +* Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960)
 +* Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931)
 +
 +
 +## Rust 1.35
 +
 +Released 2019-05-20
 +
 +[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
 +
 +* New lint: `drop_bounds` to detect `T: Drop` bounds
 +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 +* Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code
 +* Move [`get_unwrap`] to the restriction category
 +* Improve suggestions for [`iter_cloned_collect`]
 +* Improve suggestions for [`cast_lossless`] to suggest suffixed literals
 +* Fix false positives in [`print_with_newline`] and [`write_with_newline`] pertaining to raw strings
 +* Fix false positive in [`needless_range_loop`] pertaining to structs without a `.iter()`
 +* Fix false positive in [`bool_comparison`] pertaining to non-bool types
 +* Fix false positive in [`redundant_closure`] pertaining to differences in borrows
 +* Fix false positive in `option_map_unwrap_or` on non-copy types
 +* Fix false positives in [`missing_const_for_fn`] pertaining to macros and trait method impls
 +* Fix false positive in [`needless_pass_by_value`] pertaining to procedural macros
 +* Fix false positive in [`needless_continue`] pertaining to loop labels
 +* Fix false positive for [`boxed_local`] pertaining to arguments moved into closures
 +* Fix false positive for [`use_self`] in nested functions
 +* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846)
 +* Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables
 +* Fix suggestion for [`single_char_pattern`] to correctly escape single quotes
 +* Avoid triggering [`redundant_closure`] in macros
 +* ICE fixes: [#3805](https://github.com/rust-lang/rust-clippy/pull/3805), [#3772](https://github.com/rust-lang/rust-clippy/pull/3772), [#3741](https://github.com/rust-lang/rust-clippy/pull/3741)
 +
 +## Rust 1.34
 +
 +Released 2019-04-10
 +
 +[1b89724...1fac380](https://github.com/rust-lang/rust-clippy/compare/1b89724...1fac380)
 +
 +* New lint: [`assertions_on_constants`] to detect for example `assert!(true)`
 +* New lint: [`dbg_macro`] to detect uses of the `dbg!` macro
 +* New lint: [`missing_const_for_fn`] that can suggest functions to be made `const`
 +* New lint: [`too_many_lines`] to detect functions with excessive LOC. It can be
 +  configured using the `too-many-lines-threshold` configuration.
 +* New lint: [`wildcard_enum_match_arm`] to check for wildcard enum matches using `_`
 +* Expand `redundant_closure` to also work for methods (not only functions)
 +* Fix ICEs in `vec_box`, `needless_pass_by_value` and `implicit_hasher`
 +* Fix false positive in `cast_sign_loss`
 +* Fix false positive in `integer_arithmetic`
 +* Fix false positive in `unit_arg`
 +* Fix false positives in `implicit_return`
 +* Add suggestion to `explicit_write`
 +* Improve suggestions for `question_mark` lint
 +* Fix incorrect suggestion for `cast_lossless`
 +* Fix incorrect suggestion for `expect_fun_call`
 +* Fix incorrect suggestion for `needless_bool`
 +* Fix incorrect suggestion for `needless_range_loop`
 +* Fix incorrect suggestion for `use_self`
 +* Fix incorrect suggestion for `while_let_on_iterator`
 +* Clippy is now slightly easier to invoke in non-cargo contexts. See
 +  [#3665][pull3665] for more details.
 +* We now have [improved documentation][adding_lints] on how to add new lints
 +
 +## Rust 1.33
 +
 +Released 2019-02-26
 +
 +[b2601be...1b89724](https://github.com/rust-lang/rust-clippy/compare/b2601be...1b89724)
 +
 +* New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`]
 +* The `rust-clippy` repository is now part of the `rust-lang` org.
 +* Rename `stutter` to `module_name_repetitions`
 +* Merge `new_without_default_derive` into `new_without_default` lint
 +* Move `large_digit_groups` from `style` group to `pedantic`
 +* Expand `bool_comparison` to check for `<`, `<=`, `>`, `>=`, and `!=`
 +  comparisons against booleans
 +* Expand `no_effect` to detect writes to constants such as `A_CONST.field = 2`
 +* Expand `redundant_clone` to work on struct fields
 +* Expand `suspicious_else_formatting` to detect `if .. {..} {..}`
 +* Expand `use_self` to work on tuple structs and also in local macros
 +* Fix ICE in `result_map_unit_fn` and `option_map_unit_fn`
 +* Fix false positives in `implicit_return`
 +* Fix false positives in `use_self`
 +* Fix false negative in `clone_on_copy`
 +* Fix false positive in `doc_markdown`
 +* Fix false positive in `empty_loop`
 +* Fix false positive in `if_same_then_else`
 +* Fix false positive in `infinite_iter`
 +* Fix false positive in `question_mark`
 +* Fix false positive in `useless_asref`
 +* Fix false positive in `wildcard_dependencies`
 +* Fix false positive in `write_with_newline`
 +* Add suggestion to `explicit_write`
 +* Improve suggestions for `question_mark` lint
 +* Fix incorrect suggestion for `get_unwrap`
 +
 +## Rust 1.32
 +
 +Released 2019-01-17
 +
 +[2e26fdc2...b2601be](https://github.com/rust-lang/rust-clippy/compare/2e26fdc2...b2601be)
 +
 +* New lints: [`slow_vector_initialization`], `mem_discriminant_non_enum`,
 +  [`redundant_clone`], [`wildcard_dependencies`],
 +  [`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`],
 +  [`cargo_common_metadata`]
 +* Add support for `u128` and `i128` to integer related lints
 +* Add float support to `mistyped_literal_suffixes`
 +* Fix false positives in `use_self`
 +* Fix false positives in `missing_comma`
 +* Fix false positives in `new_ret_no_self`
 +* Fix false positives in `possible_missing_comma`
 +* Fix false positive in `integer_arithmetic` in constant items
 +* Fix false positive in `needless_borrow`
 +* Fix false positive in `out_of_bounds_indexing`
 +* Fix false positive in `new_without_default_derive`
 +* Fix false positive in `string_lit_as_bytes`
 +* Fix false negative in `out_of_bounds_indexing`
 +* Fix false negative in `use_self`. It will now also check existential types
 +* Fix incorrect suggestion for `redundant_closure_call`
 +* Fix various suggestions that contained expanded macros
 +* Fix `bool_comparison` triggering 3 times on on on the same code
 +* Expand `trivially_copy_pass_by_ref` to work on trait methods
 +* Improve suggestion for `needless_range_loop`
 +* Move `needless_pass_by_value` from `pedantic` group to `style`
 +
 +## Rust 1.31
 +
 +Released 2018-12-06
 +
 +[125907ad..2e26fdc2](https://github.com/rust-lang/rust-clippy/compare/125907ad..2e26fdc2)
 +
 +* Clippy has been relicensed under a dual MIT / Apache license.
 +  See [#3093](https://github.com/rust-lang/rust-clippy/issues/3093) for more
 +  information.
 +* With Rust 1.31, Clippy is no longer available via crates.io. The recommended
 +  installation method is via `rustup component add clippy`.
 +* New lints: [`redundant_pattern_matching`], [`unnecessary_filter_map`],
 +  [`unused_unit`], [`map_flatten`], [`mem_replace_option_with_none`]
 +* Fix ICE in `if_let_redundant_pattern_matching`
 +* Fix ICE in `needless_pass_by_value` when encountering a generic function
 +  argument with a lifetime parameter
 +* Fix ICE in `needless_range_loop`
 +* Fix ICE in `single_char_pattern` when encountering a constant value
 +* Fix false positive in `assign_op_pattern`
 +* Fix false positive in `boxed_local` on trait implementations
 +* Fix false positive in `cmp_owned`
 +* Fix false positive in `collapsible_if` when conditionals have comments
 +* Fix false positive in `double_parens`
 +* Fix false positive in `excessive_precision`
 +* Fix false positive in `explicit_counter_loop`
 +* Fix false positive in `fn_to_numeric_cast_with_truncation`
 +* Fix false positive in `map_clone`
 +* Fix false positive in `new_ret_no_self`
 +* Fix false positive in `new_without_default` when `new` is unsafe
 +* Fix false positive in `type_complexity` when using extern types
 +* Fix false positive in `useless_format`
 +* Fix false positive in `wrong_self_convention`
 +* Fix incorrect suggestion for `excessive_precision`
 +* Fix incorrect suggestion for `expect_fun_call`
 +* Fix incorrect suggestion for `get_unwrap`
 +* Fix incorrect suggestion for `useless_format`
 +* `fn_to_numeric_cast_with_truncation` lint can be disabled again
 +* Improve suggestions for `manual_memcpy`
 +* Improve help message for `needless_lifetimes`
 +
 +## Rust 1.30
 +
 +Released 2018-10-25
 +
 +[14207503...125907ad](https://github.com/rust-lang/rust-clippy/compare/14207503...125907ad)
 +
 +* Deprecate `assign_ops` lint
 +* New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`],
 +  [`needless_collect`], [`copy_iterator`]
 +* `cargo clippy -V` now includes the Clippy commit hash of the Rust
 +  Clippy component
 +* Fix ICE in `implicit_hasher`
 +* Fix ICE when encountering `println!("{}" a);`
 +* Fix ICE when encountering a macro call in match statements
 +* Fix false positive in `default_trait_access`
 +* Fix false positive in `trivially_copy_pass_by_ref`
 +* Fix false positive in `similar_names`
 +* Fix false positive in `redundant_field_name`
 +* Fix false positive in `expect_fun_call`
 +* Fix false negative in `identity_conversion`
 +* Fix false negative in `explicit_counter_loop`
 +* Fix `range_plus_one` suggestion and false negative
 +* `print_with_newline` / `write_with_newline`: don't warn about string with several `\n`s in them
 +* Fix `useless_attribute` to also whitelist `unused_extern_crates`
 +* Fix incorrect suggestion for `single_char_pattern`
 +* Improve suggestion for `identity_conversion` lint
 +* Move `explicit_iter_loop` and `explicit_into_iter_loop` from `style` group to `pedantic`
 +* Move `range_plus_one` and `range_minus_one` from `nursery` group to `complexity`
 +* Move `shadow_unrelated` from `restriction` group to `pedantic`
 +* Move `indexing_slicing` from `pedantic` group to `restriction`
 +
 +## Rust 1.29
 +
 +Released 2018-09-13
 +
 +[v0.0.212...14207503](https://github.com/rust-lang/rust-clippy/compare/v0.0.212...14207503)
 +
 +* :tada: :tada: **Rust 1.29 is the first stable Rust that includes a bundled Clippy** :tada:
 +  :tada:
 +  You can now run `rustup component add clippy-preview` and then `cargo
 +  clippy` to run Clippy. This should put an end to the continuous nightly
 +  upgrades for Clippy users.
 +* Clippy now follows the Rust versioning scheme instead of its own
 +* Fix ICE when encountering a `while let (..) = x.iter()` construct
 +* Fix false positives in `use_self`
 +* Fix false positive in `trivially_copy_pass_by_ref`
 +* Fix false positive in `useless_attribute` lint
 +* Fix false positive in `print_literal`
 +* Fix `use_self` regressions
 +* Improve lint message for `neg_cmp_op_on_partial_ord`
 +* Improve suggestion highlight for `single_char_pattern`
 +* Improve suggestions for various print/write macro lints
 +* Improve website header
 +
 +## 0.0.212 (2018-07-10)
 +* Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)*
 +
 +## 0.0.211
 +* Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)*
 +
 +## 0.0.210
 +* Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)*
 +
 +## 0.0.209
 +* Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)*
 +
 +## 0.0.208
 +* Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)*
 +
 +## 0.0.207
 +* Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)*
 +
 +## 0.0.206
 +* Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)*
 +
 +## 0.0.205
 +* Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)*
 +* Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint
 +
 +## 0.0.204
 +* Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)*
 +
 +## 0.0.203
 +* Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)*
 +* Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)`
 +
 +## 0.0.202
 +* Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)*
 +
 +## 0.0.201
 +* Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)*
 +
 +## 0.0.200
 +* Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)*
 +
 +## 0.0.199
 +* Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)*
 +
 +## 0.0.198
 +* Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)*
 +
 +## 0.0.197
 +* Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)*
 +
 +## 0.0.196
 +* Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)*
 +
 +## 0.0.195
 +* Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)*
 +
 +## 0.0.194
 +* Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)*
 +* New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`]
 +
 +## 0.0.193
 +* Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)*
 +
 +## 0.0.192
 +* Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)*
 +* New lint: [`print_literal`]
 +
 +## 0.0.191
 +* Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)*
 +* Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction.
 +
 +## 0.0.190
 +* Fix a bunch of intermittent cargo bugs
 +
 +## 0.0.189
 +* Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)*
 +
 +## 0.0.188
 +* Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)*
 +* New lint: [`while_immutable_condition`]
 +
 +## 0.0.187
 +* Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)*
 +* New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`]
 +
 +## 0.0.186
 +* Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)*
 +* Various false positive fixes
 +
 +## 0.0.185
 +* Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)*
 +* New lint: [`question_mark`]
 +
 +## 0.0.184
 +* Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)*
 +* New lints: [`double_comparisons`], [`empty_line_after_outer_attr`]
 +
 +## 0.0.183
 +* Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)*
 +* New lint: [`misaligned_transmute`]
 +
 +## 0.0.182
 +* Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)*
 +* New lint: [`decimal_literal_representation`]
 +
 +## 0.0.181
 +* Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)*
 +* New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`]
 +* Removed `unit_expr`
 +* Various false positive fixes for [`needless_pass_by_value`]
 +
 +## 0.0.180
 +* Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)*
 +
 +## 0.0.179
 +* Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)*
 +
 +## 0.0.178
 +* Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)*
 +
 +## 0.0.177
 +* Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)*
 +* New lint: [`match_as_ref`]
 +
 +## 0.0.176
 +* Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)*
 +
 +## 0.0.175
 +* Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)*
 +
 +## 0.0.174
 +* Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)*
 +
 +## 0.0.173
 +* Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)*
 +
 +## 0.0.172
 +* Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)*
 +
 +## 0.0.171
 +* Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)*
 +
 +## 0.0.170
 +* Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)*
 +
 +## 0.0.169
 +* Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)*
 +* New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`]
 +
 +## 0.0.168
 +* Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)*
 +
 +## 0.0.167
 +* Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)*
 +* New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`]
 +
 +## 0.0.166
 +* Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)*
 +* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`],
 +  [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`],
 +  [`transmute_int_to_float`]
 +
 +## 0.0.165
 +* Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27)
 +* New lint: [`mut_range_bound`]
 +
 +## 0.0.164
 +* Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)*
 +* New lint: [`int_plus_one`]
 +
 +## 0.0.163
 +* Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)*
 +
 +## 0.0.162
 +* Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)*
 +* New lint: [`chars_last_cmp`]
 +* Improved suggestions for [`needless_borrow`], [`ptr_arg`],
 +
 +## 0.0.161
 +* Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)*
 +
 +## 0.0.160
 +* Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)*
 +
 +## 0.0.159
 +* Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)*
 +* New lint: [`clone_on_ref_ptr`]
 +
 +## 0.0.158
 +* New lint: [`manual_memcpy`]
 +* [`cast_lossless`] no longer has redundant parentheses in its suggestions
 +* Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)*
 +
 +## 0.0.157 - 2017-09-04
 +* Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)*
 +* New lint: `unit_expr`
 +
 +## 0.0.156 - 2017-09-03
 +* Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)*
 +
 +## 0.0.155
 +* Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)*
 +* New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`]
 +
 +## 0.0.154
 +* Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)*
 +* Fix [`use_self`] triggering inside derives
 +* Add support for linting an entire workspace with `cargo clippy --all`
 +* New lint: [`naive_bytecount`]
 +
 +## 0.0.153
 +* Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)*
 +* New lint: [`use_self`]
 +
 +## 0.0.152
 +* Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)*
 +
 +## 0.0.151
 +* Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)*
 +
 +## 0.0.150
 +* Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)*
 +
 +## 0.0.148
 +* Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)*
 +* New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`]
 +
 +## 0.0.147
 +* Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)*
 +
 +## 0.0.146
 +* Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)*
 +* Fixes false positives in `inline_always`
 +* Fixes false negatives in `panic_params`
 +
 +## 0.0.145
 +* Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)*
 +
 +## 0.0.144
 +* Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)*
 +
 +## 0.0.143
 +* Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)*
 +* Fix `cargo clippy` crashing on `dylib` projects
 +* Fix false positives around `nested_while_let` and `never_loop`
 +
 +## 0.0.142
 +* Update to *rustc 1.20.0-nightly (067971139 2017-07-02)*
 +
 +## 0.0.141
 +* Rewrite of the `doc_markdown` lint.
 +* Deprecated [`range_step_by_zero`]
 +* New lint: [`iterator_step_by_zero`]
 +* New lint: [`needless_borrowed_reference`]
 +* Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)*
 +
 +## 0.0.140 - 2017-06-16
 +* Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)*
 +
 +## 0.0.139 — 2017-06-10
 +* Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)*
 +* Fix bugs with for loop desugaring
 +* Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`]
 +
 +## 0.0.138 — 2017-06-05
 +* Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)*
 +
 +## 0.0.137 — 2017-06-05
 +* Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)*
 +
 +## 0.0.136 — 2017—05—26
 +* Update to *rustc 1.19.0-nightly (557967766 2017-05-26)*
 +
 +## 0.0.135 — 2017—05—24
 +* Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)*
 +
 +## 0.0.134 — 2017—05—19
 +* Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)*
 +
 +## 0.0.133 — 2017—05—14
 +* Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)*
 +
 +## 0.0.132 — 2017—05—05
 +* Fix various bugs and some ices
 +
 +## 0.0.131 — 2017—05—04
 +* Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)*
 +
 +## 0.0.130 — 2017—05—03
 +* Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)*
 +
 +## 0.0.129 — 2017-05-01
 +* Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)*
 +
 +## 0.0.128 — 2017-04-28
 +* Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)*
 +
 +## 0.0.127 — 2017-04-27
 +* Update to *rustc 1.18.0-nightly (036983201 2017-04-26)*
 +* New lint: [`needless_continue`]
 +
 +## 0.0.126 — 2017-04-24
 +* Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)*
 +
 +## 0.0.125 — 2017-04-19
 +* Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)*
 +
 +## 0.0.124 — 2017-04-16
 +* Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)*
 +
 +## 0.0.123 — 2017-04-07
 +* Fix various false positives
 +
 +## 0.0.122 — 2017-04-07
 +* Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)*
 +* New lint: [`op_ref`]
 +
 +## 0.0.121 — 2017-03-21
 +* Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)*
 +
 +## 0.0.120 — 2017-03-17
 +* Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)*
 +
 +## 0.0.119 — 2017-03-13
 +* Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)*
 +
 +## 0.0.118 — 2017-03-05
 +* Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)*
 +
 +## 0.0.117 — 2017-03-01
 +* Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)*
 +
 +## 0.0.116 — 2017-02-28
 +* Fix `cargo clippy` on 64 bit windows systems
 +
 +## 0.0.115 — 2017-02-27
 +* Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)*
 +* New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`]
 +
 +## 0.0.114 — 2017-02-08
 +* Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)*
 +* Tests are now ui tests (testing the exact output of rustc)
 +
 +## 0.0.113 — 2017-02-04
 +* Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)*
 +* New lint: [`large_enum_variant`]
 +* `explicit_into_iter_loop` provides suggestions
 +
 +## 0.0.112 — 2017-01-27
 +* Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)*
 +
 +## 0.0.111 — 2017-01-21
 +* Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)*
 +
 +## 0.0.110 — 2017-01-20
 +* Add badges and categories to `Cargo.toml`
 +
 +## 0.0.109 — 2017-01-19
 +* Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)*
 +
 +## 0.0.108 — 2017-01-12
 +* Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)*
 +
 +## 0.0.107 — 2017-01-11
 +* Update regex dependency
 +* Fix FP when matching `&&mut` by `&ref`
 +* Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()`
 +* New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`]
 +
 +## 0.0.106 — 2017-01-04
 +* Fix FP introduced by rustup in [`wrong_self_convention`]
 +
 +## 0.0.105 — 2017-01-04
 +* Update to *rustc 1.16.0-nightly (468227129 2017-01-03)*
 +* New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`]
 +* Fix suggestion in [`new_without_default`]
 +* FP fix in [`absurd_extreme_comparisons`]
 +
 +## 0.0.104 — 2016-12-15
 +* Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)*
 +
 +## 0.0.103 — 2016-11-25
 +* Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)*
 +
 +## 0.0.102 — 2016-11-24
 +* Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)*
 +
 +## 0.0.101 — 2016-11-23
 +* Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)*
 +* New lint: [`string_extend_chars`]
 +
 +## 0.0.100 — 2016-11-20
 +* Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)*
 +
 +## 0.0.99 — 2016-11-18
 +* Update to rustc 1.15.0-nightly (0ed951993 2016-11-14)
 +* New lint: [`get_unwrap`]
 +
 +## 0.0.98 — 2016-11-08
 +* Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy`
 +
 +## 0.0.97 — 2016-11-03
 +* For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was
 +  previously added for a short time under the name `clippy` but removed for
 +  compatibility.
 +* `cargo clippy --help` is more helping (and less helpful :smile:)
 +* Rustup to *rustc 1.14.0-nightly (5665bdf3e 2016-11-02)*
 +* New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`]
 +
 +## 0.0.96 — 2016-10-22
 +* Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)*
 +* New lint: [`iter_skip_next`]
 +
 +## 0.0.95 — 2016-10-06
 +* Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)*
 +
 +## 0.0.94 — 2016-10-04
 +* Fixes bustage on Windows due to forbidden directory name
 +
 +## 0.0.93 — 2016-10-03
 +* Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)*
 +* `option_map_unwrap_or` and `option_map_unwrap_or_else` are now
 +  allowed by default.
 +* New lint: [`explicit_into_iter_loop`]
 +
 +## 0.0.92 — 2016-09-30
 +* Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)*
 +
 +## 0.0.91 — 2016-09-28
 +* Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)*
 +
 +## 0.0.90 — 2016-09-09
 +* Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)*
 +
 +## 0.0.89 — 2016-09-06
 +* Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)*
 +
 +## 0.0.88 — 2016-09-04
 +* Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)*
 +* The following lints are not new but were only usable through the `clippy`
 +  lint groups: [`filter_next`], `for_loop_over_option`,
 +  `for_loop_over_result` and [`match_overlapping_arm`]. You should now be
 +  able to `#[allow/deny]` them individually and they are available directly
 +  through `cargo clippy`.
 +
 +## 0.0.87 — 2016-08-31
 +* Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)*
 +* New lints: [`builtin_type_shadow`]
 +* Fix FP in [`zero_prefixed_literal`] and `0b`/`0o`
 +
 +## 0.0.86 — 2016-08-28
 +* Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)*
 +* New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`]
 +
 +## 0.0.85 — 2016-08-19
 +* Fix ICE with [`useless_attribute`]
 +* [`useless_attribute`] ignores `unused_imports` on `use` statements
 +
 +## 0.0.84 — 2016-08-18
 +* Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)*
 +
 +## 0.0.83 — 2016-08-17
 +* Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)*
 +* New lints: [`print_with_newline`], [`useless_attribute`]
 +
 +## 0.0.82 — 2016-08-17
 +* Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)*
 +* New lint: [`module_inception`]
 +
 +## 0.0.81 — 2016-08-14
 +* Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)*
 +* New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`]
 +* False positive fix in [`too_many_arguments`]
 +* Addition of functionality to [`needless_borrow`]
 +* Suggestions for [`clone_on_copy`]
 +* Bug fix in [`wrong_self_convention`]
 +* Doc improvements
 +
 +## 0.0.80 — 2016-07-31
 +* Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)*
 +* New lints: [`misrefactored_assign_op`], [`serde_api_misuse`]
 +
 +## 0.0.79 — 2016-07-10
 +* Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)*
 +* Major suggestions refactoring
 +
 +## 0.0.78 — 2016-07-02
 +* Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)*
 +* New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`]
 +* For compatibility, `cargo clippy` does not defines the `clippy` feature
 +  introduced in 0.0.76 anymore
 +* [`collapsible_if`] now considers `if let`
 +
 +## 0.0.77 — 2016-06-21
 +* Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)*
 +* New lints: `stutter` and [`iter_nth`]
 +
 +## 0.0.76 — 2016-06-10
 +* Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)*
 +* `cargo clippy` now automatically defines the `clippy` feature
 +* New lint: [`not_unsafe_ptr_arg_deref`]
 +
 +## 0.0.75 — 2016-06-08
 +* Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)*
 +
 +## 0.0.74 — 2016-06-07
 +* Fix bug with `cargo-clippy` JSON parsing
 +* Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the
 +  “for further information visit *lint-link*” message.
 +
 +## 0.0.73 — 2016-06-05
 +* Fix false positives in [`useless_let_if_seq`]
 +
 +## 0.0.72 — 2016-06-04
 +* Fix false positives in [`useless_let_if_seq`]
 +
 +## 0.0.71 — 2016-05-31
 +* Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)*
 +* New lint: [`useless_let_if_seq`]
 +
 +## 0.0.70 — 2016-05-28
 +* Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)*
 +* [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`,
 +  `RegexBuilder::new` and byte regexes
 +
 +## 0.0.69 — 2016-05-20
 +* Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)*
 +* [`used_underscore_binding`] has been made `Allow` temporarily
 +
 +## 0.0.68 — 2016-05-17
 +* Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)*
 +* New lint: [`unnecessary_operation`]
 +
 +## 0.0.67 — 2016-05-12
 +* Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)*
 +
 +## 0.0.66 — 2016-05-11
 +* New `cargo clippy` subcommand
 +* New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`]
 +
 +## 0.0.65 — 2016-05-08
 +* Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)*
 +* New lints: [`float_arithmetic`], [`integer_arithmetic`]
 +
 +## 0.0.64 — 2016-04-26
 +* Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)*
 +* New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`]
 +
 +## 0.0.63 — 2016-04-08
 +* Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)*
 +
 +## 0.0.62 — 2016-04-07
 +* Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)*
 +
 +## 0.0.61 — 2016-04-03
 +* Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)*
 +* New lint: [`invalid_upcast_comparisons`]
 +
 +## 0.0.60 — 2016-04-01
 +* Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)*
 +
 +## 0.0.59 — 2016-03-31
 +* Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)*
 +* New lints: [`logic_bug`], [`nonminimal_bool`]
 +* Fixed: [`match_same_arms`] now ignores arms with guards
 +* Improved: [`useless_vec`] now warns on `for … in vec![…]`
 +
 +## 0.0.58 — 2016-03-27
 +* Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)*
 +* New lint: [`doc_markdown`]
 +
 +## 0.0.57 — 2016-03-27
 +* Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)*
 +* Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`]
 +* New lint: [`crosspointer_transmute`]
 +
 +## 0.0.56 — 2016-03-23
 +* Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)*
 +* New lints: [`many_single_char_names`] and [`similar_names`]
 +
 +## 0.0.55 — 2016-03-21
 +* Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)*
 +
 +## 0.0.54 — 2016-03-16
 +* Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)*
 +
 +## 0.0.53 — 2016-03-15
 +* Add a [configuration file]
 +
 +## ~~0.0.52~~
 +
 +## 0.0.51 — 2016-03-13
 +* Add `str` to types considered by [`len_zero`]
 +* New lints: [`indexing_slicing`]
 +
 +## 0.0.50 — 2016-03-11
 +* Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)*
 +
 +## 0.0.49 — 2016-03-09
 +* Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)*
 +* New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`]
 +
 +## 0.0.48 — 2016-03-07
 +* Fixed: ICE in [`needless_range_loop`] with globals
 +
 +## 0.0.47 — 2016-03-07
 +* Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)*
 +* New lint: [`redundant_closure_call`]
 +
 +[`AsMut`]: https://doc.rust-lang.org/std/convert/trait.AsMut.html
 +[`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
 +[configuration file]: ./rust-clippy#configuration
 +[pull3665]: https://github.com/rust-lang/rust-clippy/pull/3665
 +[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
 +[`README.md`]: https://github.com/rust-lang/rust-clippy/blob/master/README.md
 +
 +<!-- lint disable no-unused-definitions -->
 +<!-- begin autogenerated links to lint list -->
 +[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
 +[`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason
 +[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
 +[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
 +[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
 +[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
 +[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
 +[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
 +[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
++[`await_holding_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
 +[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
 +[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
 +[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
 +[`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr
 +[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
 +[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
 +[`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
 +[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
 +[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
 +[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
++[`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len
 +[`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
 +[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
 +[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
 +[`cast_abs_to_unsigned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned
 +[`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor
 +[`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation
 +[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
 +[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
 +[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
 +[`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss
 +[`cast_ptr_alignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ptr_alignment
 +[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
 +[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
 +[`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes
 +[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
 +[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
 +[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
 +[`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions
 +[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
 +[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
 +[`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
 +[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied
 +[`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
 +[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
 +[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
 +[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
 +[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
 +[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
 +[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
 +[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
 +[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
 +[`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
 +[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
 +[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
 +[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
 +[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
 +[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
 +[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
 +[`default_union_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_union_representation
 +[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
 +[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
 +[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
 +[`deref_by_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_by_slicing
 +[`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls
 +[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
 +[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
 +[`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods
 +[`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents
 +[`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
 +[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
 +[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
 +[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
 +[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
 +[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
 +[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
 +[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
 +[`drop_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_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
 +[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
 +[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
++[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop
 +[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
 +[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
 +[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
 +[`empty_structs_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_structs_with_brackets
 +[`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant
 +[`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use
 +[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
 +[`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op
 +[`equatable_if_let`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_if_let
 +[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
 +[`err_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#err_expect
 +[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
 +[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
 +[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
 +[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs
 +[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
 +[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
 +[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used
 +[`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy
 +[`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop
 +[`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods
 +[`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop
 +[`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop
 +[`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write
 +[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
 +[`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain
 +[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
 +[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
 +[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
 +[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
 +[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
 +[`filter_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_identity
 +[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
 +[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
 +[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
 +[`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
 +[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option
 +[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
 +[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
 +[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
 +[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs
 +[`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons
 +[`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools
 +[`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast
 +[`fn_to_numeric_cast_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_any
 +[`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation
 +[`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map
 +[`for_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_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
 +[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
 +[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
 +[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
 +[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
 +[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
 +[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
 +[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
 +[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
 +[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
 +[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
 +[`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
 +[`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
 +[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
 +[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
 +[`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
 +[`index_refutable_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice
 +[`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
 +[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
 +[`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
 +[`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
 +[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
 +[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
 +[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
 +[`init_numbered_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#init_numbered_fields
 +[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
 +[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax
 +[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax
 +[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
 +[`inspect_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#inspect_for_each
 +[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
 +[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
 +[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
 +[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
 +[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
 +[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
 +[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
 +[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
++[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
 +[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
 +[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
 +[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
 +[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
 +[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice
 +[`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator
 +[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
 +[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
 +[`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
 +[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
 +[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
 +[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
 +[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
 +[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
 +[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
 +[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
++[`large_include_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file
 +[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
 +[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
 +[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
 +[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
 +[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
 +[`let_underscore_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop
 +[`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock
 +[`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
 +[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
 +[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
 +[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
 +[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
 +[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
 +[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
 +[`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
 +[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
 +[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits
 +[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
 +[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
 +[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
 +[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
 +[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
 +[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
 +[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
 +[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
 +[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
 +[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
 +[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
 +[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
 +[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
 +[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
 +[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
 +[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
 +[`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit
 +[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
 +[`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore
 +[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
 +[`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity
 +[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or
 +[`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
 +[`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
 +[`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
 +[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
 +[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
 +[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
 +[`match_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_result_ok
 +[`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
 +[`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
 +[`match_str_case_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_str_case_mismatch
 +[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
 +[`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants
 +[`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter
 +[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
 +[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
 +[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default
 +[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
 +[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
 +[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
 +[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
 +[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
 +[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
 +[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
 +[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
 +[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
 +[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
 +[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
 +[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
 +[`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
 +[`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files
 +[`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
 +[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
 +[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
 +[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
 +[`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
 +[`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl
 +[`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate
 +[`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit
 +[`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref
 +[`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut
 +[`mut_mutex_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mutex_lock
 +[`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound
 +[`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type
 +[`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic
 +[`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
 +[`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
 +[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
 +[`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool
 +[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
 +[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
 +[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
 +[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
 +[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
 +[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
 +[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
 +[`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
 +[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
 +[`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
 +[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
++[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take
 +[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
 +[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
 +[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
 +[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
 +[`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn
 +[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
 +[`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
 +[`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
 +[`negative_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#negative_feature_names
 +[`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop
 +[`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self
 +[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
 +[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
 +[`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding
 +[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
 +[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
 +[`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty
 +[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
 +[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
 +[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
 +[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
 +[`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes
 +[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
 +[`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_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
 +[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
 +[`option_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_filter_map
 +[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
 +[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
 +[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
 +[`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
 +[`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call
 +[`or_then_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_then_unwrap
 +[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
 +[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
 +[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
 +[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
 +[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
 +[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
 +[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
 +[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
 +[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
 +[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
 +[`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl
 +[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
 +[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr
 +[`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
 +[`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
 +[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
 +[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
 +[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr
 +[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
 +[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
 +[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
++[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
 +[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
 +[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
 +[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
 +[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
 +[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
 +[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
 +[`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex
 +[`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
 +[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
 +[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
 +[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
 +[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
 +[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
 +[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
 +[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
 +[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
 +[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
 +[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
 +[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
 +[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
 +[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
 +[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
 +[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
 +[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
 +[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
 +[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
 +[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
 +[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
 +[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
 +[`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err
 +[`return_self_not_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_self_not_must_use
 +[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
 +[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
 +[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
 +[`same_name_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_name_method
 +[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
 +[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
 +[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
 +[`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files
 +[`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
 +[`separated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#separated_literal_suffix
 +[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
 +[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
 +[`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same
 +[`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
 +[`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement
 +[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq
 +[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
 +[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
 +[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
 +[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names
 +[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
 +[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
 +[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
 +[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
 +[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
 +[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
 +[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
 +[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
 +[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
 +[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
 +[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
 +[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
 +[`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars
 +[`string_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes
 +[`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
 +[`string_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_slice
 +[`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
 +[`strlen_on_c_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#strlen_on_c_strings
 +[`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
 +[`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops
 +[`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
 +[`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting
 +[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
 +[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
 +[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
 +[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
 +[`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn
 +[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
 +[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
 +[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
 +[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
 +[`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
 +[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
 +[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
 +[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
 +[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
 +[`trailing_empty_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#trailing_empty_array
 +[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds
 +[`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str
 +[`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int
 +[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
 +[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
 +[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
 +[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
 +[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
 +[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
 +[`transmute_undefined_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_undefined_repr
 +[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
 +[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
++[`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
 +[`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_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
 +[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
 +[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
 +[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result
 +[`unwrap_or_else_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_or_else_default
 +[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used
 +[`upper_case_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms
 +[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
 +[`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
 +[`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding
 +[`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref
 +[`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute
 +[`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
 +[`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format
 +[`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq
 +[`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute
 +[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
 +[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box
 +[`vec_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push
 +[`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero
 +[`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask
 +[`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads
 +[`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons
 +[`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition
 +[`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop
 +[`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator
 +[`wildcard_dependencies`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_dependencies
 +[`wildcard_enum_match_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_enum_match_arm
 +[`wildcard_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports
 +[`wildcard_in_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_in_or_patterns
 +[`write_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_literal
 +[`write_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_with_newline
 +[`writeln_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#writeln_empty_string
 +[`wrong_pub_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_pub_self_convention
 +[`wrong_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention
 +[`wrong_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_transmute
 +[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
 +[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
 +[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
 +[`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values
 +[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
 +<!-- end autogenerated links to lint list -->
index 81faa5fe5e14823474fa0a90661da56ea75afa1e,0000000000000000000000000000000000000000..2cfbcea5034e653beb818b1be5dc89b94b856d2c
mode 100644,000000..100644
--- /dev/null
@@@ -1,20 -1,0 +1,21 @@@
 +[package]
 +name = "clippy_dev"
 +version = "0.0.1"
 +edition = "2021"
 +
 +[dependencies]
++aho-corasick = "0.7"
 +clap = "2.33"
 +indoc = "1.0"
 +itertools = "0.10.1"
 +opener = "0.5"
 +shell-escape = "0.1"
 +tempfile = "3.2"
 +walkdir = "2.3"
 +
 +[features]
 +deny-warnings = []
 +
 +[package.metadata.rust-analyzer]
 +# This package uses #[feature(rustc_private)]
 +rustc_private = true
index 9c6d754b400fc0f41595cfd8a4a745a1de926dd9,0000000000000000000000000000000000000000..81e807cf10c7c3a489e3a601b569714b160fbf66
mode 100644,000000..100644
--- /dev/null
@@@ -1,56 -1,0 +1,57 @@@
++#![feature(let_chains)]
 +#![feature(let_else)]
 +#![feature(once_cell)]
 +#![feature(rustc_private)]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +extern crate rustc_lexer;
 +
 +use std::path::PathBuf;
 +
 +pub mod bless;
 +pub mod fmt;
 +pub mod lint;
 +pub mod new_lint;
 +pub mod serve;
 +pub mod setup;
 +pub mod update_lints;
 +
 +#[cfg(not(windows))]
 +static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
 +#[cfg(windows)]
 +static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe";
 +
 +/// Returns the path to the `cargo-clippy` binary
 +#[must_use]
 +pub fn cargo_clippy_path() -> PathBuf {
 +    let mut path = std::env::current_exe().expect("failed to get current executable name");
 +    path.set_file_name(CARGO_CLIPPY_EXE);
 +    path
 +}
 +
 +/// Returns the path to the Clippy project directory
 +///
 +/// # Panics
 +///
 +/// Panics if the current directory could not be retrieved, there was an error reading any of the
 +/// Cargo.toml files or ancestor directory is the clippy root directory
 +#[must_use]
 +pub fn clippy_project_root() -> PathBuf {
 +    let current_dir = std::env::current_dir().unwrap();
 +    for path in current_dir.ancestors() {
 +        let result = std::fs::read_to_string(path.join("Cargo.toml"));
 +        if let Err(err) = &result {
 +            if err.kind() == std::io::ErrorKind::NotFound {
 +                continue;
 +            }
 +        }
 +
 +        let content = result.unwrap();
 +        if content.contains("[package]\nname = \"clippy\"") {
 +            return path.to_path_buf();
 +        }
 +    }
 +    panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
 +}
index b1fe35a0243f04e4887cdb020edd0de32040c7eb,0000000000000000000000000000000000000000..ebf8f38d4906eae4d15eaf1c3ef6179295216553
mode 100644,000000..100644
--- /dev/null
@@@ -1,246 -1,0 +1,310 @@@
-                 update_lints::run(update_lints::UpdateMode::Check);
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
 +use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints};
 +use indoc::indoc;
 +fn main() {
 +    let matches = get_clap_config();
 +
 +    match matches.subcommand() {
 +        ("bless", Some(matches)) => {
 +            bless::bless(matches.is_present("ignore-timestamp"));
 +        },
 +        ("fmt", Some(matches)) => {
 +            fmt::run(matches.is_present("check"), matches.is_present("verbose"));
 +        },
 +        ("update_lints", Some(matches)) => {
 +            if matches.is_present("print-only") {
 +                update_lints::print_lints();
 +            } else if matches.is_present("check") {
-                 update_lints::run(update_lints::UpdateMode::Change);
++                update_lints::update(update_lints::UpdateMode::Check);
 +            } else {
-                 Ok(_) => update_lints::run(update_lints::UpdateMode::Change),
++                update_lints::update(update_lints::UpdateMode::Change);
 +            }
 +        },
 +        ("new_lint", Some(matches)) => {
 +            match new_lint::create(
 +                matches.value_of("pass"),
 +                matches.value_of("name"),
 +                matches.value_of("category"),
 +                matches.is_present("msrv"),
 +            ) {
-             ("intellij", Some(matches)) => setup::intellij::setup_rustc_src(
-                 matches
-                     .value_of("rustc-repo-path")
-                     .expect("this field is mandatory and therefore always valid"),
-             ),
-             ("git-hook", Some(matches)) => setup::git_hook::install_hook(matches.is_present("force-override")),
-             ("vscode-tasks", Some(matches)) => setup::vscode::install_tasks(matches.is_present("force-override")),
++                Ok(_) => update_lints::update(update_lints::UpdateMode::Change),
 +                Err(e) => eprintln!("Unable to create lint: {}", e),
 +            }
 +        },
 +        ("setup", Some(sub_command)) => match sub_command.subcommand() {
++            ("intellij", Some(matches)) => {
++                if matches.is_present("remove") {
++                    setup::intellij::remove_rustc_src();
++                } else {
++                    setup::intellij::setup_rustc_src(
++                        matches
++                            .value_of("rustc-repo-path")
++                            .expect("this field is mandatory and therefore always valid"),
++                    );
++                }
++            },
++            ("git-hook", Some(matches)) => {
++                if matches.is_present("remove") {
++                    setup::git_hook::remove_hook();
++                } else {
++                    setup::git_hook::install_hook(matches.is_present("force-override"));
++                }
++            },
++            ("vscode-tasks", Some(matches)) => {
++                if matches.is_present("remove") {
++                    setup::vscode::remove_tasks();
++                } else {
++                    setup::vscode::install_tasks(matches.is_present("force-override"));
++                }
++            },
 +            _ => {},
 +        },
 +        ("remove", Some(sub_command)) => match sub_command.subcommand() {
 +            ("git-hook", Some(_)) => setup::git_hook::remove_hook(),
 +            ("intellij", Some(_)) => setup::intellij::remove_rustc_src(),
 +            ("vscode-tasks", Some(_)) => setup::vscode::remove_tasks(),
 +            _ => {},
 +        },
 +        ("serve", Some(matches)) => {
 +            let port = matches.value_of("port").unwrap().parse().unwrap();
 +            let lint = matches.value_of("lint");
 +            serve::run(port, lint);
 +        },
 +        ("lint", Some(matches)) => {
 +            let path = matches.value_of("path").unwrap();
 +            lint::run(path);
 +        },
++        ("rename_lint", Some(matches)) => {
++            let old_name = matches.value_of("old_name").unwrap();
++            let new_name = matches.value_of("new_name").unwrap_or(old_name);
++            let uplift = matches.is_present("uplift");
++            update_lints::rename(old_name, new_name, uplift);
++        },
 +        _ => {},
 +    }
 +}
 +
 +fn get_clap_config<'a>() -> ArgMatches<'a> {
 +    App::new("Clippy developer tooling")
 +        .setting(AppSettings::ArgRequiredElseHelp)
 +        .subcommand(
 +            SubCommand::with_name("bless")
 +                .about("bless the test output changes")
 +                .arg(
 +                    Arg::with_name("ignore-timestamp")
 +                        .long("ignore-timestamp")
 +                        .help("Include files updated before clippy was built"),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("fmt")
 +                .about("Run rustfmt on all projects and tests")
 +                .arg(
 +                    Arg::with_name("check")
 +                        .long("check")
 +                        .help("Use the rustfmt --check option"),
 +                )
 +                .arg(
 +                    Arg::with_name("verbose")
 +                        .short("v")
 +                        .long("verbose")
 +                        .help("Echo commands run"),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("update_lints")
 +                .about("Updates lint registration and information from the source code")
 +                .long_about(
 +                    "Makes sure that:\n \
 +                 * the lint count in README.md is correct\n \
 +                 * the changelog contains markdown link references at the bottom\n \
 +                 * all lint groups include the correct lints\n \
 +                 * lint modules in `clippy_lints/*` are visible in `src/lifb.rs` via `pub mod`\n \
 +                 * all lints are registered in the lint store",
 +                )
 +                .arg(Arg::with_name("print-only").long("print-only").help(
 +                    "Print a table of lints to STDOUT. \
 +                 This does not include deprecated and internal lints. \
 +                 (Does not modify any files)",
 +                ))
 +                .arg(
 +                    Arg::with_name("check")
 +                        .long("check")
 +                        .help("Checks that `cargo dev update_lints` has been run. Used on CI."),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("new_lint")
 +                .about("Create new lint and run `cargo dev update_lints`")
 +                .arg(
 +                    Arg::with_name("pass")
 +                        .short("p")
 +                        .long("pass")
 +                        .help("Specify whether the lint runs during the early or late pass")
 +                        .takes_value(true)
 +                        .possible_values(&["early", "late"])
 +                        .required(true),
 +                )
 +                .arg(
 +                    Arg::with_name("name")
 +                        .short("n")
 +                        .long("name")
 +                        .help("Name of the new lint in snake case, ex: fn_too_long")
 +                        .takes_value(true)
 +                        .required(true),
 +                )
 +                .arg(
 +                    Arg::with_name("category")
 +                        .short("c")
 +                        .long("category")
 +                        .help("What category the lint belongs to")
 +                        .default_value("nursery")
 +                        .possible_values(&[
 +                            "style",
 +                            "correctness",
 +                            "suspicious",
 +                            "complexity",
 +                            "perf",
 +                            "pedantic",
 +                            "restriction",
 +                            "cargo",
 +                            "nursery",
 +                            "internal",
 +                            "internal_warn",
 +                        ])
 +                        .takes_value(true),
 +                )
 +                .arg(
 +                    Arg::with_name("msrv")
 +                        .long("msrv")
 +                        .help("Add MSRV config code to the lint"),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("setup")
 +                .about("Support for setting up your personal development environment")
 +                .setting(AppSettings::ArgRequiredElseHelp)
 +                .subcommand(
 +                    SubCommand::with_name("intellij")
 +                        .about("Alter dependencies so Intellij Rust can find rustc internals")
++                        .arg(
++                            Arg::with_name("remove")
++                                .long("remove")
++                                .help("Remove the dependencies added with 'cargo dev setup intellij'")
++                                .required(false),
++                        )
 +                        .arg(
 +                            Arg::with_name("rustc-repo-path")
 +                                .long("repo-path")
 +                                .short("r")
 +                                .help("The path to a rustc repo that will be used for setting the dependencies")
 +                                .takes_value(true)
 +                                .value_name("path")
++                                .conflicts_with("remove")
 +                                .required(true),
 +                        ),
 +                )
 +                .subcommand(
 +                    SubCommand::with_name("git-hook")
 +                        .about("Add a pre-commit git hook that formats your code to make it look pretty")
++                        .arg(
++                            Arg::with_name("remove")
++                                .long("remove")
++                                .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'")
++                                .required(false),
++                        )
 +                        .arg(
 +                            Arg::with_name("force-override")
 +                                .long("force-override")
 +                                .short("f")
 +                                .help("Forces the override of an existing git pre-commit hook")
 +                                .required(false),
 +                        ),
 +                )
 +                .subcommand(
 +                    SubCommand::with_name("vscode-tasks")
 +                        .about("Add several tasks to vscode for formatting, validation and testing")
++                        .arg(
++                            Arg::with_name("remove")
++                                .long("remove")
++                                .help("Remove the tasks added with 'cargo dev setup vscode-tasks'")
++                                .required(false),
++                        )
 +                        .arg(
 +                            Arg::with_name("force-override")
 +                                .long("force-override")
 +                                .short("f")
 +                                .help("Forces the override of existing vscode tasks")
 +                                .required(false),
 +                        ),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("remove")
 +                .about("Support for undoing changes done by the setup command")
 +                .setting(AppSettings::ArgRequiredElseHelp)
 +                .subcommand(SubCommand::with_name("git-hook").about("Remove any existing pre-commit git hook"))
 +                .subcommand(SubCommand::with_name("vscode-tasks").about("Remove any existing vscode tasks"))
 +                .subcommand(
 +                    SubCommand::with_name("intellij")
 +                        .about("Removes rustc source paths added via `cargo dev setup intellij`"),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("serve")
 +                .about("Launch a local 'ALL the Clippy Lints' website in a browser")
 +                .arg(
 +                    Arg::with_name("port")
 +                        .long("port")
 +                        .short("p")
 +                        .help("Local port for the http server")
 +                        .default_value("8000")
 +                        .validator_os(serve::validate_port),
 +                )
 +                .arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("lint")
 +                .about("Manually run clippy on a file or package")
 +                .after_help(indoc! {"
 +                    EXAMPLES
 +                        Lint a single file:
 +                            cargo dev lint tests/ui/attrs.rs
 +
 +                        Lint a package directory:
 +                            cargo dev lint tests/ui-cargo/wildcard_dependencies/fail
 +                            cargo dev lint ~/my-project
 +                "})
 +                .arg(
 +                    Arg::with_name("path")
 +                        .required(true)
 +                        .help("The path to a file or package directory to lint"),
 +                ),
 +        )
++        .subcommand(
++            SubCommand::with_name("rename_lint")
++                .about("Renames the given lint")
++                .arg(
++                    Arg::with_name("old_name")
++                        .index(1)
++                        .required(true)
++                        .help("The name of the lint to rename"),
++                )
++                .arg(
++                    Arg::with_name("new_name")
++                        .index(2)
++                        .required_unless("uplift")
++                        .help("The new name of the lint"),
++                )
++                .arg(
++                    Arg::with_name("uplift")
++                        .long("uplift")
++                        .help("This lint will be uplifted into rustc"),
++                ),
++        )
 +        .get_matches()
 +}
index 7a3fd1317619e39d3021f8dafe8e0281dc4a99a1,0000000000000000000000000000000000000000..10f67d301f887d5893974cdf2e8292da2f230007
mode 100644,000000..100644
--- /dev/null
@@@ -1,321 -1,0 +1,323 @@@
-     result.push_str(&format!(
 +use crate::clippy_project_root;
 +use indoc::indoc;
++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,
 +    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<&str>, lint_name: Option<&str>, category: Option<&str>, msrv: bool) -> io::Result<()> {
 +    let lint = LintData {
 +        pass: pass.expect("`pass` argument is validated by clap"),
 +        name: lint_name.expect("`name` argument is validated by clap"),
 +        category: category.expect("`category` argument is validated by clap"),
 +        project_root: clippy_project_root(),
 +    };
 +
 +    create_lint(&lint, msrv).context("Unable to create lint implementation")?;
 +    create_test(&lint).context("Unable to create a test for the new lint")?;
 +    add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")
 +}
 +
 +fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
 +    let lint_contents = get_lint_file_contents(lint, enable_msrv);
 +
 +    let lint_path = format!("clippy_lints/src/{}.rs", lint.name);
 +    write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())
 +}
 +
 +fn create_test(lint: &LintData<'_>) -> io::Result<()> {
 +    fn create_project_layout<P: Into<PathBuf>>(lint_name: &str, location: P, case: &str, hint: &str) -> io::Result<()> {
 +        let mut path = location.into().join(case);
 +        fs::create_dir(&path)?;
 +        write_file(path.join("Cargo.toml"), get_manifest_contents(lint_name, hint))?;
 +
 +        path.push("src");
 +        fs::create_dir(&path)?;
 +        let header = format!("// compile-flags: --crate-name={}", lint_name);
 +        write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?;
 +
 +        Ok(())
 +    }
 +
 +    if lint.category == "cargo" {
 +        let relative_test_dir = format!("tests/ui-cargo/{}", lint.name);
 +        let test_dir = lint.project_root.join(relative_test_dir);
 +        fs::create_dir(&test_dir)?;
 +
 +        create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?;
 +        create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint")
 +    } else {
 +        let test_path = format!("tests/ui/{}.rs", lint.name);
 +        let test_contents = get_test_file_contents(lint.name, None);
 +        write_file(lint.project_root.join(test_path), test_contents)
 +    }
 +}
 +
 +fn 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::from("")
 +            } else {
 +                [&s[0..1].to_uppercase(), &s[1..]].concat()
 +            }
 +        })
 +        .collect()
 +}
 +
 +fn get_stabilisation_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 version = get_stabilisation_version();
 +    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,
 +        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 = version,
 +        name_upper = name_upper,
 +        category = category,
++    );
 +
 +    result.push_str(&if enable_msrv {
 +        format!(
 +            indoc! {"
 +                pub struct {name_camel} {{
 +                    msrv: Option<RustcVersion>,
 +                }}
 +
 +                impl {name_camel} {{
 +                    #[must_use]
 +                    pub fn new(msrv: Option<RustcVersion>) -> Self {{
 +                        Self {{ msrv }}
 +                    }}
 +                }}
 +
 +                impl_lint_pass!({name_camel} => [{name_upper}]);
 +
 +                impl {pass_type}{pass_lifetimes} for {name_camel} {{
 +                    extract_msrv_attr!({context_import});
 +                }}
 +
 +                // TODO: 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
 +}
 +
 +#[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 a1e4dd103b88bfa708948c44ba7cd8a48ec1a346,0000000000000000000000000000000000000000..f691ae4fa45da22e67d8700894b2365db3d4fad2
mode 100644,000000..100644
--- /dev/null
@@@ -1,23 -1,0 +1,23 @@@
- /// This is useful to ensure that setups only modify Clippys resources. The verification
 +pub mod git_hook;
 +pub mod intellij;
 +pub mod vscode;
 +
 +use std::path::Path;
 +
 +const CLIPPY_DEV_DIR: &str = "clippy_dev";
 +
 +/// This function verifies that the tool is being executed in the clippy directory.
-         eprintln!("error: unable to verify that the working directory is clippys directory");
++/// This is useful to ensure that setups only modify Clippy's resources. The verification
 +/// is done by checking that `clippy_dev` is a sub directory of the current directory.
 +///
 +/// It will print an error message and return `false` if the directory could not be
 +/// verified.
 +fn verify_inside_clippy_dir() -> bool {
 +    let path = Path::new(CLIPPY_DEV_DIR);
 +    if path.exists() && path.is_dir() {
 +        true
 +    } else {
++        eprintln!("error: unable to verify that the working directory is clippy's directory");
 +        false
 +    }
 +}
index 59db51fbfac5149fec5158a137c91e94fa86bf4b,0000000000000000000000000000000000000000..1a6a4336da27e2c2955fcf48a828e7138cd9a95b
mode 100644,000000..100644
--- /dev/null
@@@ -1,604 -1,0 +1,947 @@@
- use core::fmt::Write;
++use aho_corasick::AhoCorasickBuilder;
++use core::fmt::Write as _;
 +use itertools::Itertools;
 +use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
- use std::collections::HashMap;
++use std::collections::{HashMap, HashSet};
 +use std::ffi::OsStr;
 +use std::fs;
- use std::path::Path;
- use walkdir::WalkDir;
++use std::io::{self, Read as _, Seek as _, Write as _};
++use std::path::{Path, PathBuf};
++use walkdir::{DirEntry, WalkDir};
 +
 +use crate::clippy_project_root;
 +
 +const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\
 +     // Use that command to update this file and do not edit by hand.\n\
 +     // Manual edits will be overwritten.\n\n";
 +
 +const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
 +
 +#[derive(Clone, Copy, PartialEq)]
 +pub enum UpdateMode {
 +    Check,
 +    Change,
 +}
 +
 +/// Runs the `update_lints` command.
 +///
 +/// This updates various generated values from the lint source code.
 +///
 +/// `update_mode` indicates if the files should be updated or if updates should be checked for.
 +///
 +/// # Panics
 +///
 +/// Panics if a file path could not read from or then written to
- #[allow(clippy::too_many_lines)]
- pub fn run(update_mode: UpdateMode) {
-     let (lints, deprecated_lints) = gather_all();
++pub fn update(update_mode: UpdateMode) {
++    let (lints, deprecated_lints, renamed_lints) = gather_all();
++    generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints);
++}
 +
-     let internal_lints = Lint::internal_lints(&lints);
-     let usable_lints = Lint::usable_lints(&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("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))
 +                .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),
++        &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 (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");
++}
++
++/// 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(|| 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, Debug)]
 +struct Lint {
 +    name: String,
 +    group: String,
 +    desc: String,
 +    module: String,
 +}
 +
 +impl Lint {
 +    #[must_use]
 +    fn new(name: &str, group: &str, desc: &str, module: &str) -> Self {
 +        Self {
 +            name: name.to_lowercase(),
 +            group: group.into(),
 +            desc: remove_line_splices(desc),
 +            module: module.into(),
 +        }
 +    }
 +
 +    /// 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, Debug)]
 +struct DeprecatedLint {
 +    name: String,
 +    reason: String,
 +}
 +impl DeprecatedLint {
 +    fn new(name: &str, reason: &str) -> Self {
 +        Self {
 +            name: name.to_lowercase(),
 +            reason: remove_line_splices(reason),
 +        }
 +    }
 +}
 +
++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();
 +
-     output.push_str(&format!(
-         "store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![\n",
++    let _ = writeln!(
++        output,
++        "store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![",
 +        group_name
-     ));
++    );
 +    for (module, name) in details {
-         output.push_str(&format!("    LintId::of({}::{}),\n", module, name));
++        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 {
-         output.push_str(&format!(
++        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");
 +        }
-         output.push_str(&format!("    {}::{},\n", module_name, lint_name));
++        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>) {
++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 root_path = clippy_project_root().join("clippy_lints/src");
++    let mut renamed_lints = Vec::with_capacity(50);
 +
-     for (rel_path, file) in WalkDir::new(&root_path)
-         .into_iter()
-         .map(Result::unwrap)
-         .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
-         .map(|f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f))
-     {
++    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)
 +        };
 +
-         if module == "deprecated_lints" {
-             parse_deprecated_contents(&contents, &mut deprecated_lints);
-         } else {
-             parse_contents(&contents, module, &mut lints);
++        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)
++    (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((TokenKind::$token $({$($fields)*})?, _x)) = $iter.next() {
 +                _x
 +            } else {
 +                continue;
 +            };)*
 +            #[allow(clippy::unused_unit)]
 +            { ($($($capture,)?)*) }
 +        }
 +    }
 +}
 +
 +/// 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;
 +        offset = range.end;
 +        (t.kind, &contents[range])
 +    });
 +
 +    while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_clippy_lint") {
 +        let mut iter = iter
 +            .by_ref()
 +            .filter(|&(kind, _)| !matches!(kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
 +        // matches `!{`
 +        match_tokens!(iter, Bang OpenBrace);
 +        match iter.next() {
 +            // #[clippy::version = "version"] pub
 +            Some((TokenKind::Pound, _)) => {
 +                match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
 +            },
 +            // pub
 +            Some((TokenKind::Ident, _)) => (),
 +            _ => continue,
 +        }
 +        let (name, group, desc) = match_tokens!(
 +            iter,
 +            // LINT_NAME
 +            Ident(name) Comma
 +            // group,
 +            Ident(group) Comma
 +            // "description" }
 +            Literal{..}(desc) CloseBrace
 +        );
 +        lints.push(Lint::new(name, group, desc, module));
 +    }
 +}
 +
 +/// 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;
 +        offset = range.end;
 +        (t.kind, &contents[range])
 +    });
 +    while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_deprecated_lint") {
 +        let mut iter = iter
 +            .by_ref()
 +            .filter(|&(kind, _)| !matches!(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)
 +            // }
 +            CloseBrace
 +        );
 +        lints.push(DeprecatedLint::new(name, reason));
 +    }
 +}
 +
++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;
++            offset = range.end;
++            (t.kind, &line[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);
 +
 +        let expected = vec![
 +            Lint::new("ptr_arg", "style", "\"really long text\"", "module_name"),
 +            Lint::new("doc_markdown", "pedantic", "\"single line\"", "module_name"),
 +        ];
 +        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);
 +
 +        let expected = vec![DeprecatedLint::new(
 +            "should_assert_eq",
 +            "\"`assert!()` will be more flexible with RFC 2011\"",
 +        )];
 +        assert_eq!(expected, result);
 +    }
 +
 +    #[test]
 +    fn test_usable_lints() {
 +        let lints = vec![
 +            Lint::new("should_assert_eq2", "Not Deprecated", "\"abc\"", "module_name"),
 +            Lint::new("should_assert_eq2", "internal", "\"abc\"", "module_name"),
 +            Lint::new("should_assert_eq2", "internal_style", "\"abc\"", "module_name"),
 +        ];
 +        let expected = vec![Lint::new(
 +            "should_assert_eq2",
 +            "Not Deprecated",
 +            "\"abc\"",
 +            "module_name",
 +        )];
 +        assert_eq!(expected, Lint::usable_lints(&lints));
 +    }
 +
 +    #[test]
 +    fn test_by_lint_group() {
 +        let lints = vec![
 +            Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
 +            Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name"),
 +            Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"),
 +        ];
 +        let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
 +        expected.insert(
 +            "group1".to_string(),
 +            vec![
 +                Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
 +                Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"),
 +            ],
 +        );
 +        expected.insert(
 +            "group2".to_string(),
 +            vec![Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name")],
 +        );
 +        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\""),
 +            DeprecatedLint::new("another_deprecated", "\"will be removed\""),
 +        ];
 +
 +        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"),
 +            Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
 +            Lint::new("internal", "internal_style", "\"abc\"", "module_name"),
 +        ];
 +        let expected = GENERATED_FILE_COMMENT.to_string()
 +            + &[
 +                "store.register_group(true, \"clippy::group1\", Some(\"clippy_group1\"), vec![",
 +                "    LintId::of(module_name::ABC),",
 +                "    LintId::of(module_name::INTERNAL),",
 +                "    LintId::of(module_name::SHOULD_ASSERT_EQ),",
 +                "])",
 +            ]
 +            .join("\n")
 +            + "\n";
 +
 +        let result = gen_lint_group_list("group1", lints.iter());
 +
 +        assert_eq!(expected, result);
 +    }
 +}
index d0b03c0a9c276e644e89c6bfa22499cfdce7e052,0000000000000000000000000000000000000000..8b0e11cb802eeae0996551e3c588f45688d096bc
mode 100644,000000..100644
--- /dev/null
@@@ -1,721 -1,0 +1,724 @@@
- use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
 +//! checks for attributes
 +
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::macros::{is_panic, macro_backtrace};
 +use clippy_utils::msrvs;
 +use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
 +use clippy_utils::{extract_msrv_attr, meets_msrv};
 +use if_chain::if_chain;
-                             // permit `unused_imports`, `deprecated`, `unreachable_pub`,
-                             // `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items
-                             // and `unused_imports` for `extern crate` items with `macro_use`
++use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MacArgs, MacArgsEq, MetaItemKind, NestedMetaItem};
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
 +};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +use rustc_span::sym;
 +use rustc_span::symbol::Symbol;
 +use semver::Version;
 +
 +static UNIX_SYSTEMS: &[&str] = &[
 +    "android",
 +    "dragonfly",
 +    "emscripten",
 +    "freebsd",
 +    "fuchsia",
 +    "haiku",
 +    "illumos",
 +    "ios",
 +    "l4re",
 +    "linux",
 +    "macos",
 +    "netbsd",
 +    "openbsd",
 +    "redox",
 +    "solaris",
 +    "vxworks",
 +];
 +
 +// NOTE: windows is excluded from the list because it's also a valid target family.
 +static NON_UNIX_SYSTEMS: &[&str] = &["hermit", "none", "wasi"];
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for items annotated with `#[inline(always)]`,
 +    /// unless the annotated function is empty or simply panics.
 +    ///
 +    /// ### Why is this bad?
 +    /// While there are valid uses of this annotation (and once
 +    /// you know when to use it, by all means `allow` this lint), it's a common
 +    /// newbie-mistake to pepper one's code with it.
 +    ///
 +    /// As a rule of thumb, before slapping `#[inline(always)]` on a function,
 +    /// measure if that additional function call really affects your runtime profile
 +    /// sufficiently to make up for the increase in compile time.
 +    ///
 +    /// ### Known problems
 +    /// False positives, big time. This lint is meant to be
 +    /// deactivated by everyone doing serious performance work. This means having
 +    /// done the measurement.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// #[inline(always)]
 +    /// fn not_quite_hot_code(..) { ... }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub INLINE_ALWAYS,
 +    pedantic,
 +    "use of `#[inline(always)]`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `extern crate` and `use` items annotated with
 +    /// lint attributes.
 +    ///
 +    /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`,
 +    /// `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and
 +    /// `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on
 +    /// `extern crate` items with a `#[macro_use]` attribute.
 +    ///
 +    /// ### Why is this bad?
 +    /// Lint attributes have no effect on crate imports. Most
 +    /// likely a `!` was forgotten.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// // Bad
 +    /// #[deny(dead_code)]
 +    /// extern crate foo;
 +    /// #[forbid(dead_code)]
 +    /// use foo::bar;
 +    ///
 +    /// // Ok
 +    /// #[allow(unused_imports)]
 +    /// use foo::baz;
 +    /// #[allow(unused_imports)]
 +    /// #[macro_use]
 +    /// extern crate baz;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USELESS_ATTRIBUTE,
 +    correctness,
 +    "use of lint attributes on `extern crate` items"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `#[deprecated]` annotations with a `since`
 +    /// field that is not a valid semantic version.
 +    ///
 +    /// ### Why is this bad?
 +    /// For checking the version of the deprecation, it must be
 +    /// a valid semver. Failing that, the contained information is useless.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #[deprecated(since = "forever")]
 +    /// fn something_else() { /* ... */ }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DEPRECATED_SEMVER,
 +    correctness,
 +    "use of `#[deprecated(since = \"x\")]` where x is not semver"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for empty lines after outer attributes
 +    ///
 +    /// ### Why is this bad?
 +    /// Most likely the attribute was meant to be an inner attribute using a '!'.
 +    /// If it was meant to be an outer attribute, then the following item
 +    /// should not be separated by empty lines.
 +    ///
 +    /// ### Known problems
 +    /// Can cause false positives.
 +    ///
 +    /// From the clippy side it's difficult to detect empty lines between an attributes and the
 +    /// following item because empty lines and comments are not part of the AST. The parsing
 +    /// currently works for basic cases but is not perfect.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Good (as inner attribute)
 +    /// #![allow(dead_code)]
 +    ///
 +    /// fn this_is_fine() { }
 +    ///
 +    /// // Bad
 +    /// #[allow(dead_code)]
 +    ///
 +    /// fn not_quite_good_code() { }
 +    ///
 +    /// // Good (as outer attribute)
 +    /// #[allow(dead_code)]
 +    /// fn this_is_fine_too() { }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EMPTY_LINE_AFTER_OUTER_ATTR,
 +    nursery,
 +    "empty line after outer attribute"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.
 +    ///
 +    /// ### Why is this bad?
 +    /// Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust.
 +    /// These lints should only be enabled on a lint-by-lint basis and with careful consideration.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust
 +    /// #![deny(clippy::restriction)]
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust
 +    /// #![deny(clippy::as_conversions)]
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub BLANKET_CLIPPY_RESTRICTION_LINTS,
 +    suspicious,
 +    "enabling the complete restriction group"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it
 +    /// with `#[rustfmt::skip]`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690))
 +    /// are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes.
 +    ///
 +    /// ### Known problems
 +    /// This lint doesn't detect crate level inner attributes, because they get
 +    /// processed before the PreExpansionPass lints get executed. See
 +    /// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust
 +    /// #[cfg_attr(rustfmt, rustfmt_skip)]
 +    /// fn main() { }
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust
 +    /// #[rustfmt::skip]
 +    /// fn main() { }
 +    /// ```
 +    #[clippy::version = "1.32.0"]
 +    pub DEPRECATED_CFG_ATTR,
 +    complexity,
 +    "usage of `cfg_attr(rustfmt)` instead of tool attributes"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for cfg attributes having operating systems used in target family position.
 +    ///
 +    /// ### Why is this bad?
 +    /// The configuration option will not be recognised and the related item will not be included
 +    /// by the conditional compilation engine.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust
 +    /// #[cfg(linux)]
 +    /// fn conditional() { }
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust
 +    /// #[cfg(target_os = "linux")]
 +    /// fn conditional() { }
 +    /// ```
 +    ///
 +    /// Or:
 +    /// ```rust
 +    /// #[cfg(unix)]
 +    /// fn conditional() { }
 +    /// ```
 +    /// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.
 +    #[clippy::version = "1.45.0"]
 +    pub MISMATCHED_TARGET_OS,
 +    correctness,
 +    "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for attributes that allow lints without a reason.
 +    ///
 +    /// (This requires the `lint_reasons` feature)
 +    ///
 +    /// ### Why is this bad?
 +    /// Allowing a lint should always have a reason. This reason should be documented to
 +    /// ensure that others understand the reasoning
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust
 +    /// #![feature(lint_reasons)]
 +    ///
 +    /// #![allow(clippy::some_lint)]
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust
 +    /// #![feature(lint_reasons)]
 +    ///
 +    /// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub ALLOW_ATTRIBUTES_WITHOUT_REASON,
 +    restriction,
 +    "ensures that all `allow` and `expect` attributes have a reason"
 +}
 +
 +declare_lint_pass!(Attributes => [
 +    ALLOW_ATTRIBUTES_WITHOUT_REASON,
 +    INLINE_ALWAYS,
 +    DEPRECATED_SEMVER,
 +    USELESS_ATTRIBUTE,
 +    BLANKET_CLIPPY_RESTRICTION_LINTS,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Attributes {
 +    fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) {
 +        if let Some(items) = &attr.meta_item_list() {
 +            if let Some(ident) = attr.ident() {
 +                if is_lint_level(ident.name) {
 +                    check_clippy_lint_names(cx, ident.name, items);
 +                }
 +                if matches!(ident.name, sym::allow | sym::expect) {
 +                    check_lint_reason(cx, ident.name, items, attr);
 +                }
 +                if items.is_empty() || !attr.has_name(sym::deprecated) {
 +                    return;
 +                }
 +                for item in items {
 +                    if_chain! {
 +                        if let NestedMetaItem::MetaItem(mi) = &item;
 +                        if let MetaItemKind::NameValue(lit) = &mi.kind;
 +                        if mi.has_name(sym::since);
 +                        then {
 +                            check_semver(cx, item.span(), lit);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        if is_relevant_item(cx, item) {
 +            check_attrs(cx, item.span, item.ident.name, attrs);
 +        }
 +        match item.kind {
 +            ItemKind::ExternCrate(..) | ItemKind::Use(..) => {
 +                let skip_unused_imports = attrs.iter().any(|attr| attr.has_name(sym::macro_use));
 +
 +                for attr in attrs {
 +                    if in_external_macro(cx.sess(), attr.span) {
 +                        return;
 +                    }
 +                    if let Some(lint_list) = &attr.meta_item_list() {
 +                        if attr.ident().map_or(false, |ident| is_lint_level(ident.name)) {
-                                             || extract_clippy_lint(lint)
-                                                 .map_or(false, |s| s.as_str() == "wildcard_imports")
-                                             || extract_clippy_lint(lint)
-                                                 .map_or(false, |s| s.as_str() == "enum_glob_use")
 +                            for lint in lint_list {
 +                                match item.kind {
 +                                    ItemKind::Use(..) => {
 +                                        if is_word(lint, sym!(unused_imports))
 +                                            || is_word(lint, sym::deprecated)
 +                                            || is_word(lint, sym!(unreachable_pub))
 +                                            || is_word(lint, sym!(unused))
++                                            || extract_clippy_lint(lint).map_or(false, |s| {
++                                                matches!(
++                                                    s.as_str(),
++                                                    "wildcard_imports" | "enum_glob_use" | "redundant_pub_crate",
++                                                )
++                                            })
 +                                        {
 +                                            return;
 +                                        }
 +                                    },
 +                                    ItemKind::ExternCrate(..) => {
 +                                        if is_word(lint, sym!(unused_imports)) && skip_unused_imports {
 +                                            return;
 +                                        }
 +                                        if is_word(lint, sym!(unused_extern_crates)) {
 +                                            return;
 +                                        }
 +                                    },
 +                                    _ => {},
 +                                }
 +                            }
 +                            let line_span = first_line_of_span(cx, attr.span);
 +
 +                            if let Some(mut sugg) = snippet_opt(cx, line_span) {
 +                                if sugg.contains("#[") {
 +                                    span_lint_and_then(
 +                                        cx,
 +                                        USELESS_ATTRIBUTE,
 +                                        line_span,
 +                                        "useless lint attribute",
 +                                        |diag| {
 +                                            sugg = sugg.replacen("#[", "#![", 1);
 +                                            diag.span_suggestion(
 +                                                line_span,
 +                                                "if you just forgot a `!`, use",
 +                                                sugg,
 +                                                Applicability::MaybeIncorrect,
 +                                            );
 +                                        },
 +                                    );
 +                                }
 +                            }
 +                        }
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
 +        if is_relevant_impl(cx, item) {
 +            check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id()));
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if is_relevant_trait(cx, item) {
 +            check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id()));
 +        }
 +    }
 +}
 +
 +/// Returns the lint name if it is clippy lint.
 +fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<Symbol> {
 +    if_chain! {
 +        if let Some(meta_item) = lint.meta_item();
 +        if meta_item.path.segments.len() > 1;
 +        if let tool_name = meta_item.path.segments[0].ident;
 +        if tool_name.name == sym::clippy;
 +        then {
 +            let lint_name = meta_item.path.segments.last().unwrap().ident.name;
 +            return Some(lint_name);
 +        }
 +    }
 +    None
 +}
 +
 +fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) {
 +    for lint in items {
 +        if let Some(lint_name) = extract_clippy_lint(lint) {
 +            if lint_name.as_str() == "restriction" && name != sym::allow {
 +                span_lint_and_help(
 +                    cx,
 +                    BLANKET_CLIPPY_RESTRICTION_LINTS,
 +                    lint.span(),
 +                    "restriction lints are not meant to be all enabled",
 +                    None,
 +                    "try enabling only the lints you really need",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem], attr: &'_ Attribute) {
 +    // Check for the feature
 +    if !cx.tcx.sess.features_untracked().lint_reasons {
 +        return;
 +    }
 +
 +    // Check if the reason is present
 +    if let Some(item) = items.last().and_then(NestedMetaItem::meta_item)
 +        && let MetaItemKind::NameValue(_) = &item.kind
 +        && item.path == sym::reason
 +    {
 +        return;
 +    }
 +
 +    span_lint_and_help(
 +        cx,
 +        ALLOW_ATTRIBUTES_WITHOUT_REASON,
 +        attr.span,
 +        &format!("`{}` attribute without specifying a reason", name.as_str()),
 +        None,
 +        "try adding a reason at the end with `, reason = \"..\"`",
 +    );
 +}
 +
 +fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
 +    if let ItemKind::Fn(_, _, eid) = item.kind {
 +        is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value)
 +    } else {
 +        true
 +    }
 +}
 +
 +fn is_relevant_impl(cx: &LateContext<'_>, item: &ImplItem<'_>) -> bool {
 +    match item.kind {
 +        ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value),
 +        _ => false,
 +    }
 +}
 +
 +fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool {
 +    match item.kind {
 +        TraitItemKind::Fn(_, TraitFn::Required(_)) => true,
 +        TraitItemKind::Fn(_, TraitFn::Provided(eid)) => {
 +            is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, block: &Block<'_>) -> bool {
 +    block.stmts.first().map_or(
 +        block
 +            .expr
 +            .as_ref()
 +            .map_or(false, |e| is_relevant_expr(cx, typeck_results, e)),
 +        |stmt| match &stmt.kind {
 +            StmtKind::Local(_) => true,
 +            StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
 +            StmtKind::Item(_) => false,
 +        },
 +    )
 +}
 +
 +fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool {
 +    if macro_backtrace(expr.span).last().map_or(false, |macro_call| {
 +        is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable
 +    }) {
 +        return false;
 +    }
 +    match &expr.kind {
 +        ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block),
 +        ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e),
 +        ExprKind::Ret(None) | ExprKind::Break(_, None) => false,
 +        _ => true,
 +    }
 +}
 +
 +fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) {
 +    if span.from_expansion() {
 +        return;
 +    }
 +
 +    for attr in attrs {
 +        if let Some(values) = attr.meta_item_list() {
 +            if values.len() != 1 || !attr.has_name(sym::inline) {
 +                continue;
 +            }
 +            if is_word(&values[0], sym::always) {
 +                span_lint(
 +                    cx,
 +                    INLINE_ALWAYS,
 +                    attr.span,
 +                    &format!(
 +                        "you have declared `#[inline(always)]` on `{}`. This is usually a bad idea",
 +                        name
 +                    ),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn check_semver(cx: &LateContext<'_>, span: Span, lit: &Lit) {
 +    if let LitKind::Str(is, _) = lit.kind {
 +        if Version::parse(is.as_str()).is_ok() {
 +            return;
 +        }
 +    }
 +    span_lint(
 +        cx,
 +        DEPRECATED_SEMVER,
 +        span,
 +        "the since field must contain a semver-compliant version",
 +    );
 +}
 +
 +fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
 +    if let NestedMetaItem::MetaItem(mi) = &nmi {
 +        mi.is_word() && mi.has_name(expected)
 +    } else {
 +        false
 +    }
 +}
 +
 +pub struct EarlyAttributes {
 +    pub msrv: Option<RustcVersion>,
 +}
 +
 +impl_lint_pass!(EarlyAttributes => [
 +    DEPRECATED_CFG_ATTR,
 +    MISMATCHED_TARGET_OS,
 +    EMPTY_LINE_AFTER_OUTER_ATTR,
 +]);
 +
 +impl EarlyLintPass for EarlyAttributes {
 +    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
 +        check_empty_line_after_outer_attr(cx, item);
 +    }
 +
 +    fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
 +        check_deprecated_cfg_attr(cx, attr, self.msrv);
 +        check_mismatched_target_os(cx, attr);
 +    }
 +
 +    extract_msrv_attr!(EarlyContext);
 +}
 +
 +fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
 +    for attr in &item.attrs {
 +        let attr_item = if let AttrKind::Normal(ref attr, _) = attr.kind {
 +            attr
 +        } else {
 +            return;
 +        };
 +
 +        if attr.style == AttrStyle::Outer {
++            if let MacArgs::Eq(_, MacArgsEq::Ast(expr)) = &attr_item.args
++                && !matches!(expr.kind, rustc_ast::ExprKind::Lit(..)) {
++                return;
++            }
 +            if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) {
 +                return;
 +            }
 +
 +            let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent());
 +            let end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt(), item.span.parent());
 +
 +            if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) {
 +                let lines = snippet.split('\n').collect::<Vec<_>>();
 +                let lines = without_block_comments(lines);
 +
 +                if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
 +                    span_lint(
 +                        cx,
 +                        EMPTY_LINE_AFTER_OUTER_ATTR,
 +                        begin_of_attr_to_item,
 +                        "found an empty line after an outer attribute. \
 +                        Perhaps you forgot to add a `!` to make it an inner attribute?",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option<RustcVersion>) {
 +    if_chain! {
 +        if meets_msrv(msrv.as_ref(), &msrvs::TOOL_ATTRIBUTES);
 +        // check cfg_attr
 +        if attr.has_name(sym::cfg_attr);
 +        if let Some(items) = attr.meta_item_list();
 +        if items.len() == 2;
 +        // check for `rustfmt`
 +        if let Some(feature_item) = items[0].meta_item();
 +        if feature_item.has_name(sym::rustfmt);
 +        // check for `rustfmt_skip` and `rustfmt::skip`
 +        if let Some(skip_item) = &items[1].meta_item();
 +        if skip_item.has_name(sym!(rustfmt_skip)) ||
 +            skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym::skip;
 +        // Only lint outer attributes, because custom inner attributes are unstable
 +        // Tracking issue: https://github.com/rust-lang/rust/issues/54726
 +        if attr.style == AttrStyle::Outer;
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                DEPRECATED_CFG_ATTR,
 +                attr.span,
 +                "`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes",
 +                "use",
 +                "#[rustfmt::skip]".to_string(),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
 +    fn find_os(name: &str) -> Option<&'static str> {
 +        UNIX_SYSTEMS
 +            .iter()
 +            .chain(NON_UNIX_SYSTEMS.iter())
 +            .find(|&&os| os == name)
 +            .copied()
 +    }
 +
 +    fn is_unix(name: &str) -> bool {
 +        UNIX_SYSTEMS.iter().any(|&os| os == name)
 +    }
 +
 +    fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> {
 +        let mut mismatched = Vec::new();
 +
 +        for item in items {
 +            if let NestedMetaItem::MetaItem(meta) = item {
 +                match &meta.kind {
 +                    MetaItemKind::List(list) => {
 +                        mismatched.extend(find_mismatched_target_os(list));
 +                    },
 +                    MetaItemKind::Word => {
 +                        if_chain! {
 +                            if let Some(ident) = meta.ident();
 +                            if let Some(os) = find_os(ident.name.as_str());
 +                            then {
 +                                mismatched.push((os, ident.span));
 +                            }
 +                        }
 +                    },
 +                    MetaItemKind::NameValue(..) => {},
 +                }
 +            }
 +        }
 +
 +        mismatched
 +    }
 +
 +    if_chain! {
 +        if attr.has_name(sym::cfg);
 +        if let Some(list) = attr.meta_item_list();
 +        let mismatched = find_mismatched_target_os(&list);
 +        if !mismatched.is_empty();
 +        then {
 +            let mess = "operating system used in target family position";
 +
 +            span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| {
 +                // Avoid showing the unix suggestion multiple times in case
 +                // we have more than one mismatch for unix-like systems
 +                let mut unix_suggested = false;
 +
 +                for (os, span) in mismatched {
 +                    let sugg = format!("target_os = \"{}\"", os);
 +                    diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect);
 +
 +                    if !unix_suggested && is_unix(os) {
 +                        diag.help("did you mean `unix`?");
 +                        unix_suggested = true;
 +                    }
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +fn is_lint_level(symbol: Symbol) -> bool {
 +    matches!(symbol, sym::allow | sym::expect | sym::warn | sym::deny | sym::forbid)
 +}
index 4592ca7274888dd97b13552ea01ac9c75dd8bf50,0000000000000000000000000000000000000000..5b7c4591504e1cbaa3710a1cad5b684b4f13efbe
mode 100644,000000..100644
--- /dev/null
@@@ -1,200 -1,0 +1,291 @@@
- use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind};
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::{match_def_path, paths};
++use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::def_id::DefId;
- use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_hir::{def::Res, AsyncGeneratorKind, Body, BodyId, GeneratorKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::GeneratorInteriorTypeCause;
- declare_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF]);
++use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::Span;
 +
++use crate::utils::conf::DisallowedType;
++
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to await while holding a non-async-aware MutexGuard.
 +    ///
 +    /// ### Why is this bad?
 +    /// The Mutex types found in std::sync and parking_lot
 +    /// are not designed to operate in an async context across await points.
 +    ///
 +    /// There are two potential solutions. One is to use an async-aware Mutex
 +    /// type. Many asynchronous foundation crates provide such a Mutex type. The
 +    /// other solution is to ensure the mutex is unlocked before calling await,
 +    /// either by introducing a scope or an explicit call to Drop::drop.
 +    ///
 +    /// ### Known problems
 +    /// Will report false positive for explicitly dropped guards
 +    /// ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)). A workaround for this is
 +    /// to wrap the `.lock()` call in a block instead of explicitly dropping the guard.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::sync::Mutex;
 +    /// # async fn baz() {}
 +    /// async fn foo(x: &Mutex<u32>) {
 +    ///   let mut guard = x.lock().unwrap();
 +    ///   *guard += 1;
 +    ///   baz().await;
 +    /// }
 +    ///
 +    /// async fn bar(x: &Mutex<u32>) {
 +    ///   let mut guard = x.lock().unwrap();
 +    ///   *guard += 1;
 +    ///   drop(guard); // explicit drop
 +    ///   baz().await;
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::sync::Mutex;
 +    /// # async fn baz() {}
 +    /// async fn foo(x: &Mutex<u32>) {
 +    ///   {
 +    ///     let mut guard = x.lock().unwrap();
 +    ///     *guard += 1;
 +    ///   }
 +    ///   baz().await;
 +    /// }
 +    ///
 +    /// async fn bar(x: &Mutex<u32>) {
 +    ///   {
 +    ///     let mut guard = x.lock().unwrap();
 +    ///     *guard += 1;
 +    ///   } // guard dropped here at end of scope
 +    ///   baz().await;
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub AWAIT_HOLDING_LOCK,
 +    suspicious,
 +    "inside an async function, holding a `MutexGuard` while calling `await`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to await while holding a `RefCell` `Ref` or `RefMut`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `RefCell` refs only check for exclusive mutable access
 +    /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point
 +    /// risks panics from a mutable ref shared while other refs are outstanding.
 +    ///
 +    /// ### Known problems
 +    /// Will report false positive for explicitly dropped refs
 +    /// ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)). A workaround for this is
 +    /// to wrap the `.borrow[_mut]()` call in a block instead of explicitly dropping the ref.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::cell::RefCell;
 +    /// # async fn baz() {}
 +    /// async fn foo(x: &RefCell<u32>) {
 +    ///   let mut y = x.borrow_mut();
 +    ///   *y += 1;
 +    ///   baz().await;
 +    /// }
 +    ///
 +    /// async fn bar(x: &RefCell<u32>) {
 +    ///   let mut y = x.borrow_mut();
 +    ///   *y += 1;
 +    ///   drop(y); // explicit drop
 +    ///   baz().await;
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::cell::RefCell;
 +    /// # async fn baz() {}
 +    /// async fn foo(x: &RefCell<u32>) {
 +    ///   {
 +    ///      let mut y = x.borrow_mut();
 +    ///      *y += 1;
 +    ///   }
 +    ///   baz().await;
 +    /// }
 +    ///
 +    /// async fn bar(x: &RefCell<u32>) {
 +    ///   {
 +    ///     let mut y = x.borrow_mut();
 +    ///     *y += 1;
 +    ///   } // y dropped here at end of scope
 +    ///   baz().await;
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub AWAIT_HOLDING_REFCELL_REF,
 +    suspicious,
 +    "inside an async function, holding a `RefCell` ref while calling `await`"
 +}
 +
-             check_interior_types(
++declare_clippy_lint! {
++    /// ### What it does
++    /// Allows users to configure types which should not be held across `await`
++    /// suspension points.
++    ///
++    /// ### Why is this bad?
++    /// There are some types which are perfectly "safe" to be used concurrently
++    /// from a memory access perspective but will cause bugs at runtime if they
++    /// are held in such a way.
++    ///
++    /// ### Known problems
++    ///
++    /// ### Example
++    ///
++    /// ```toml
++    /// await-holding-invalid-types = [
++    ///   # You can specify a type name
++    ///   "CustomLockType",
++    ///   # You can (optionally) specify a reason
++    ///   { path = "OtherCustomLockType", reason = "Relies on a thread local" }
++    /// ]
++    /// ```
++    ///
++    /// ```rust
++    /// # async fn baz() {}
++    /// struct CustomLockType;
++    /// struct OtherCustomLockType;
++    /// async fn foo() {
++    ///   let _x = CustomLockType;
++    ///   let _y = OtherCustomLockType;
++    ///   baz().await; // Lint violation
++    /// }
++    /// ```
++    #[clippy::version = "1.49.0"]
++    pub AWAIT_HOLDING_INVALID_TYPE,
++    suspicious,
++    "holding a type across an await point which is not allowed to be held as per the configuration"
++}
++
++impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]);
++
++#[derive(Debug)]
++pub struct AwaitHolding {
++    conf_invalid_types: Vec<DisallowedType>,
++    def_ids: FxHashMap<DefId, DisallowedType>,
++}
++
++impl AwaitHolding {
++    pub(crate) fn new(conf_invalid_types: Vec<DisallowedType>) -> Self {
++        Self {
++            conf_invalid_types,
++            def_ids: FxHashMap::default(),
++        }
++    }
++}
 +
 +impl LateLintPass<'_> for AwaitHolding {
++    fn check_crate(&mut self, cx: &LateContext<'_>) {
++        for conf in &self.conf_invalid_types {
++            let path = match conf {
++                DisallowedType::Simple(path) | DisallowedType::WithReason { path, .. } => path,
++            };
++            let segs: Vec<_> = path.split("::").collect();
++            if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) {
++                self.def_ids.insert(id, conf.clone());
++            }
++        }
++    }
++
 +    fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
 +        use AsyncGeneratorKind::{Block, Closure, Fn};
 +        if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind {
 +            let body_id = BodyId {
 +                hir_id: body.value.hir_id,
 +            };
 +            let typeck_results = cx.tcx.typeck_body(body_id);
- fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
-     for ty_cause in ty_causes {
-         if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
-             if is_mutex_guard(cx, adt.did()) {
-                 span_lint_and_then(
-                     cx,
-                     AWAIT_HOLDING_LOCK,
-                     ty_cause.span,
-                     "this `MutexGuard` is held across an `await` point",
-                     |diag| {
-                         diag.help(
-                             "consider using an async-aware `Mutex` type or ensuring the \
++            self.check_interior_types(
 +                cx,
 +                typeck_results.generator_interior_types.as_ref().skip_binder(),
 +                body.value.span,
 +            );
 +        }
 +    }
 +}
 +
-                         );
-                         diag.span_note(
-                             ty_cause.scope_span.unwrap_or(span),
-                             "these are all the `await` points this lock is held through",
-                         );
-                     },
-                 );
-             }
-             if is_refcell_ref(cx, adt.did()) {
-                 span_lint_and_then(
-                     cx,
-                     AWAIT_HOLDING_REFCELL_REF,
-                     ty_cause.span,
-                     "this `RefCell` reference is held across an `await` point",
-                     |diag| {
-                         diag.help("ensure the reference is dropped before calling `await`");
-                         diag.span_note(
-                             ty_cause.scope_span.unwrap_or(span),
-                             "these are all the `await` points this reference is held through",
-                         );
-                     },
-                 );
++impl AwaitHolding {
++    fn check_interior_types(&self, cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
++        for ty_cause in ty_causes {
++            if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
++                if is_mutex_guard(cx, adt.did()) {
++                    span_lint_and_then(
++                        cx,
++                        AWAIT_HOLDING_LOCK,
++                        ty_cause.span,
++                        "this `MutexGuard` is held across an `await` point",
++                        |diag| {
++                            diag.help(
++                                "consider using an async-aware `Mutex` type or ensuring the \
 +                                `MutexGuard` is dropped before calling await",
++                            );
++                            diag.span_note(
++                                ty_cause.scope_span.unwrap_or(span),
++                                "these are all the `await` points this lock is held through",
++                            );
++                        },
++                    );
++                } else if is_refcell_ref(cx, adt.did()) {
++                    span_lint_and_then(
++                        cx,
++                        AWAIT_HOLDING_REFCELL_REF,
++                        ty_cause.span,
++                        "this `RefCell` reference is held across an `await` point",
++                        |diag| {
++                            diag.help("ensure the reference is dropped before calling `await`");
++                            diag.span_note(
++                                ty_cause.scope_span.unwrap_or(span),
++                                "these are all the `await` points this reference is held through",
++                            );
++                        },
++                    );
++                } else if let Some(disallowed) = self.def_ids.get(&adt.did()) {
++                    emit_invalid_type(cx, ty_cause.span, disallowed);
++                }
 +            }
 +        }
 +    }
 +}
 +
++fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedType) {
++    let (type_name, reason) = match disallowed {
++        DisallowedType::Simple(path) => (path, &None),
++        DisallowedType::WithReason { path, reason } => (path, reason),
++    };
++
++    span_lint_and_then(
++        cx,
++        AWAIT_HOLDING_INVALID_TYPE,
++        span,
++        &format!("`{type_name}` may not be held across an `await` point per `clippy.toml`",),
++        |diag| {
++            if let Some(reason) = reason {
++                diag.note(reason.clone());
++            }
++        },
++    );
++}
++
 +fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    match_def_path(cx, def_id, &paths::MUTEX_GUARD)
 +        || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD)
 +        || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD)
 +        || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD)
 +        || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
 +        || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
 +}
 +
 +fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT)
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d70dbf5b23904aa282d7be16b3c025add46f1781
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::is_type_diagnostic_item;
++use clippy_utils::{match_def_path, paths};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::sym;
++
++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_lint_pass!(BytesCountToLen => [BYTES_COUNT_TO_LEN]);
++
++impl<'tcx> LateLintPass<'tcx> for BytesCountToLen {
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if_chain! {
++            if let hir::ExprKind::MethodCall(_, expr_args, _) = &expr.kind;
++            if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
++            if match_def_path(cx, expr_def_id, &paths::ITER_COUNT);
++
++            if let [bytes_expr] = &**expr_args;
++            if let hir::ExprKind::MethodCall(_, bytes_args, _) = &bytes_expr.kind;
++            if let Some(bytes_def_id) = cx.typeck_results().type_dependent_def_id(bytes_expr.hir_id);
++            if match_def_path(cx, bytes_def_id, &paths::STR_BYTES);
++
++            if let [str_expr] = &**bytes_args;
++            let ty = cx.typeck_results().expr_ty(str_expr).peel_refs();
++
++            if is_type_diagnostic_item(cx, ty, sym::String) || ty.kind() == &ty::Str;
++            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, str_expr.span, "..", &mut applicability)),
++                    applicability
++                );
++            }
++        };
++    }
++}
index a8f9c189adec88672e722c00c265425ea4acec71,0000000000000000000000000000000000000000..7af200708ff0326533b354b5d9801515261e6d0c
mode 100644,000000..100644
--- /dev/null
@@@ -1,83 -1,0 +1,83 @@@
-         if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_digit(10))
-             || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_digit(10));
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
 +use rustc_hir::{Expr, ExprKind, PathSegment};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{source_map::Spanned, symbol::sym, Span};
 +
 +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 {
 +    ///     filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rs")) == Some(true)
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
 +    pedantic,
 +    "Checks for calls to ends_with with case-sensitive file extensions"
 +}
 +
 +declare_lint_pass!(CaseSensitiveFileExtensionComparisons => [CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS]);
 +
 +fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Span> {
 +    if_chain! {
 +        if let ExprKind::MethodCall(PathSegment { ident, .. }, [obj, extension, ..], span) = expr.kind;
 +        if ident.as_str() == "ends_with";
 +        if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind;
 +        if (2..=6).contains(&ext_literal.as_str().len());
 +        if ext_literal.as_str().starts_with('.');
++        if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
++            || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
 +        then {
 +            let mut ty = ctx.typeck_results().expr_ty(obj);
 +            ty = match ty.kind() {
 +                ty::Ref(_, ty, ..) => *ty,
 +                _ => ty
 +            };
 +
 +            match ty.kind() {
 +                ty::Str => {
 +                    return Some(span);
 +                },
 +                ty::Adt(def, _) => {
 +                    if ctx.tcx.is_diagnostic_item(sym::String, def.did()) {
 +                        return Some(span);
 +                    }
 +                },
 +                _ => { return None; }
 +            }
 +        }
 +    }
 +    None
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons {
 +    fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
 +        if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) {
 +            span_lint_and_help(
 +                ctx,
 +                CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
 +                span,
 +                "case-sensitive file extension comparison",
 +                None,
 +                "consider using a case-insensitive comparison instead",
 +            );
 +        }
 +    }
 +}
index 421bd6f53f71b4a78f37788f2a16b61301c76a44,0000000000000000000000000000000000000000..64f87c80f8d147a5e3e21aed776582434d465fee
mode 100644,000000..100644
--- /dev/null
@@@ -1,171 -1,0 +1,169 @@@
-                 apply_reductions(cx, nbits, left, signed)
-                     - (if signed {
-                         0 // let's be conservative here
-                     } else {
-                         // by dividing by 1, we remove 0 bits, etc.
-                         get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
-                     })
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::expr_or_init;
 +use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize};
 +use rustc_ast::ast;
 +use rustc_attr::IntType;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::{BinOpKind, Expr, ExprKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, FloatTy, Ty};
 +
 +use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION};
 +
 +fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
 +    if let Some((Constant::Int(c), _)) = constant(cx, cx.typeck_results(), expr) {
 +        Some(c)
 +    } else {
 +        None
 +    }
 +}
 +
 +fn get_constant_bits(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u64> {
 +    constant_int(cx, expr).map(|c| u64::from(128 - c.leading_zeros()))
 +}
 +
 +fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: bool) -> u64 {
 +    match expr_or_init(cx, expr).kind {
 +        ExprKind::Cast(inner, _) => apply_reductions(cx, nbits, inner, signed),
 +        ExprKind::Block(block, _) => block.expr.map_or(nbits, |e| apply_reductions(cx, nbits, e, signed)),
 +        ExprKind::Binary(op, left, right) => match op.node {
 +            BinOpKind::Div => {
-             BinOpKind::Shr => {
-                 apply_reductions(cx, nbits, left, signed)
-                     - constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))
-             },
++                apply_reductions(cx, nbits, left, signed).saturating_sub(if signed {
++                    // let's be conservative here
++                    0
++                } else {
++                    // by dividing by 1, we remove 0 bits, etc.
++                    get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
++                })
 +            },
 +            BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right)
 +                .unwrap_or(u64::max_value())
 +                .min(apply_reductions(cx, nbits, left, signed)),
++            BinOpKind::Shr => apply_reductions(cx, nbits, left, signed)
++                .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))),
 +            _ => nbits,
 +        },
 +        ExprKind::MethodCall(method, [left, right], _) => {
 +            if signed {
 +                return nbits;
 +            }
 +            let max_bits = if method.ident.as_str() == "min" {
 +                get_constant_bits(cx, right)
 +            } else {
 +                None
 +            };
 +            apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::max_value()))
 +        },
 +        ExprKind::MethodCall(method, [_, lo, hi], _) => {
 +            if method.ident.as_str() == "clamp" {
 +                //FIXME: make this a diagnostic item
 +                if let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi)) {
 +                    return lo_bits.max(hi_bits);
 +                }
 +            }
 +            nbits
 +        },
 +        ExprKind::MethodCall(method, [_value], _) => {
 +            if method.ident.name.as_str() == "signum" {
 +                0 // do not lint if cast comes from a `signum` function
 +            } else {
 +                nbits
 +            }
 +        },
 +        _ => nbits,
 +    }
 +}
 +
 +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
 +    let msg = match (cast_from.kind(), cast_to.is_integral()) {
 +        (ty::Int(_) | ty::Uint(_), true) => {
 +            let from_nbits = apply_reductions(
 +                cx,
 +                utils::int_ty_to_nbits(cast_from, cx.tcx),
 +                cast_expr,
 +                cast_from.is_signed(),
 +            );
 +            let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
 +
 +            let (should_lint, suffix) = match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) {
 +                (true, true) | (false, false) => (to_nbits < from_nbits, ""),
 +                (true, false) => (
 +                    to_nbits <= 32,
 +                    if to_nbits == 32 {
 +                        " on targets with 64-bit wide pointers"
 +                    } else {
 +                        ""
 +                    },
 +                ),
 +                (false, true) => (from_nbits == 64, " on targets with 32-bit wide pointers"),
 +            };
 +
 +            if !should_lint {
 +                return;
 +            }
 +
 +            format!(
 +                "casting `{}` to `{}` may truncate the value{}",
 +                cast_from, cast_to, suffix,
 +            )
 +        },
 +
 +        (ty::Adt(def, _), true) if def.is_enum() => {
 +            let (from_nbits, variant) = if let ExprKind::Path(p) = &cast_expr.kind
 +                && let Res::Def(DefKind::Ctor(..), id) = cx.qpath_res(p, cast_expr.hir_id)
 +            {
 +                let i = def.variant_index_with_ctor_id(id);
 +                let variant = def.variant(i);
 +                let nbits = utils::enum_value_nbits(get_discriminant_value(cx.tcx, *def, i));
 +                (nbits, Some(variant))
 +            } else {
 +                (utils::enum_ty_to_nbits(*def, cx.tcx), None)
 +            };
 +            let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
 +
 +            let cast_from_ptr_size = def.repr().int.map_or(true, |ty| {
 +                matches!(
 +                    ty,
 +                    IntType::SignedInt(ast::IntTy::Isize) | IntType::UnsignedInt(ast::UintTy::Usize)
 +                )
 +            });
 +            let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) {
 +                (false, false) if from_nbits > to_nbits => "",
 +                (true, false) if from_nbits > to_nbits => "",
 +                (false, true) if from_nbits > 64 => "",
 +                (false, true) if from_nbits > 32 => " on targets with 32-bit wide pointers",
 +                _ => return,
 +            };
 +
 +            if let Some(variant) = variant {
 +                span_lint(
 +                    cx,
 +                    CAST_ENUM_TRUNCATION,
 +                    expr.span,
 +                    &format!(
 +                        "casting `{}::{}` to `{}` will truncate the value{}",
 +                        cast_from, variant.name, cast_to, suffix,
 +                    ),
 +                );
 +                return;
 +            }
 +            format!(
 +                "casting `{}` to `{}` may truncate the value{}",
 +                cast_from, cast_to, suffix,
 +            )
 +        },
 +
 +        (ty::Float(_), true) => {
 +            format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to)
 +        },
 +
 +        (ty::Float(FloatTy::F64), false) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => {
 +            "casting `f64` to `f32` may truncate the value".to_string()
 +        },
 +
 +        _ => return,
 +    };
 +
 +    span_lint(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg);
 +}
index 3608c1654d5c71196b209cc0c1c47617a16556b8,0000000000000000000000000000000000000000..2238668abca71d464e9f8f0be73600d1b95140a2
mode 100644,000000..100644
--- /dev/null
@@@ -1,117 -1,0 +1,143 @@@
- use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source::snippet_opt};
++use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source};
 +use if_chain::if_chain;
 +use rustc_ast::Mutability;
 +use rustc_hir::{Expr, ExprKind, Node};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, layout::LayoutOf, Ty, TypeAndMut};
 +use rustc_semver::RustcVersion;
 +
 +use super::CAST_SLICE_DIFFERENT_SIZES;
 +
- fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-     let map = cx.tcx.hir();
-     if_chain! {
-         if let Some(parent_id) = map.find_parent_node(expr.hir_id);
-         if let Some(parent) = map.find(parent_id);
-         then {
-             let expr = match parent {
-                 Node::Block(block) => {
-                     if let Some(parent_expr) = block.expr {
-                         parent_expr
-                     } else {
-                         return false;
-                     }
-                 },
-                 Node::Expr(expr) => expr,
-                 _ => return false,
-             };
-             matches!(expr.kind, ExprKind::Cast(..))
-         } else {
-             false
-         }
-     }
- }
- pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVersion>) {
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Option<RustcVersion>) {
 +    // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist
 +    if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) {
 +        return;
 +    }
 +
 +    // if this cast is the child of another cast expression then don't emit something for it, the full
 +    // chain will be analyzed
 +    if is_child_of_cast(cx, expr) {
 +        return;
 +    }
 +
-     if let Some((from_slice_ty, to_slice_ty)) = expr_cast_chain_tys(cx, expr) {
-         if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(from_slice_ty.ty), cx.layout_of(to_slice_ty.ty)) {
++    if let Some(CastChainInfo {
++        left_cast,
++        start_ty,
++        end_ty,
++    }) = expr_cast_chain_tys(cx, expr)
++    {
++        if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) {
 +            let from_size = from_layout.size.bytes();
 +            let to_size = to_layout.size.bytes();
 +            if from_size != to_size && from_size != 0 && to_size != 0 {
 +                span_lint_and_then(
 +                    cx,
 +                    CAST_SLICE_DIFFERENT_SIZES,
 +                    expr.span,
 +                    &format!(
 +                        "casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count",
-                         from_slice_ty, from_size, to_slice_ty, to_size,
++                        start_ty.ty, from_size, end_ty.ty, to_size,
 +                    ),
 +                    |diag| {
-                         let cast_expr = match expr.kind {
-                             ExprKind::Cast(cast_expr, ..) => cast_expr,
-                             _ => unreachable!("expr should be a cast as checked by expr_cast_chain_tys"),
-                         };
-                         let ptr_snippet = snippet_opt(cx, cast_expr.span).unwrap();
++                        let ptr_snippet = source::snippet(cx, left_cast.span, "..");
 +
-                         let (mutbl_fn_str, mutbl_ptr_str) = match to_slice_ty.mutbl {
++                        let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl {
 +                            Mutability::Mut => ("_mut", "mut"),
 +                            Mutability::Not => ("", "const"),
 +                        };
 +                        let sugg = format!(
-                             "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {to_slice_ty}, ..)"
++                            "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)",
++                            // get just the ty from the TypeAndMut so that the printed type isn't something like `mut
++                            // T`, extract just the `T`
++                            end_ty.ty
 +                        );
 +
 +                        diag.span_suggestion(
 +                            expr.span,
 +                            &format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
 +                            sugg,
 +                            rustc_errors::Applicability::HasPlaceholders,
 +                        );
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
++fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
++    let map = cx.tcx.hir();
++    if_chain! {
++        if let Some(parent_id) = map.find_parent_node(expr.hir_id);
++        if let Some(parent) = map.find(parent_id);
++        then {
++            let expr = match parent {
++                Node::Block(block) => {
++                    if let Some(parent_expr) = block.expr {
++                        parent_expr
++                    } else {
++                        return false;
++                    }
++                },
++                Node::Expr(expr) => expr,
++                _ => return false,
++            };
++
++            matches!(expr.kind, ExprKind::Cast(..))
++        } else {
++            false
++        }
++    }
++}
++
 +/// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if
 +/// the type is one of those slices
 +fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option<TypeAndMut<'_>> {
 +    match ty.kind() {
 +        ty::RawPtr(TypeAndMut { ty: slice_ty, mutbl }) => match slice_ty.kind() {
 +            ty::Slice(ty) => Some(TypeAndMut { ty: *ty, mutbl: *mutbl }),
 +            _ => None,
 +        },
 +        _ => None,
 +    }
 +}
 +
- /// Returns the pair (original ptr T, final ptr U) if the expression is composed of casts
++struct CastChainInfo<'tcx> {
++    /// The left most part of the cast chain, or in other words, the first cast in the chain
++    /// Used for diagnostics
++    left_cast: &'tcx Expr<'tcx>,
++    /// The starting type of the cast chain
++    start_ty: TypeAndMut<'tcx>,
++    /// The final type of the cast chain
++    end_ty: TypeAndMut<'tcx>,
++}
++
++/// Returns a `CastChainInfo` with the left-most cast in the chain and the original ptr T and final
++/// ptr U if the expression is composed of casts.
 +/// Returns None if the expr is not a Cast
- fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<(TypeAndMut<'tcx>, TypeAndMut<'tcx>)> {
++fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<CastChainInfo<'tcx>> {
 +    if let ExprKind::Cast(cast_expr, _cast_to_hir_ty) = expr.peel_blocks().kind {
 +        let cast_to = cx.typeck_results().expr_ty(expr);
 +        let to_slice_ty = get_raw_slice_ty_mut(cast_to)?;
-         if let Some((inner_from_ty, _inner_to_ty)) = expr_cast_chain_tys(cx, cast_expr) {
-             Some((inner_from_ty, to_slice_ty))
++
++        // If the expression that makes up the source of this cast is itself a cast, recursively
++        // call `expr_cast_chain_tys` and update the end type with the final tartet type.
++        // Otherwise, this cast is not immediately nested, just construct the info for this cast
++        if let Some(prev_info) = expr_cast_chain_tys(cx, cast_expr) {
++            Some(CastChainInfo {
++                end_ty: to_slice_ty,
++                ..prev_info
++            })
 +        } else {
 +            let cast_from = cx.typeck_results().expr_ty(cast_expr);
 +            let from_slice_ty = get_raw_slice_ty_mut(cast_from)?;
-             Some((from_slice_ty, to_slice_ty))
++            Some(CastChainInfo {
++                left_cast: cast_expr,
++                start_ty: from_slice_ty,
++                end_ty: to_slice_ty,
++            })
 +        }
 +    } else {
 +        None
 +    }
 +}
index 55c1f085657bb1842eafd7c52ff5e75a5b68321c,0000000000000000000000000000000000000000..b58252dcb9424b90339fc2cb86f5159f8991a181
mode 100644,000000..100644
--- /dev/null
@@@ -1,578 -1,0 +1,579 @@@
-     /// accidentally if parantheses are omitted from a function call. If you aren't doing anything
 +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 char_lit_as_u8;
 +mod fn_to_numeric_cast;
 +mod fn_to_numeric_cast_any;
 +mod fn_to_numeric_cast_with_truncation;
 +mod ptr_as_ptr;
 +mod unnecessary_cast;
 +mod utils;
 +
 +use clippy_utils::is_hir_ty_cfg_dependant;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from any numerical to a float type where
 +    /// the receiving type cannot store all values from the original type without
 +    /// rounding errors. This possible rounding is to be expected, so this lint is
 +    /// `Allow` by default.
 +    ///
 +    /// Basically, this warns on casting any integer with 32 or more bits to `f32`
 +    /// or any 64-bit integer to `f64`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's not bad at all. But in some applications it can be
 +    /// helpful to know where precision loss can take place. This lint can help find
 +    /// those places in the code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = u64::MAX;
 +    /// x as f64;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_PRECISION_LOSS,
 +    pedantic,
 +    "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from a signed to an unsigned numerical
 +    /// type. In this case, negative values wrap around to large positive values,
 +    /// which can be quite surprising in practice. However, as the cast works as
 +    /// defined, this lint is `Allow` by default.
 +    ///
 +    /// ### Why is this bad?
 +    /// Possibly surprising results. You can activate this lint
 +    /// as a one-time check to see where numerical wrapping can arise.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let y: i8 = -1;
 +    /// y as u128; // will return 18446744073709551615
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_SIGN_LOSS,
 +    pedantic,
 +    "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts between numerical types that may
 +    /// truncate large values. This is expected behavior, so the cast is `Allow` by
 +    /// default.
 +    ///
 +    /// ### Why is this bad?
 +    /// In some problem domains, it is good practice to avoid
 +    /// truncation. This lint can be activated to help assess where additional
 +    /// checks could be beneficial.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn as_u8(x: u64) -> u8 {
 +    ///     x as u8
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_POSSIBLE_TRUNCATION,
 +    pedantic,
 +    "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from an unsigned type to a signed type of
 +    /// the same size. Performing such a cast is a 'no-op' for the compiler,
 +    /// i.e., nothing is changed at the bit level, and the binary representation of
 +    /// the value is reinterpreted. This can cause wrapping if the value is too big
 +    /// for the target signed type. However, the cast works as defined, so this lint
 +    /// is `Allow` by default.
 +    ///
 +    /// ### Why is this bad?
 +    /// While such a cast is not bad in itself, the results can
 +    /// be surprising when this is not the intended behavior, as demonstrated by the
 +    /// example below.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// u32::MAX as i32; // will yield a value of `-1`
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_POSSIBLE_WRAP,
 +    pedantic,
 +    "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts between numerical types that may
 +    /// be replaced by safe conversion functions.
 +    ///
 +    /// ### Why is this bad?
 +    /// Rust's `as` keyword will perform many kinds of
 +    /// conversions, including silently lossy conversions. Conversion functions such
 +    /// as `i32::from` will only perform lossless conversions. Using the conversion
 +    /// functions prevents conversions from turning into silent lossy conversions if
 +    /// the types of the input expressions ever change, and make it easier for
 +    /// people reading the code to know that the conversion is lossless.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn as_u64(x: u8) -> u64 {
 +    ///     x as u64
 +    /// }
 +    /// ```
 +    ///
 +    /// Using `::from` would look like this:
 +    ///
 +    /// ```rust
 +    /// fn as_u64(x: u8) -> u64 {
 +    ///     u64::from(x)
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_LOSSLESS,
 +    pedantic,
 +    "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts to the same type, casts of int literals to integer types
 +    /// and casts of float literals to float types.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's just unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = 2i32 as i32;
 +    /// let _ = 0.5 as f32;
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust
 +    /// let _ = 2_i32;
 +    /// let _ = 0.5_f32;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNNECESSARY_CAST,
 +    complexity,
 +    "cast to the same type, e.g., `x as i32` where `x: i32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts, using `as` or `pointer::cast`,
 +    /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer
 +    ///
 +    /// ### Why is this bad?
 +    /// Dereferencing the resulting pointer may be undefined
 +    /// behavior.
 +    ///
 +    /// ### Known problems
 +    /// Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar
 +    /// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like
 +    /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (&1u8 as *const u8) as *const u16;
 +    /// let _ = (&mut 1u8 as *mut u8) as *mut u16;
 +    ///
 +    /// (&1u8 as *const u8).cast::<u16>();
 +    /// (&mut 1u8 as *mut u8).cast::<u16>();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_PTR_ALIGNMENT,
 +    pedantic,
 +    "cast from a pointer to a more-strictly-aligned pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of function pointers to something other than usize
 +    ///
 +    /// ### Why is this bad?
 +    /// Casting a function pointer to anything other than usize/isize is not portable across
 +    /// architectures, because you end up losing bits if the target type is too small or end up with a
 +    /// bunch of extra bits that waste space and add more instructions to the final binary than
 +    /// strictly necessary for the problem
 +    ///
 +    /// Casting to isize also doesn't make sense since there are no signed addresses.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// fn fun() -> i32 { 1 }
 +    /// let a = fun as i64;
 +    ///
 +    /// // Good
 +    /// fn fun2() -> i32 { 1 }
 +    /// let a = fun2 as usize;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FN_TO_NUMERIC_CAST,
 +    style,
 +    "casting a function pointer to a numeric type other than usize"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of a function pointer to a numeric type not wide enough to
 +    /// store address.
 +    ///
 +    /// ### Why is this bad?
 +    /// Such a cast discards some bits of the function's address. If this is intended, it would be more
 +    /// clearly expressed by casting to usize first, then casting the usize to the intended type (with
 +    /// a comment) to perform the truncation.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// fn fn1() -> i16 {
 +    ///     1
 +    /// };
 +    /// let _ = fn1 as i32;
 +    ///
 +    /// // Better: Cast to usize first, then comment with the reason for the truncation
 +    /// fn fn2() -> i16 {
 +    ///     1
 +    /// };
 +    /// let fn_ptr = fn2 as usize;
 +    /// let fn_ptr_truncated = fn_ptr as i32;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
 +    style,
 +    "casting a function pointer to a numeric type not wide enough to store the address"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of a function pointer to any integer type.
 +    ///
 +    /// ### Why is this bad?
 +    /// Casting a function pointer to an integer can have surprising results and can occur
++    /// accidentally if 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
 +    /// // Bad: fn1 is cast as `usize`
 +    /// fn fn1() -> u16 {
 +    ///     1
 +    /// };
 +    /// let _ = fn1 as usize;
 +    ///
 +    /// // Good: maybe you intended to call the function?
 +    /// fn fn2() -> u16 {
 +    ///     1
 +    /// };
 +    /// let _ = fn2() as usize;
 +    ///
 +    /// // Good: maybe you intended to cast it to a function type?
 +    /// fn fn3() -> u16 {
 +    ///     1
 +    /// }
 +    /// let _ = fn3 as fn() -> u16;
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub FN_TO_NUMERIC_CAST_ANY,
 +    restriction,
 +    "casting a function pointer to any integer type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of `&T` to `&mut T` anywhere in the code.
 +    ///
 +    /// ### Why is this bad?
 +    /// It’s basically guaranteed to be undefined behaviour.
 +    /// `UnsafeCell` is the only way to obtain aliasable data that is considered
 +    /// mutable.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// fn x(r: &i32) {
 +    ///     unsafe {
 +    ///         *(r as *const _ as *mut _) += 1;
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Instead consider using interior mutability types.
 +    ///
 +    /// ```rust
 +    /// use std::cell::UnsafeCell;
 +    ///
 +    /// fn x(r: &UnsafeCell<i32>) {
 +    ///     unsafe {
 +    ///         *r.get() += 1;
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.33.0"]
 +    pub CAST_REF_TO_MUT,
 +    correctness,
 +    "a cast of reference to a mutable pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expressions where a character literal is cast
 +    /// to `u8` and suggests using a byte literal instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// In general, casting values to smaller types is
 +    /// error-prone and should be avoided where possible. In the particular case of
 +    /// converting a character literal to u8, it is easy to avoid by just using a
 +    /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter
 +    /// than `'a' as u8`.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// 'x' as u8
 +    /// ```
 +    ///
 +    /// A better version, using the byte literal:
 +    ///
 +    /// ```rust,ignore
 +    /// b'x'
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CHAR_LIT_AS_U8,
 +    complexity,
 +    "casting a character literal to `u8` truncates"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `as` casts between raw pointers without changing its mutability,
 +    /// namely `*const T` to `*const U` and `*mut T` to `*mut U`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because
 +    /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let ptr: *const u32 = &42_u32;
 +    /// let mut_ptr: *mut u32 = &mut 42_u32;
 +    /// let _ = ptr as *const i32;
 +    /// let _ = mut_ptr as *mut i32;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let ptr: *const u32 = &42_u32;
 +    /// let mut_ptr: *mut u32 = &mut 42_u32;
 +    /// let _ = ptr.cast::<i32>();
 +    /// let _ = mut_ptr.cast::<i32>();
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub PTR_AS_PTR,
 +    pedantic,
 +    "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from an enum type to an integral type which will definitely truncate the
 +    /// value.
 +    ///
 +    /// ### Why is this bad?
 +    /// The resulting integral value will not match the value of the variant it came from.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// enum E { X = 256 };
 +    /// let _ = E::X as u8;
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub CAST_ENUM_TRUNCATION,
 +    suspicious,
 +    "casts from an enum type to an integral type which will truncate the value"
 +}
 +
 +declare_clippy_lint! {
 +    /// 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.60.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.61.0"]
 +    pub CAST_ABS_TO_UNSIGNED,
 +    suspicious,
 +    "casting the result of `abs()` to an unsigned integer can panic"
 +}
 +
 +pub struct Casts {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl Casts {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(Casts => [
 +    CAST_PRECISION_LOSS,
 +    CAST_SIGN_LOSS,
 +    CAST_POSSIBLE_TRUNCATION,
 +    CAST_POSSIBLE_WRAP,
 +    CAST_LOSSLESS,
 +    CAST_REF_TO_MUT,
 +    CAST_PTR_ALIGNMENT,
 +    CAST_SLICE_DIFFERENT_SIZES,
 +    UNNECESSARY_CAST,
 +    FN_TO_NUMERIC_CAST_ANY,
 +    FN_TO_NUMERIC_CAST,
 +    FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
 +    CHAR_LIT_AS_U8,
 +    PTR_AS_PTR,
 +    CAST_ENUM_TRUNCATION,
 +    CAST_ENUM_CONSTRUCTOR,
 +    CAST_ABS_TO_UNSIGNED
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Casts {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if !in_external_macro(cx.sess(), expr.span) {
 +            ptr_as_ptr::check(cx, expr, &self.msrv);
 +        }
 +
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
 +            if is_hir_ty_cfg_dependant(cx, cast_to) {
 +                return;
 +            }
 +            let (cast_from, cast_to) = (
 +                cx.typeck_results().expr_ty(cast_expr),
 +                cx.typeck_results().expr_ty(expr),
 +            );
 +
 +            if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
 +                return;
 +            }
 +
 +            fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
 +            fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
 +            fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
 +
 +            if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
 +                cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
 +                if cast_from.is_numeric() {
 +                    cast_possible_wrap::check(cx, expr, cast_from, cast_to);
 +                    cast_precision_loss::check(cx, expr, cast_from, cast_to);
 +                    cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
 +                    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);
 +            }
 +        }
 +
 +        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 eae2723a7dac1f60ea6e1282a8385e8ae07166ec,0000000000000000000000000000000000000000..3227e6e86af4e3021c3b97c28655437536f12f2c
mode 100644,000000..100644
--- /dev/null
@@@ -1,185 -1,0 +1,195 @@@
- use clippy_utils::source::{snippet_block, snippet_block_with_applicability};
 +//! Checks for if expressions that contain only an if expression.
 +//!
 +//! For example, the lint would catch:
 +//!
 +//! ```rust,ignore
 +//! if x {
 +//!     if y {
 +//!         println!("Hello world");
 +//!     }
 +//! }
 +//! ```
 +//!
 +//! This lint is **warn** by default
 +
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-             check_collapsible_maybe_if_let(cx, else_);
++use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability};
 +use clippy_utils::sugg::Sugg;
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for nested `if` statements which can be collapsed
 +    /// by `&&`-combining their conditions.
 +    ///
 +    /// ### Why is this bad?
 +    /// Each `if`-statement adds one level of nesting, which
 +    /// makes code look more complex than it really is.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// if x {
 +    ///     if y {
 +    ///         …
 +    ///     }
 +    /// }
 +    ///
 +    /// ```
 +    ///
 +    /// Should be written:
 +    ///
 +    /// ```rust,ignore
 +    /// if x && y {
 +    ///     …
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub COLLAPSIBLE_IF,
 +    style,
 +    "nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for collapsible `else { if ... }` expressions
 +    /// that can be collapsed to `else if ...`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Each `if`-statement adds one level of nesting, which
 +    /// makes code look more complex than it really is.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    ///
 +    /// if x {
 +    ///     …
 +    /// } else {
 +    ///     if y {
 +    ///         …
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Should be written:
 +    ///
 +    /// ```rust,ignore
 +    /// if x {
 +    ///     …
 +    /// } else if y {
 +    ///     …
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub COLLAPSIBLE_ELSE_IF,
 +    style,
 +    "nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)"
 +}
 +
 +declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]);
 +
 +impl EarlyLintPass for CollapsibleIf {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
 +        if !expr.span.from_expansion() {
 +            check_if(cx, expr);
 +        }
 +    }
 +}
 +
 +fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
 +    if let ast::ExprKind::If(check, then, else_) = &expr.kind {
 +        if let Some(else_) = else_ {
- fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
++            check_collapsible_maybe_if_let(cx, then.span, else_);
 +        } else if let ast::ExprKind::Let(..) = check.kind {
 +            // Prevent triggering on `if let a = b { if c { .. } }`.
 +        } else {
 +            check_collapsible_no_if_let(cx, expr, check, then);
 +        }
 +    }
 +}
 +
 +fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool {
 +    // We trim all opening braces and whitespaces and then check if the next string is a comment.
 +    let trimmed_block_text = snippet_block(cx, expr.span, "..", None)
 +        .trim_start_matches(|c: char| c.is_whitespace() || c == '{')
 +        .to_owned();
 +    trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*")
 +}
 +
-                 snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(),
++fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) {
 +    if_chain! {
 +        if let ast::ExprKind::Block(ref block, _) = else_.kind;
 +        if !block_starts_with_comment(cx, block);
 +        if let Some(else_) = expr_block(block);
 +        if else_.attrs.is_empty();
 +        if !else_.span.from_expansion();
 +        if let ast::ExprKind::If(..) = else_.kind;
 +        then {
++            // Prevent "elseif"
++            // Check that the "else" is followed by whitespace
++            let up_to_else = then_span.between(block.span);
++            let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { !c.is_whitespace() } else { false };
++
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_sugg(
 +                cx,
 +                COLLAPSIBLE_ELSE_IF,
 +                block.span,
 +                "this `else { if .. }` block can be collapsed",
 +                "collapse nested if block",
++                format!(
++                    "{}{}",
++                    if requires_space { " " } else { "" },
++                    snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability)
++                ),
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) {
 +    if_chain! {
 +        if !block_starts_with_comment(cx, then);
 +        if let Some(inner) = expr_block(then);
 +        if inner.attrs.is_empty();
 +        if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind;
 +        // Prevent triggering on `if c { if let a = b { .. } }`.
 +        if !matches!(check_inner.kind, ast::ExprKind::Let(..));
 +        if expr.span.ctxt() == inner.span.ctxt();
 +        then {
 +            span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| {
 +                let lhs = Sugg::ast(cx, check, "..");
 +                let rhs = Sugg::ast(cx, check_inner, "..");
 +                diag.span_suggestion(
 +                    expr.span,
 +                    "collapse nested if block",
 +                    format!(
 +                        "if {} {}",
 +                        lhs.and(&rhs),
 +                        snippet_block(cx, content.span, "..", Some(expr.span)),
 +                    ),
 +                    Applicability::MachineApplicable, // snippet
 +                );
 +            });
 +        }
 +    }
 +}
 +
 +/// If the block contains only one expression, return it.
 +fn expr_block(block: &ast::Block) -> Option<&ast::Expr> {
 +    let mut it = block.stmts.iter();
 +
 +    if let (Some(stmt), None) = (it.next(), it.next()) {
 +        match stmt.kind {
 +            ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => Some(expr),
 +            _ => None,
 +        }
 +    } else {
 +        None
 +    }
 +}
index 503cef76775e4c8a56fa5479f8efe0066c2e09d3,0000000000000000000000000000000000000000..b3fd8af4730dc16d6ee1e6102e040d2e9da38217
mode 100644,000000..100644
--- /dev/null
@@@ -1,851 -1,0 +1,849 @@@
-             hir::ItemKind::Impl(ref impl_) => {
 +use clippy_utils::attrs::is_doc_hidden;
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then};
 +use clippy_utils::macros::{is_panic, root_macro_call_first_node};
 +use clippy_utils::source::{first_line_of_span, snippet_with_applicability};
 +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 +use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty};
 +use if_chain::if_chain;
 +use itertools::Itertools;
 +use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind};
 +use rustc_ast::token::CommentKind;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_data_structures::sync::Lrc;
 +use rustc_errors::emitter::EmitterWriter;
 +use rustc_errors::{Applicability, Handler, MultiSpan, SuggestionStyle};
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{self, Visitor};
 +use rustc_hir::{AnonConst, Expr};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty;
 +use rustc_parse::maybe_new_parser_from_source_str;
 +use rustc_parse::parser::ForceCollect;
 +use rustc_session::parse::ParseSess;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::def_id::LocalDefId;
 +use rustc_span::edition::Edition;
 +use rustc_span::source_map::{BytePos, FilePathMapping, SourceMap, Span};
 +use rustc_span::{sym, FileName, Pos};
 +use std::io;
 +use std::ops::Range;
 +use std::thread;
 +use url::Url;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the presence of `_`, `::` or camel-case words
 +    /// outside ticks in documentation.
 +    ///
 +    /// ### Why is this bad?
 +    /// *Rustdoc* supports markdown formatting, `_`, `::` and
 +    /// camel-case probably indicates some code which should be included between
 +    /// ticks. `_` can also be used for emphasis in markdown, this lint tries to
 +    /// consider that.
 +    ///
 +    /// ### Known problems
 +    /// Lots of bad docs won’t be fixed, what the lint checks
 +    /// for is limited, and there are still false positives. HTML elements and their
 +    /// content are not linted.
 +    ///
 +    /// In addition, when writing documentation comments, including `[]` brackets
 +    /// inside a link text would trip the parser. Therefore, documenting link with
 +    /// `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec
 +    /// would fail.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// /// Do something with the foo_bar parameter. See also
 +    /// /// that::other::module::foo.
 +    /// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
 +    /// fn doit(foo_bar: usize) {}
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// // Link text with `[]` brackets should be written as following:
 +    /// /// Consume the array and return the inner
 +    /// /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].
 +    /// /// [SmallVec]: SmallVec
 +    /// fn main() {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DOC_MARKDOWN,
 +    pedantic,
 +    "presence of `_`, `::` or camel-case outside backticks in documentation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the doc comments of publicly visible
 +    /// unsafe functions and warns if there is no `# Safety` section.
 +    ///
 +    /// ### Why is this bad?
 +    /// Unsafe functions should document their safety
 +    /// preconditions, so that users can be sure they are using them safely.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    ///# type Universe = ();
 +    /// /// This function should really be documented
 +    /// pub unsafe fn start_apocalypse(u: &mut Universe) {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    ///
 +    /// At least write a line about safety:
 +    ///
 +    /// ```rust
 +    ///# type Universe = ();
 +    /// /// # Safety
 +    /// ///
 +    /// /// This function should not be called before the horsemen are ready.
 +    /// pub unsafe fn start_apocalypse(u: &mut Universe) {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub MISSING_SAFETY_DOC,
 +    style,
 +    "`pub unsafe fn` without `# Safety` docs"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks the doc comments of publicly visible functions that
 +    /// return a `Result` type and warns if there is no `# Errors` section.
 +    ///
 +    /// ### Why is this bad?
 +    /// Documenting the type of errors that can be returned from a
 +    /// function can help callers write code to handle the errors appropriately.
 +    ///
 +    /// ### Examples
 +    /// Since the following function returns a `Result` it has an `# Errors` section in
 +    /// its doc comment:
 +    ///
 +    /// ```rust
 +    ///# use std::io;
 +    /// /// # Errors
 +    /// ///
 +    /// /// Will return `Err` if `filename` does not exist or the user does not have
 +    /// /// permission to read it.
 +    /// pub fn read(filename: String) -> io::Result<String> {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.41.0"]
 +    pub MISSING_ERRORS_DOC,
 +    pedantic,
 +    "`pub fn` returns `Result` without `# Errors` in doc comment"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks the doc comments of publicly visible functions that
 +    /// may panic and warns if there is no `# Panics` section.
 +    ///
 +    /// ### Why is this bad?
 +    /// Documenting the scenarios in which panicking occurs
 +    /// can help callers who do not want to panic to avoid those situations.
 +    ///
 +    /// ### Examples
 +    /// Since the following function may panic it has a `# Panics` section in
 +    /// its doc comment:
 +    ///
 +    /// ```rust
 +    /// /// # Panics
 +    /// ///
 +    /// /// Will panic if y is 0
 +    /// pub fn divide_by(x: i32, y: i32) -> i32 {
 +    ///     if y == 0 {
 +    ///         panic!("Cannot divide by 0")
 +    ///     } else {
 +    ///         x / y
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub MISSING_PANICS_DOC,
 +    pedantic,
 +    "`pub fn` may panic without `# Panics` in doc comment"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `fn main() { .. }` in doctests
 +    ///
 +    /// ### Why is this bad?
 +    /// The test can be shorter (and likely more readable)
 +    /// if the `fn main()` is left implicit.
 +    ///
 +    /// ### Examples
 +    /// ``````rust
 +    /// /// An example of a doctest with a `main()` function
 +    /// ///
 +    /// /// # Examples
 +    /// ///
 +    /// /// ```
 +    /// /// fn main() {
 +    /// ///     // this needs not be in an `fn`
 +    /// /// }
 +    /// /// ```
 +    /// fn needless_main() {
 +    ///     unimplemented!();
 +    /// }
 +    /// ``````
 +    #[clippy::version = "1.40.0"]
 +    pub NEEDLESS_DOCTEST_MAIN,
 +    style,
 +    "presence of `fn main() {` in code examples"
 +}
 +
 +#[allow(clippy::module_name_repetitions)]
 +#[derive(Clone)]
 +pub struct DocMarkdown {
 +    valid_idents: FxHashSet<String>,
 +    in_trait_impl: bool,
 +}
 +
 +impl DocMarkdown {
 +    pub fn new(valid_idents: FxHashSet<String>) -> Self {
 +        Self {
 +            valid_idents,
 +            in_trait_impl: false,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(DocMarkdown =>
 +    [DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, NEEDLESS_DOCTEST_MAIN]
 +);
 +
 +impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
 +    fn check_crate(&mut self, cx: &LateContext<'tcx>) {
 +        let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID);
 +        check_attrs(cx, &self.valid_idents, attrs);
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let headers = check_attrs(cx, &self.valid_idents, attrs);
 +        match item.kind {
 +            hir::ItemKind::Fn(ref sig, _, body_id) => {
 +                if !(is_entrypoint_fn(cx, item.def_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
 +                    let body = cx.tcx.hir().body(body_id);
 +                    let mut fpu = FindPanicUnwrap {
 +                        cx,
 +                        typeck_results: cx.tcx.typeck(item.def_id),
 +                        panic_span: None,
 +                    };
 +                    fpu.visit_expr(&body.value);
 +                    lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span);
 +                }
 +            },
-                 let fallback_bundle = rustc_errors::fallback_fluent_bundle(
-                     rustc_errors::DEFAULT_LOCALE_RESOURCES,
-                     false
-                 );
++            hir::ItemKind::Impl(impl_) => {
 +                self.in_trait_impl = impl_.of_trait.is_some();
 +            },
 +            hir::ItemKind::Trait(_, unsafety, ..) => {
 +                if !headers.safety && unsafety == hir::Unsafety::Unsafe {
 +                    span_lint(
 +                        cx,
 +                        MISSING_SAFETY_DOC,
 +                        item.span,
 +                        "docs for unsafe trait missing `# Safety` section",
 +                    );
 +                }
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        if let hir::ItemKind::Impl { .. } = item.kind {
 +            self.in_trait_impl = false;
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let headers = check_attrs(cx, &self.valid_idents, attrs);
 +        if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
 +            if !in_external_macro(cx.tcx.sess, item.span) {
 +                lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, None, None);
 +            }
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let headers = check_attrs(cx, &self.valid_idents, attrs);
 +        if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) {
 +            return;
 +        }
 +        if let hir::ImplItemKind::Fn(ref sig, body_id) = item.kind {
 +            let body = cx.tcx.hir().body(body_id);
 +            let mut fpu = FindPanicUnwrap {
 +                cx,
 +                typeck_results: cx.tcx.typeck(item.def_id),
 +                panic_span: None,
 +            };
 +            fpu.visit_expr(&body.value);
 +            lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span);
 +        }
 +    }
 +}
 +
 +fn lint_for_missing_headers<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    def_id: LocalDefId,
 +    span: impl Into<MultiSpan> + Copy,
 +    sig: &hir::FnSig<'_>,
 +    headers: DocHeaders,
 +    body_id: Option<hir::BodyId>,
 +    panic_span: Option<Span>,
 +) {
 +    if !cx.access_levels.is_exported(def_id) {
 +        return; // Private functions do not require doc comments
 +    }
 +
 +    // do not lint if any parent has `#[doc(hidden)]` attribute (#7347)
 +    if cx
 +        .tcx
 +        .hir()
 +        .parent_iter(cx.tcx.hir().local_def_id_to_hir_id(def_id))
 +        .any(|(id, _node)| is_doc_hidden(cx.tcx.hir().attrs(id)))
 +    {
 +        return;
 +    }
 +
 +    if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe {
 +        span_lint(
 +            cx,
 +            MISSING_SAFETY_DOC,
 +            span,
 +            "unsafe function's docs miss `# Safety` section",
 +        );
 +    }
 +    if !headers.panics && panic_span.is_some() {
 +        span_lint_and_note(
 +            cx,
 +            MISSING_PANICS_DOC,
 +            span,
 +            "docs for function which may panic missing `# Panics` section",
 +            panic_span,
 +            "first possible panic found here",
 +        );
 +    }
 +    if !headers.errors {
 +        let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
 +        if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::Result) {
 +            span_lint(
 +                cx,
 +                MISSING_ERRORS_DOC,
 +                span,
 +                "docs for function returning `Result` missing `# Errors` section",
 +            );
 +        } else {
 +            if_chain! {
 +                if let Some(body_id) = body_id;
 +                if let Some(future) = cx.tcx.lang_items().future_trait();
 +                let typeck = cx.tcx.typeck_body(body_id);
 +                let body = cx.tcx.hir().body(body_id);
 +                let ret_ty = typeck.expr_ty(&body.value);
 +                if implements_trait(cx, ret_ty, future, &[]);
 +                if let ty::Opaque(_, subs) = ret_ty.kind();
 +                if let Some(gen) = subs.types().next();
 +                if let ty::Generator(_, subs, _) = gen.kind();
 +                if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym::Result);
 +                then {
 +                    span_lint(
 +                        cx,
 +                        MISSING_ERRORS_DOC,
 +                        span,
 +                        "docs for function returning `Result` missing `# Errors` section",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Cleanup documentation decoration.
 +///
 +/// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or
 +/// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we
 +/// need to keep track of
 +/// the spans but this function is inspired from the later.
 +#[allow(clippy::cast_possible_truncation)]
 +#[must_use]
 +pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) {
 +    // one-line comments lose their prefix
 +    if comment_kind == CommentKind::Line {
 +        let mut doc = doc.to_owned();
 +        doc.push('\n');
 +        let len = doc.len();
 +        // +3 skips the opening delimiter
 +        return (doc, vec![(len, span.with_lo(span.lo() + BytePos(3)))]);
 +    }
 +
 +    let mut sizes = vec![];
 +    let mut contains_initial_stars = false;
 +    for line in doc.lines() {
 +        let offset = line.as_ptr() as usize - doc.as_ptr() as usize;
 +        debug_assert_eq!(offset as u32 as usize, offset);
 +        contains_initial_stars |= line.trim_start().starts_with('*');
 +        // +1 adds the newline, +3 skips the opening delimiter
 +        sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(3 + offset as u32))));
 +    }
 +    if !contains_initial_stars {
 +        return (doc.to_string(), sizes);
 +    }
 +    // remove the initial '*'s if any
 +    let mut no_stars = String::with_capacity(doc.len());
 +    for line in doc.lines() {
 +        let mut chars = line.chars();
 +        for c in &mut chars {
 +            if c.is_whitespace() {
 +                no_stars.push(c);
 +            } else {
 +                no_stars.push(if c == '*' { ' ' } else { c });
 +                break;
 +            }
 +        }
 +        no_stars.push_str(chars.as_str());
 +        no_stars.push('\n');
 +    }
 +
 +    (no_stars, sizes)
 +}
 +
 +#[derive(Copy, Clone)]
 +struct DocHeaders {
 +    safety: bool,
 +    errors: bool,
 +    panics: bool,
 +}
 +
 +fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &'a [Attribute]) -> DocHeaders {
 +    use pulldown_cmark::{BrokenLink, CowStr, Options};
 +    /// We don't want the parser to choke on intra doc links. Since we don't
 +    /// actually care about rendering them, just pretend that all broken links are
 +    /// point to a fake address.
 +    #[allow(clippy::unnecessary_wraps)] // we're following a type signature
 +    fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> {
 +        Some(("fake".into(), "fake".into()))
 +    }
 +
 +    let mut doc = String::new();
 +    let mut spans = vec![];
 +
 +    for attr in attrs {
 +        if let AttrKind::DocComment(comment_kind, comment) = attr.kind {
 +            let (comment, current_spans) = strip_doc_comment_decoration(comment.as_str(), comment_kind, attr.span);
 +            spans.extend_from_slice(&current_spans);
 +            doc.push_str(&comment);
 +        } else if attr.has_name(sym::doc) {
 +            // ignore mix of sugared and non-sugared doc
 +            // don't trigger the safety or errors check
 +            return DocHeaders {
 +                safety: true,
 +                errors: true,
 +                panics: true,
 +            };
 +        }
 +    }
 +
 +    let mut current = 0;
 +    for &mut (ref mut offset, _) in &mut spans {
 +        let offset_copy = *offset;
 +        *offset = current;
 +        current += offset_copy;
 +    }
 +
 +    if doc.is_empty() {
 +        return DocHeaders {
 +            safety: false,
 +            errors: false,
 +            panics: false,
 +        };
 +    }
 +
 +    let mut cb = fake_broken_link_callback;
 +
 +    let parser =
 +        pulldown_cmark::Parser::new_with_broken_link_callback(&doc, Options::empty(), Some(&mut cb)).into_offset_iter();
 +    // Iterate over all `Events` and combine consecutive events into one
 +    let events = parser.coalesce(|previous, current| {
 +        use pulldown_cmark::Event::Text;
 +
 +        let previous_range = previous.1;
 +        let current_range = current.1;
 +
 +        match (previous.0, current.0) {
 +            (Text(previous), Text(current)) => {
 +                let mut previous = previous.to_string();
 +                previous.push_str(&current);
 +                Ok((Text(previous.into()), previous_range))
 +            },
 +            (previous, current) => Err(((previous, previous_range), (current, current_range))),
 +        }
 +    });
 +    check_doc(cx, valid_idents, events, &spans)
 +}
 +
 +const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
 +
 +fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(
 +    cx: &LateContext<'_>,
 +    valid_idents: &FxHashSet<String>,
 +    events: Events,
 +    spans: &[(usize, Span)],
 +) -> DocHeaders {
 +    // true if a safety header was found
 +    use pulldown_cmark::Event::{
 +        Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
 +    };
 +    use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
 +    use pulldown_cmark::{CodeBlockKind, CowStr};
 +
 +    let mut headers = DocHeaders {
 +        safety: false,
 +        errors: false,
 +        panics: false,
 +    };
 +    let mut in_code = false;
 +    let mut in_link = None;
 +    let mut in_heading = false;
 +    let mut is_rust = false;
 +    let mut edition = None;
 +    let mut ticks_unbalanced = false;
 +    let mut text_to_check: Vec<(CowStr<'_>, Span)> = Vec::new();
 +    let mut paragraph_span = spans.get(0).expect("function isn't called if doc comment is empty").1;
 +    for (event, range) in events {
 +        match event {
 +            Start(CodeBlock(ref kind)) => {
 +                in_code = true;
 +                if let CodeBlockKind::Fenced(lang) = kind {
 +                    for item in lang.split(',') {
 +                        if item == "ignore" {
 +                            is_rust = false;
 +                            break;
 +                        }
 +                        if let Some(stripped) = item.strip_prefix("edition") {
 +                            is_rust = true;
 +                            edition = stripped.parse::<Edition>().ok();
 +                        } else if item.is_empty() || RUST_CODE.contains(&item) {
 +                            is_rust = true;
 +                        }
 +                    }
 +                }
 +            },
 +            End(CodeBlock(_)) => {
 +                in_code = false;
 +                is_rust = false;
 +            },
 +            Start(Link(_, url, _)) => in_link = Some(url),
 +            End(Link(..)) => in_link = None,
 +            Start(Heading(_, _, _) | Paragraph | Item) => {
 +                if let Start(Heading(_, _, _)) = event {
 +                    in_heading = true;
 +                }
 +                ticks_unbalanced = false;
 +                let (_, span) = get_current_span(spans, range.start);
 +                paragraph_span = first_line_of_span(cx, span);
 +            },
 +            End(Heading(_, _, _) | Paragraph | Item) => {
 +                if let End(Heading(_, _, _)) = event {
 +                    in_heading = false;
 +                }
 +                if ticks_unbalanced {
 +                    span_lint_and_help(
 +                        cx,
 +                        DOC_MARKDOWN,
 +                        paragraph_span,
 +                        "backticks are unbalanced",
 +                        None,
 +                        "a backtick may be missing a pair",
 +                    );
 +                } else {
 +                    for (text, span) in text_to_check {
 +                        check_text(cx, valid_idents, &text, span);
 +                    }
 +                }
 +                text_to_check = Vec::new();
 +            },
 +            Start(_tag) | End(_tag) => (), // We don't care about other tags
 +            Html(_html) => (),             // HTML is weird, just ignore it
 +            SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
 +            FootnoteReference(text) | Text(text) => {
 +                let (begin, span) = get_current_span(spans, range.start);
 +                paragraph_span = paragraph_span.with_hi(span.hi());
 +                ticks_unbalanced |= text.contains('`') && !in_code;
 +                if Some(&text) == in_link.as_ref() || ticks_unbalanced {
 +                    // Probably a link of the form `<http://example.com>`
 +                    // Which are represented as a link to "http://example.com" with
 +                    // text "http://example.com" by pulldown-cmark
 +                    continue;
 +                }
 +                let trimmed_text = text.trim();
 +                headers.safety |= in_heading && trimmed_text == "Safety";
 +                headers.safety |= in_heading && trimmed_text == "Implementation safety";
 +                headers.safety |= in_heading && trimmed_text == "Implementation Safety";
 +                headers.errors |= in_heading && trimmed_text == "Errors";
 +                headers.panics |= in_heading && trimmed_text == "Panics";
 +                if in_code {
 +                    if is_rust {
 +                        let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
 +                        check_code(cx, &text, edition, span);
 +                    }
 +                } else {
 +                    // Adjust for the beginning of the current `Event`
 +                    let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
 +                    text_to_check.push((text, span));
 +                }
 +            },
 +        }
 +    }
 +    headers
 +}
 +
 +fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) {
 +    let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) {
 +        Ok(o) => o,
 +        Err(e) => e - 1,
 +    };
 +    spans[index]
 +}
 +
 +fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
 +    fn has_needless_main(code: String, edition: Edition) -> bool {
 +        rustc_driver::catch_fatal_errors(|| {
 +            rustc_span::create_session_globals_then(edition, || {
 +                let filename = FileName::anon_source_code(&code);
 +
 +                let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-         if s.starts_with(|c: char| c.is_digit(10)) {
++                let fallback_bundle =
++                    rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
 +                let emitter = EmitterWriter::new(
 +                    Box::new(io::sink()),
 +                    None,
 +                    None,
 +                    fallback_bundle,
 +                    false,
 +                    false,
 +                    false,
 +                    None,
 +                    false,
 +                );
 +                let handler = Handler::with_emitter(false, None, Box::new(emitter));
 +                let sess = ParseSess::with_span_handler(handler, sm);
 +
 +                let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
 +                    Ok(p) => p,
 +                    Err(errs) => {
 +                        drop(errs);
 +                        return false;
 +                    },
 +                };
 +
 +                let mut relevant_main_found = false;
 +                loop {
 +                    match parser.parse_item(ForceCollect::No) {
 +                        Ok(Some(item)) => match &item.kind {
 +                            ItemKind::Fn(box Fn {
 +                                sig, body: Some(block), ..
 +                            }) if item.ident.name == sym::main => {
 +                                let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
 +                                let returns_nothing = match &sig.decl.output {
 +                                    FnRetTy::Default(..) => true,
 +                                    FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
 +                                    FnRetTy::Ty(_) => false,
 +                                };
 +
 +                                if returns_nothing && !is_async && !block.stmts.is_empty() {
 +                                    // This main function should be linted, but only if there are no other functions
 +                                    relevant_main_found = true;
 +                                } else {
 +                                    // This main function should not be linted, we're done
 +                                    return false;
 +                                }
 +                            },
 +                            // Tests with one of these items are ignored
 +                            ItemKind::Static(..)
 +                            | ItemKind::Const(..)
 +                            | ItemKind::ExternCrate(..)
 +                            | ItemKind::ForeignMod(..)
 +                            // Another function was found; this case is ignored
 +                            | ItemKind::Fn(..) => return false,
 +                            _ => {},
 +                        },
 +                        Ok(None) => break,
 +                        Err(e) => {
 +                            e.cancel();
 +                            return false;
 +                        },
 +                    }
 +                }
 +
 +                relevant_main_found
 +            })
 +        })
 +        .ok()
 +        .unwrap_or_default()
 +    }
 +
 +    // Because of the global session, we need to create a new session in a different thread with
 +    // the edition we need.
 +    let text = text.to_owned();
 +    if thread::spawn(move || has_needless_main(text, edition))
 +        .join()
 +        .expect("thread::spawn failed")
 +    {
 +        span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
 +    }
 +}
 +
 +fn check_text(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) {
 +    for word in text.split(|c: char| c.is_whitespace() || c == '\'') {
 +        // Trim punctuation as in `some comment (see foo::bar).`
 +        //                                                   ^^
 +        // Or even as in `_foo bar_` which is emphasized. Also preserve `::` as a prefix/suffix.
 +        let mut word = word.trim_matches(|c: char| !c.is_alphanumeric() && c != ':');
 +
 +        // Remove leading or trailing single `:` which may be part of a sentence.
 +        if word.starts_with(':') && !word.starts_with("::") {
 +            word = word.trim_start_matches(':');
 +        }
 +        if word.ends_with(':') && !word.ends_with("::") {
 +            word = word.trim_end_matches(':');
 +        }
 +
 +        if valid_idents.contains(word) || word.chars().all(|c| c == ':') {
 +            continue;
 +        }
 +
 +        // Adjust for the current word
 +        let offset = word.as_ptr() as usize - text.as_ptr() as usize;
 +        let span = Span::new(
 +            span.lo() + BytePos::from_usize(offset),
 +            span.lo() + BytePos::from_usize(offset + word.len()),
 +            span.ctxt(),
 +            span.parent(),
 +        );
 +
 +        check_word(cx, word, span);
 +    }
 +}
 +
 +fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
 +    /// Checks if a string is camel-case, i.e., contains at least two uppercase
 +    /// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok).
 +    /// Plurals are also excluded (`IDs` is ok).
 +    fn is_camel_case(s: &str) -> bool {
++        if s.starts_with(|c: char| c.is_ascii_digit()) {
 +            return false;
 +        }
 +
 +        let s = s.strip_suffix('s').unwrap_or(s);
 +
 +        s.chars().all(char::is_alphanumeric)
 +            && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1
 +            && s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
 +    }
 +
 +    fn has_underscore(s: &str) -> bool {
 +        s != "_" && !s.contains("\\_") && s.contains('_')
 +    }
 +
 +    fn has_hyphen(s: &str) -> bool {
 +        s != "-" && s.contains('-')
 +    }
 +
 +    if let Ok(url) = Url::parse(word) {
 +        // try to get around the fact that `foo::bar` parses as a valid URL
 +        if !url.cannot_be_a_base() {
 +            span_lint(
 +                cx,
 +                DOC_MARKDOWN,
 +                span,
 +                "you should put bare URLs between `<`/`>` or make a proper Markdown link",
 +            );
 +
 +            return;
 +        }
 +    }
 +
 +    // We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343)
 +    if has_underscore(word) && has_hyphen(word) {
 +        return;
 +    }
 +
 +    if has_underscore(word) || word.contains("::") || is_camel_case(word) {
 +        let mut applicability = Applicability::MachineApplicable;
 +
 +        span_lint_and_then(
 +            cx,
 +            DOC_MARKDOWN,
 +            span,
 +            "item in documentation is missing backticks",
 +            |diag| {
 +                let snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
 +                diag.span_suggestion_with_style(
 +                    span,
 +                    "try",
 +                    format!("`{}`", snippet),
 +                    applicability,
 +                    // always show the suggestion in a separate line, since the
 +                    // inline presentation adds another pair of backticks
 +                    SuggestionStyle::ShowAlways,
 +                );
 +            },
 +        );
 +    }
 +}
 +
 +struct FindPanicUnwrap<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    panic_span: Option<Span>,
 +    typeck_results: &'tcx ty::TypeckResults<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
 +    type NestedFilter = nested_filter::OnlyBodies;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.panic_span.is_some() {
 +            return;
 +        }
 +
 +        if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) {
 +            if is_panic(self.cx, macro_call.def_id)
 +                || matches!(
 +                    self.cx.tcx.item_name(macro_call.def_id).as_str(),
 +                    "assert" | "assert_eq" | "assert_ne" | "todo"
 +                )
 +            {
 +                self.panic_span = Some(macro_call.span);
 +            }
 +        }
 +
 +        // check for `unwrap`
 +        if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
 +            let receiver_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
 +            if is_type_diagnostic_item(self.cx, receiver_ty, sym::Option)
 +                || is_type_diagnostic_item(self.cx, receiver_ty, sym::Result)
 +            {
 +                self.panic_span = Some(expr.span);
 +            }
 +        }
 +
 +        // and check sub-expressions
 +        intravisit::walk_expr(self, expr);
 +    }
 +
 +    // Panics in const blocks will cause compilation to fail.
 +    fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
index 88c54828da834da3e94ae9d513a76102e1d1f0c9,0000000000000000000000000000000000000000..25014bfa1a5b12595b41415a2010317ac7952143
mode 100644,000000..100644
--- /dev/null
@@@ -1,243 -1,0 +1,243 @@@
-                                  Dropping such a type only extends it's contained lifetimes";
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
 +use clippy_utils::is_must_use_func_call;
 +use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item};
 +use rustc_hir::{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 calls to `std::mem::drop` with a reference
 +    /// instead of an owned value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `drop` on a reference will only drop the
 +    /// reference itself, which is a no-op. It will not call the `drop` method (from
 +    /// the `Drop` trait implementation) on the underlying referenced value, which
 +    /// is likely what was intended.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// let mut lock_guard = mutex.lock();
 +    /// std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex
 +    /// // still locked
 +    /// operation_that_requires_mutex_to_be_unlocked();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DROP_REF,
 +    correctness,
 +    "calls to `std::mem::drop` with a reference instead of an owned value"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `std::mem::forget` with a reference
 +    /// instead of an owned value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `forget` on a reference will only forget the
 +    /// reference itself, which is a no-op. It will not forget the underlying
 +    /// referenced
 +    /// value, which is likely what was intended.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = Box::new(1);
 +    /// std::mem::forget(&x) // Should have been forget(x), x will still be dropped
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FORGET_REF,
 +    correctness,
 +    "calls to `std::mem::forget` with a reference instead of an owned value"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `std::mem::drop` with a value
 +    /// that derives the Copy trait
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `std::mem::drop` [does nothing for types that
 +    /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the
 +    /// value will be copied and moved into the function on invocation.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: i32 = 42; // i32 implements Copy
 +    /// std::mem::drop(x) // A copy of x is passed to the function, leaving the
 +    ///                   // original unaffected
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DROP_COPY,
 +    correctness,
 +    "calls to `std::mem::drop` with a value that implements Copy"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `std::mem::forget` with a value that
 +    /// derives the Copy trait
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `std::mem::forget` [does nothing for types that
 +    /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the
 +    /// value will be copied and moved into the function on invocation.
 +    ///
 +    /// An alternative, but also valid, explanation is that Copy types do not
 +    /// implement
 +    /// the Drop trait, which means they have no destructors. Without a destructor,
 +    /// there
 +    /// is nothing for `std::mem::forget` to ignore.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: i32 = 42; // i32 implements Copy
 +    /// std::mem::forget(x) // A copy of x is passed to the function, leaving the
 +    ///                     // original unaffected
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FORGET_COPY,
 +    correctness,
 +    "calls to `std::mem::forget` with a value that implements Copy"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `std::mem::drop` with a value that does not implement `Drop`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `std::mem::drop` is no different than dropping such a type. A different value may
 +    /// have been intended.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Foo;
 +    /// let x = Foo;
 +    /// std::mem::drop(x);
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub DROP_NON_DROP,
 +    suspicious,
 +    "call to `std::mem::drop` with a value which does not implement `Drop`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `std::mem::forget` with a value that does not implement `Drop`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `std::mem::forget` is no different than dropping such a type. A different value may
 +    /// have been intended.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Foo;
 +    /// let x = Foo;
 +    /// std::mem::forget(x);
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub FORGET_NON_DROP,
 +    suspicious,
 +    "call to `std::mem::forget` with a value which does not implement `Drop`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The safe `drop` function does not drop the inner value of a `ManuallyDrop`.
 +    ///
 +    /// ### Known problems
 +    /// Does not catch cases if the user binds `std::mem::drop`
 +    /// to a different name and calls it that way.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct S;
 +    /// drop(std::mem::ManuallyDrop::new(S));
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// struct S;
 +    /// unsafe {
 +    ///     std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub UNDROPPED_MANUALLY_DROPS,
 +    correctness,
 +    "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value"
 +}
 +
 +const DROP_REF_SUMMARY: &str = "calls to `std::mem::drop` with a reference instead of an owned value. \
 +                                Dropping a reference does nothing";
 +const FORGET_REF_SUMMARY: &str = "calls to `std::mem::forget` with a reference instead of an owned value. \
 +                                  Forgetting a reference does nothing";
 +const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that implements `Copy`. \
 +                                 Dropping a copy leaves the original intact";
 +const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \
 +                                   Forgetting a copy leaves the original intact";
 +const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \
++                                 Dropping such a type only extends its contained lifetimes";
 +const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \
 +                                   Forgetting such a type is the same as dropping it";
 +
 +declare_lint_pass!(DropForgetRef => [
 +    DROP_REF,
 +    FORGET_REF,
 +    DROP_COPY,
 +    FORGET_COPY,
 +    DROP_NON_DROP,
 +    FORGET_NON_DROP,
 +    UNDROPPED_MANUALLY_DROPS
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::Call(path, [arg]) = expr.kind
 +            && let ExprKind::Path(ref qpath) = path.kind
 +            && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
 +            && let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id)
 +        {
 +            let arg_ty = cx.typeck_results().expr_ty(arg);
 +            let (lint, msg) = match fn_name {
 +                sym::mem_drop if arg_ty.is_ref() => (DROP_REF, DROP_REF_SUMMARY),
 +                sym::mem_forget if arg_ty.is_ref() => (FORGET_REF, FORGET_REF_SUMMARY),
 +                sym::mem_drop if is_copy(cx, arg_ty) => (DROP_COPY, DROP_COPY_SUMMARY),
 +                sym::mem_forget if is_copy(cx, arg_ty) => (FORGET_COPY, FORGET_COPY_SUMMARY),
 +                sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => {
 +                    span_lint_and_help(
 +                        cx,
 +                        UNDROPPED_MANUALLY_DROPS,
 +                        expr.span,
 +                        "the inner value of this ManuallyDrop will not be dropped",
 +                        None,
 +                        "to drop a `ManuallyDrop<T>`, use std::mem::ManuallyDrop::drop",
 +                    );
 +                    return;
 +                }
 +                sym::mem_drop
 +                    if !(arg_ty.needs_drop(cx.tcx, cx.param_env)
 +                        || is_must_use_func_call(cx, arg)
 +                        || is_must_use_ty(cx, arg_ty)) =>
 +                {
 +                    (DROP_NON_DROP, DROP_NON_DROP_SUMMARY)
 +                },
 +                sym::mem_forget if !arg_ty.needs_drop(cx.tcx, cx.param_env) => {
 +                    (FORGET_NON_DROP, FORGET_NON_DROP_SUMMARY)
 +                },
 +                _ => return,
 +            };
 +            span_lint_and_note(
 +                cx,
 +                lint,
 +                expr.span,
 +                msg,
 +                Some(arg.span),
 +                &format!("argument has type `{}`", arg_ty),
 +            );
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..325ae2356c14cb255531b3df3950d3208feaa39e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,65 @@@
++use clippy_utils::{diagnostics::span_lint_and_sugg, peel_blocks};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for empty `Drop` implementations.
++    ///
++    /// ### Why is this bad?
++    /// Empty `Drop` implementations have no effect when dropping an instance of the type. They are
++    /// most likely useless. However, an empty `Drop` implementation prevents a type from being
++    /// destructured, which might be the intention behind adding the implementation as a marker.
++    ///
++    /// ### Example
++    /// ```rust
++    /// struct S;
++    ///
++    /// impl Drop for S {
++    ///     fn drop(&mut self) {}
++    /// }
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// struct S;
++    /// ```
++    #[clippy::version = "1.61.0"]
++    pub EMPTY_DROP,
++    restriction,
++    "empty `Drop` implementations"
++}
++declare_lint_pass!(EmptyDrop => [EMPTY_DROP]);
++
++impl LateLintPass<'_> for EmptyDrop {
++    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
++        if_chain! {
++            if let ItemKind::Impl(Impl {
++                of_trait: Some(ref trait_ref),
++                items: [child],
++                ..
++            }) = item.kind;
++            if trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait();
++            if let impl_item_hir = child.id.hir_id();
++            if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir);
++            if let ImplItemKind::Fn(_, b) = &impl_item.kind;
++            if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
++            let func_expr = peel_blocks(func_expr);
++            if let ExprKind::Block(block, _) = func_expr.kind;
++            if block.stmts.is_empty() && block.expr.is_none();
++            then {
++                span_lint_and_sugg(
++                    cx,
++                    EMPTY_DROP,
++                    item.span,
++                    "empty drop implementation",
++                    "try removing this impl",
++                    String::new(),
++                    Applicability::MaybeIncorrect
++                );
++            }
++        }
++    }
++}
index fdeac8d82557fd518cb54143fe152246070bd2bb,0000000000000000000000000000000000000000..8430e7b4c82713554b0107be84ef57d269c1a483
mode 100644,000000..100644
--- /dev/null
@@@ -1,99 -1,0 +1,99 @@@
-     // (conditionaly compiled code using #[cfg(..)])
 +use clippy_utils::{diagnostics::span_lint_and_then, source::snippet_opt};
 +use rustc_ast::ast::{Item, ItemKind, VariantData};
 +use rustc_errors::Applicability;
 +use rustc_lexer::TokenKind;
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Finds structs without fields (a so-called "empty struct") that are declared with brackets.
 +    ///
 +    /// ### Why is this bad?
 +    /// Empty brackets after a struct declaration can be omitted.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Cookie {}
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// struct Cookie;
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub EMPTY_STRUCTS_WITH_BRACKETS,
 +    restriction,
 +    "finds struct declarations with empty brackets"
 +}
 +declare_lint_pass!(EmptyStructsWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS]);
 +
 +impl EarlyLintPass for EmptyStructsWithBrackets {
 +    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
 +        let span_after_ident = item.span.with_lo(item.ident.span.hi());
 +
 +        if let ItemKind::Struct(var_data, _) = &item.kind
 +            && has_brackets(var_data)
 +            && has_no_fields(cx, var_data, span_after_ident) {
 +            span_lint_and_then(
 +                cx,
 +                EMPTY_STRUCTS_WITH_BRACKETS,
 +                span_after_ident,
 +                "found empty brackets on struct declaration",
 +                |diagnostic| {
 +                    diagnostic.span_suggestion_hidden(
 +                        span_after_ident,
 +                        "remove the brackets",
 +                        ";".to_string(),
 +                        Applicability::MachineApplicable);
 +                    },
 +            );
 +        }
 +    }
 +}
 +
 +fn has_no_ident_token(braces_span_str: &str) -> bool {
 +    !rustc_lexer::tokenize(braces_span_str).any(|t| t.kind == TokenKind::Ident)
 +}
 +
 +fn has_brackets(var_data: &VariantData) -> bool {
 +    !matches!(var_data, VariantData::Unit(_))
 +}
 +
 +fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Span) -> bool {
 +    if !var_data.fields().is_empty() {
 +        return false;
 +    }
 +
 +    // there might still be field declarations hidden from the AST
++    // (conditionally compiled code using #[cfg(..)])
 +
 +    let Some(braces_span_str) = snippet_opt(cx, braces_span) else {
 +        return false;
 +    };
 +
 +    has_no_ident_token(braces_span_str.as_ref())
 +}
 +
 +#[cfg(test)]
 +mod unit_test {
 +    use super::*;
 +
 +    #[test]
 +    fn test_has_no_ident_token() {
 +        let input = "{ field: u8 }";
 +        assert!(!has_no_ident_token(input));
 +
 +        let input = "(u8, String);";
 +        assert!(!has_no_ident_token(input));
 +
 +        let input = " {
 +                // test = 5
 +        }
 +        ";
 +        assert!(has_no_ident_token(input));
 +
 +        let input = " ();";
 +        assert!(has_no_ident_token(input));
 +    }
 +}
index 845863bd209c6ef4cc9696b68341bf9fee537437,0000000000000000000000000000000000000000..1b19868e4c70faf2d33f6a5911528f971b25672b
mode 100644,000000..100644
--- /dev/null
@@@ -1,232 -1,0 +1,233 @@@
- use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id};
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::higher::VecArgs;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::usage::local_used_after_expr;
-                             if get_enclosing_loop_or_closure(cx.tcx, expr).is_some()
-                                 || path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, callee));
++use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{Expr, ExprKind, Param, PatKind, Unsafety};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 +use rustc_middle::ty::binding::BindingMode;
 +use rustc_middle::ty::subst::Subst;
 +use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for closures which just call another function where
 +    /// the function can be called directly. `unsafe` functions or calls where types
 +    /// get adjusted are ignored.
 +    ///
 +    /// ### Why is this bad?
 +    /// Needlessly creating a closure adds code for no benefit
 +    /// and gives the optimizer more work.
 +    ///
 +    /// ### Known problems
 +    /// If creating the closure inside the closure has a side-
 +    /// effect then moving the closure creation out will change when that side-
 +    /// effect runs.
 +    /// See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// xs.map(|x| foo(x))
 +    ///
 +    /// // Good
 +    /// xs.map(foo)
 +    /// ```
 +    /// where `foo(_)` is a plain function that takes the exact argument type of
 +    /// `x`.
 +    #[clippy::version = "pre 1.29.0"]
 +    pub REDUNDANT_CLOSURE,
 +    style,
 +    "redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for closures which only invoke a method on the closure
 +    /// argument and can be replaced by referencing the method directly.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's unnecessary to create the closure.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// Some('a').map(|s| s.to_uppercase());
 +    /// ```
 +    /// may be rewritten as
 +    /// ```rust,ignore
 +    /// Some('a').map(char::to_uppercase);
 +    /// ```
 +    #[clippy::version = "1.35.0"]
 +    pub REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
 +    pedantic,
 +    "redundant closures for method calls"
 +}
 +
 +declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for EtaReduction {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +        let body = match expr.kind {
 +            ExprKind::Closure(_, _, id, _, _) => cx.tcx.hir().body(id),
 +            _ => return,
 +        };
 +        if body.value.span.from_expansion() {
 +            if body.params.is_empty() {
 +                if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, &body.value) {
 +                    // replace `|| vec![]` with `Vec::new`
 +                    span_lint_and_sugg(
 +                        cx,
 +                        REDUNDANT_CLOSURE,
 +                        expr.span,
 +                        "redundant closure",
 +                        "replace the closure with `Vec::new`",
 +                        "std::vec::Vec::new".into(),
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            }
 +            // skip `foo(|| macro!())`
 +            return;
 +        }
 +
 +        let closure_ty = cx.typeck_results().expr_ty(expr);
 +
 +        if_chain!(
++            if !is_adjusted(cx, &body.value);
 +            if let ExprKind::Call(callee, args) = body.value.kind;
 +            if let ExprKind::Path(_) = callee.kind;
 +            if check_inputs(cx, body.params, args);
 +            let callee_ty = cx.typeck_results().expr_ty_adjusted(callee);
 +            let call_ty = cx.typeck_results().type_dependent_def_id(body.value.hir_id)
 +                .map_or(callee_ty, |id| cx.tcx.type_of(id));
 +            if check_sig(cx, closure_ty, call_ty);
 +            let substs = cx.typeck_results().node_substs(callee.hir_id);
 +            // This fixes some false positives that I don't entirely understand
 +            if substs.is_empty() || !cx.typeck_results().expr_ty(expr).has_late_bound_regions();
 +            // A type param function ref like `T::f` is not 'static, however
 +            // it is if cast like `T::f as fn()`. This seems like a rustc bug.
 +            if !substs.types().any(|t| matches!(t.kind(), ty::Param(_)));
 +            let callee_ty_unadjusted = cx.typeck_results().expr_ty(callee).peel_refs();
 +            if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc);
 +            if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc);
 +            then {
 +                span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
 +                    if let Some(mut snippet) = snippet_opt(cx, callee.span) {
 +                        if_chain! {
 +                            if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
 +                            if substs.as_closure().kind() == ClosureKind::FnMut;
++                            if path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr));
 +
 +                            then {
 +                                // Mutable closure is used after current expr; we cannot consume it.
 +                                snippet = format!("&mut {}", snippet);
 +                            }
 +                        }
 +                        diag.span_suggestion(
 +                            expr.span,
 +                            "replace the closure with the function itself",
 +                            snippet,
 +                            Applicability::MachineApplicable,
 +                        );
 +                    }
 +                });
 +            }
 +        );
 +
 +        if_chain!(
++            if !is_adjusted(cx, &body.value);
 +            if let ExprKind::MethodCall(path, args, _) = body.value.kind;
 +            if check_inputs(cx, body.params, args);
 +            let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
 +            let substs = cx.typeck_results().node_substs(body.value.hir_id);
 +            let call_ty = cx.tcx.type_of(method_def_id).subst(cx.tcx, substs);
 +            if check_sig(cx, closure_ty, call_ty);
 +            then {
 +                span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
 +                    let name = get_ufcs_type_name(cx, method_def_id);
 +                    diag.span_suggestion(
 +                        expr.span,
 +                        "replace the closure with the method itself",
 +                        format!("{}::{}", name, path.ident.name),
 +                        Applicability::MachineApplicable,
 +                    );
 +                })
 +            }
 +        );
 +    }
 +}
 +
 +fn check_inputs(cx: &LateContext<'_>, params: &[Param<'_>], call_args: &[Expr<'_>]) -> bool {
 +    if params.len() != call_args.len() {
 +        return false;
 +    }
 +    let binding_modes = cx.typeck_results().pat_binding_modes();
 +    std::iter::zip(params, call_args).all(|(param, arg)| {
 +        match param.pat.kind {
 +            PatKind::Binding(_, id, ..) if path_to_local_id(arg, id) => {},
 +            _ => return false,
 +        }
 +        // checks that parameters are not bound as `ref` or `ref mut`
 +        if let Some(BindingMode::BindByReference(_)) = binding_modes.get(param.pat.hir_id) {
 +            return false;
 +        }
 +
 +        match *cx.typeck_results().expr_adjustments(arg) {
 +            [] => true,
 +            [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(AutoBorrow::Ref(_, mu2)),
 +                    ..
 +                },
 +            ] => {
 +                // re-borrow with the same mutability is allowed
 +                let ty = cx.typeck_results().expr_ty(arg);
 +                matches!(*ty.kind(), ty::Ref(.., mu1) if mu1 == mu2.into())
 +            },
 +            _ => false,
 +        }
 +    })
 +}
 +
 +fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tcx>) -> bool {
 +    let call_sig = call_ty.fn_sig(cx.tcx);
 +    if call_sig.unsafety() == Unsafety::Unsafe {
 +        return false;
 +    }
 +    if !closure_ty.has_late_bound_regions() {
 +        return true;
 +    }
 +    let substs = match closure_ty.kind() {
 +        ty::Closure(_, substs) => substs,
 +        _ => return false,
 +    };
 +    let closure_sig = cx.tcx.signature_unclosure(substs.as_closure().sig(), Unsafety::Normal);
 +    cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig)
 +}
 +
 +fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String {
 +    match cx.tcx.associated_item(method_def_id).container {
 +        ty::TraitContainer(def_id) => cx.tcx.def_path_str(def_id),
 +        ty::ImplContainer(def_id) => {
 +            let ty = cx.tcx.type_of(def_id);
 +            match ty.kind() {
 +                ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did()),
 +                _ => ty.to_string(),
 +            }
 +        },
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ee15ae9f59aca8eb96429d52464b3c53ae689a5b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,77 @@@
++use clippy_utils::diagnostics::span_lint_and_help;
++use clippy_utils::ty::is_type_diagnostic_item;
++use clippy_utils::{match_def_path, paths, peel_hir_expr_refs};
++use rustc_hir::{BinOpKind, 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
++    /// Detects cases where the result of a `format!` call is
++    /// appended to an existing `String`.
++    ///
++    /// ### Why is this bad?
++    /// Introduces an extra, avoidable heap allocation.
++    ///
++    /// ### Example
++    /// ```rust
++    /// let mut s = String::new();
++    /// s += &format!("0x{:X}", 1024);
++    /// s.push_str(&format!("0x{:X}", 1024));
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// use std::fmt::Write as _; // import without risk of name clashing
++    ///
++    /// let mut s = String::new();
++    /// let _ = write!(s, "0x{:X}", 1024);
++    /// ```
++    #[clippy::version = "1.61.0"]
++    pub FORMAT_PUSH_STRING,
++    perf,
++    "`format!(..)` appended to existing `String`"
++}
++declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]);
++
++fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
++    is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String)
++}
++fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
++    if let Some(macro_def_id) = e.span.ctxt().outer_expn_data().macro_def_id {
++        cx.tcx.get_diagnostic_name(macro_def_id) == Some(sym::format_macro)
++    } else {
++        false
++    }
++}
++
++impl<'tcx> LateLintPass<'tcx> for FormatPushString {
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
++        let arg = match expr.kind {
++            ExprKind::MethodCall(_, [_, arg], _) => {
++                if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) &&
++                match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
++                    arg
++                } else {
++                    return;
++                }
++            }
++            ExprKind::AssignOp(op, left, arg)
++            if op.node == BinOpKind::Add && is_string(cx, left) => {
++                arg
++            },
++            _ => return,
++        };
++        let (arg, _) = peel_hir_expr_refs(arg);
++        if is_format(cx, arg) {
++            span_lint_and_help(
++                cx,
++                FORMAT_PUSH_STRING,
++                expr.span,
++                "`format!(..)` appended to existing `String`",
++                None,
++                "consider using `write!` to avoid the extra allocation",
++            );
++        }
++    }
++}
index 5462d913fb4414bf554e3c73d1bd5f0697493413,0000000000000000000000000000000000000000..38e943d2eb8720cd98a0039e5dc3ac188c40d6b8
mode 100644,000000..100644
--- /dev/null
@@@ -1,264 -1,0 +1,259 @@@
-     if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind {
 +use rustc_ast::ast::Attribute;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::{DefIdSet, LocalDefId};
 +use rustc_hir::{self as hir, def::Res, intravisit, QPath};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::{
 +    lint::in_external_macro,
 +    ty::{self, Ty},
 +};
 +use rustc_span::{sym, Span};
 +
 +use clippy_utils::attrs::is_proc_macro;
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::is_must_use_ty;
 +use clippy_utils::{match_def_path, must_use_attr, return_ty, trait_ref_of_method};
 +
 +use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT};
 +
 +pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +    let attrs = cx.tcx.hir().attrs(item.hir_id());
 +    let attr = must_use_attr(attrs);
-                 diag.span_suggestion(
-                     attr.span,
-                     "remove the attribute",
-                     "",
-                     Applicability::MachineApplicable,
-                 );
++    if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind {
 +        let is_public = cx.access_levels.is_exported(item.def_id);
 +        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +        if let Some(attr) = attr {
 +            check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
 +        } else if is_public && !is_proc_macro(cx.sess(), attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) {
 +            check_must_use_candidate(
 +                cx,
 +                sig.decl,
 +                cx.tcx.hir().body(*body_id),
 +                item.span,
 +                item.def_id,
 +                item.span.with_hi(sig.decl.output.span().hi()),
 +                "this function could have a `#[must_use]` attribute",
 +            );
 +        }
 +    }
 +}
 +
 +pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
 +    if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind {
 +        let is_public = cx.access_levels.is_exported(item.def_id);
 +        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let attr = must_use_attr(attrs);
 +        if let Some(attr) = attr {
 +            check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
 +        } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.def_id).is_none() {
 +            check_must_use_candidate(
 +                cx,
 +                sig.decl,
 +                cx.tcx.hir().body(*body_id),
 +                item.span,
 +                item.def_id,
 +                item.span.with_hi(sig.decl.output.span().hi()),
 +                "this method could have a `#[must_use]` attribute",
 +            );
 +        }
 +    }
 +}
 +
 +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
 +    if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind {
 +        let is_public = cx.access_levels.is_exported(item.def_id);
 +        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let attr = must_use_attr(attrs);
 +        if let Some(attr) = attr {
 +            check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
 +        } else if let hir::TraitFn::Provided(eid) = *eid {
 +            let body = cx.tcx.hir().body(eid);
 +            if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) {
 +                check_must_use_candidate(
 +                    cx,
 +                    sig.decl,
 +                    body,
 +                    item.span,
 +                    item.def_id,
 +                    item.span.with_hi(sig.decl.output.span().hi()),
 +                    "this method could have a `#[must_use]` attribute",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn check_needless_must_use(
 +    cx: &LateContext<'_>,
 +    decl: &hir::FnDecl<'_>,
 +    item_id: hir::HirId,
 +    item_span: Span,
 +    fn_header_span: Span,
 +    attr: &Attribute,
 +) {
 +    if in_external_macro(cx.sess(), item_span) {
 +        return;
 +    }
 +    if returns_unit(decl) {
 +        span_lint_and_then(
 +            cx,
 +            MUST_USE_UNIT,
 +            fn_header_span,
 +            "this unit-returning function has a `#[must_use]` attribute",
 +            |diag| {
++                diag.span_suggestion(attr.span, "remove the attribute", "", Applicability::MachineApplicable);
 +            },
 +        );
 +    } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
 +        span_lint_and_help(
 +            cx,
 +            DOUBLE_MUST_USE,
 +            fn_header_span,
 +            "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`",
 +            None,
 +            "either add some descriptive text or remove the attribute",
 +        );
 +    }
 +}
 +
 +fn check_must_use_candidate<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    decl: &'tcx hir::FnDecl<'_>,
 +    body: &'tcx hir::Body<'_>,
 +    item_span: Span,
 +    item_id: LocalDefId,
 +    fn_span: Span,
 +    msg: &str,
 +) {
 +    if has_mutable_arg(cx, body)
 +        || mutates_static(cx, body)
 +        || in_external_macro(cx.sess(), item_span)
 +        || returns_unit(decl)
 +        || !cx.access_levels.is_exported(item_id)
 +        || is_must_use_ty(cx, return_ty(cx, cx.tcx.hir().local_def_id_to_hir_id(item_id)))
 +    {
 +        return;
 +    }
 +    span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| {
 +        if let Some(snippet) = snippet_opt(cx, fn_span) {
 +            diag.span_suggestion(
 +                fn_span,
 +                "add the attribute",
 +                format!("#[must_use] {}", snippet),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    });
 +}
 +
 +fn returns_unit(decl: &hir::FnDecl<'_>) -> bool {
 +    match decl.output {
 +        hir::FnRetTy::DefaultReturn(_) => true,
 +        hir::FnRetTy::Return(ty) => match ty.kind {
 +            hir::TyKind::Tup(tys) => tys.is_empty(),
 +            hir::TyKind::Never => true,
 +            _ => false,
 +        },
 +    }
 +}
 +
 +fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool {
 +    let mut tys = DefIdSet::default();
 +    body.params.iter().any(|param| is_mutable_pat(cx, param.pat, &mut tys))
 +}
 +
 +fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) -> bool {
 +    if let hir::PatKind::Wild = pat.kind {
 +        return false; // ignore `_` patterns
 +    }
 +    if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) {
 +        is_mutable_ty(cx, cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys)
 +    } else {
 +        false
 +    }
 +}
 +
 +static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]];
 +
 +fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut DefIdSet) -> bool {
 +    match *ty.kind() {
 +        // primitive types are never mutable
 +        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
 +        ty::Adt(adt, substs) => {
 +            tys.insert(adt.did()) && !ty.is_freeze(cx.tcx.at(span), cx.param_env)
 +                || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did(), path))
 +                    && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys))
 +        },
 +        ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, span, tys)),
 +        ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys),
 +        ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => {
 +            mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys)
 +        },
 +        // calling something constitutes a side effect, so return true on all callables
 +        // also never calls need not be used, so return true for them, too
 +        _ => true,
 +    }
 +}
 +
 +struct StaticMutVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    mutates_static: bool,
 +}
 +
 +impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
 +        use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
 +
 +        if self.mutates_static {
 +            return;
 +        }
 +        match expr.kind {
 +            Call(_, args) | MethodCall(_, args, _) => {
 +                let mut tys = DefIdSet::default();
 +                for arg in args {
 +                    if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
 +                        && is_mutable_ty(
 +                            self.cx,
 +                            self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg),
 +                            arg.span,
 +                            &mut tys,
 +                        )
 +                        && is_mutated_static(arg)
 +                    {
 +                        self.mutates_static = true;
 +                        return;
 +                    }
 +                    tys.clear();
 +                }
 +            },
 +            Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target) => {
 +                self.mutates_static |= is_mutated_static(target);
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
 +    use hir::ExprKind::{Field, Index, Path};
 +
 +    match e.kind {
 +        Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)),
 +        Path(_) => true,
 +        Field(inner, _) | Index(inner, _) => is_mutated_static(inner),
 +        _ => false,
 +    }
 +}
 +
 +fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool {
 +    let mut v = StaticMutVisitor {
 +        cx,
 +        mutates_static: false,
 +    };
 +    intravisit::walk_expr(&mut v, &body.value);
 +    v.mutates_static
 +}
index 120fcb2619c7c66dd59dd58bdb3b7c1ea8b30170,0000000000000000000000000000000000000000..2e63a1f920d64b39a69ad214ecd4cb6e03ada3a2
mode 100644,000000..100644
--- /dev/null
@@@ -1,66 -1,0 +1,66 @@@
-     if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind {
 +use rustc_hir as hir;
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty;
 +use rustc_span::{sym, Span};
 +use rustc_typeck::hir_ty_to_ty;
 +
 +use if_chain::if_chain;
 +
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::trait_ref_of_method;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +
 +use super::RESULT_UNIT_ERR;
 +
 +pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
++    if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind {
 +        let is_public = cx.access_levels.is_exported(item.def_id);
 +        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +        if is_public {
 +            check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
 +        }
 +    }
 +}
 +
 +pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &hir::ImplItem<'_>) {
 +    if let hir::ImplItemKind::Fn(ref sig, _) = item.kind {
 +        let is_public = cx.access_levels.is_exported(item.def_id);
 +        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +        if is_public && trait_ref_of_method(cx, item.def_id).is_none() {
 +            check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
 +        }
 +    }
 +}
 +
 +pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>) {
 +    if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
 +        let is_public = cx.access_levels.is_exported(item.def_id);
 +        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +        if is_public {
 +            check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
 +        }
 +    }
 +}
 +
 +fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) {
 +    if_chain! {
 +        if !in_external_macro(cx.sess(), item_span);
 +        if let hir::FnRetTy::Return(ty) = decl.output;
 +        let ty = hir_ty_to_ty(cx.tcx, ty);
 +        if is_type_diagnostic_item(cx, ty, sym::Result);
 +        if let ty::Adt(_, substs) = ty.kind();
 +        let err_ty = substs.type_at(1);
 +        if err_ty.is_unit();
 +        then {
 +            span_lint_and_help(
 +                cx,
 +                RESULT_UNIT_ERR,
 +                fn_header_span,
 +                "this returns a `Result<_, ()>`",
 +                None,
 +                "use a custom `Error` type instead",
 +            );
 +        }
 +    }
 +}
index 4d6bef89bea7f09cbbfa8f618b4d26b871e371b6,0000000000000000000000000000000000000000..40cc5cd4bcf9df4111d202591fde32e8fb4339e6
mode 100644,000000..100644
--- /dev/null
@@@ -1,114 -1,0 +1,150 @@@
-     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-         if e.span.from_expansion() {
++use clippy_utils::get_parent_expr;
 +use clippy_utils::source::snippet;
 +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +
 +use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt};
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::{clip, unsext};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for identity operations, e.g., `x + 0`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This code can be removed without changing the
 +    /// meaning. So it just obscures what's going on. Delete it mercilessly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 1;
 +    /// x / 1 + 0 * 1 - 0 | 0;
 +    /// ```
++    ///
++    /// ### Known problems
++    /// False negatives: `f(0 + if b { 1 } else { 2 } + 3);` is reducible to
++    /// `f(if b { 1 } else { 2 } + 3);`. But the lint doesn't trigger for the code.
++    /// See [#8724](https://github.com/rust-lang/rust-clippy/issues/8724)
 +    #[clippy::version = "pre 1.29.0"]
 +    pub IDENTITY_OP,
 +    complexity,
 +    "using identity operations, e.g., `x + 0` or `y / 1`"
 +}
 +
 +declare_lint_pass!(IdentityOp => [IDENTITY_OP]);
 +
 +impl<'tcx> LateLintPass<'tcx> for IdentityOp {
-         if let ExprKind::Binary(cmp, left, right) = e.kind {
-             if is_allowed(cx, cmp, left, right) {
-                 return;
-             }
-             match cmp.node {
-                 BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
-                     check(cx, left, 0, e.span, right.span);
-                     check(cx, right, 0, e.span, left.span);
-                 },
-                 BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => check(cx, right, 0, e.span, left.span),
-                 BinOpKind::Mul => {
-                     check(cx, left, 1, e.span, right.span);
-                     check(cx, right, 1, e.span, left.span);
-                 },
-                 BinOpKind::Div => check(cx, right, 1, e.span, left.span),
-                 BinOpKind::BitAnd => {
-                     check(cx, left, -1, e.span, right.span);
-                     check(cx, right, -1, e.span, left.span);
-                 },
-                 BinOpKind::Rem => check_remainder(cx, left, right, e.span, left.span),
-                 _ => (),
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
++        if expr.span.from_expansion() {
 +            return;
 +        }
++        if let ExprKind::Binary(cmp, left, right) = &expr.kind {
++            if !is_allowed(cx, *cmp, left, right) {
++                match cmp.node {
++                    BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
++                        if reducible_to_right(cx, expr, right) {
++                            check(cx, left, 0, expr.span, right.span);
++                        }
++                        check(cx, right, 0, expr.span, left.span);
++                    },
++                    BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
++                        check(cx, right, 0, expr.span, left.span);
++                    },
++                    BinOpKind::Mul => {
++                        if reducible_to_right(cx, expr, right) {
++                            check(cx, left, 1, expr.span, right.span);
++                        }
++                        check(cx, right, 1, expr.span, left.span);
++                    },
++                    BinOpKind::Div => check(cx, right, 1, expr.span, left.span),
++                    BinOpKind::BitAnd => {
++                        if reducible_to_right(cx, expr, right) {
++                            check(cx, left, -1, expr.span, right.span);
++                        }
++                        check(cx, right, -1, expr.span, left.span);
++                    },
++                    BinOpKind::Rem => {
++                        // Don't call reducible_to_right because N % N is always reducible to 1
++                        check_remainder(cx, left, right, expr.span, left.span);
++                    },
++                    _ => (),
++                }
 +            }
 +        }
 +    }
 +}
 +
++/// Checks if `left op ..right` can be actually reduced to `right`
++/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }`
++/// cannot be reduced to `if b { 1 } else { 2 } +  if b { 3 } else { 4 }`
++/// See #8724
++fn reducible_to_right(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> bool {
++    if let ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) = right.kind {
++        is_toplevel_binary(cx, binary)
++    } else {
++        true
++    }
++}
++
++fn is_toplevel_binary(cx: &LateContext<'_>, must_be_binary: &Expr<'_>) -> bool {
++    if let Some(parent) = get_parent_expr(cx, must_be_binary) && let ExprKind::Binary(..) = &parent.kind {
++        false
++    } else {
++        true
++    }
++}
++
 +fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +    // This lint applies to integers
 +    !cx.typeck_results().expr_ty(left).peel_refs().is_integral()
 +        || !cx.typeck_results().expr_ty(right).peel_refs().is_integral()
 +        // `1 << 0` is a common pattern in bit manipulation code
 +        || (cmp.node == BinOpKind::Shl
 +            && constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0))
 +            && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)))
 +}
 +
 +fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) {
 +    let lhs_const = constant_full_int(cx, cx.typeck_results(), left);
 +    let rhs_const = constant_full_int(cx, cx.typeck_results(), right);
 +    if match (lhs_const, rhs_const) {
 +        (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(),
 +        (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv,
 +        _ => return,
 +    } {
 +        span_ineffective_operation(cx, span, arg);
 +    }
 +}
 +
 +fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) {
 +    if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) {
 +        let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
 +            ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
 +            ty::Uint(uty) => clip(cx.tcx, !0, uty),
 +            _ => return,
 +        };
 +        if match m {
 +            0 => v == 0,
 +            -1 => v == check,
 +            1 => v == 1,
 +            _ => unreachable!(),
 +        } {
 +            span_ineffective_operation(cx, span, arg);
 +        }
 +    }
 +}
 +
 +fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span) {
 +    span_lint(
 +        cx,
 +        IDENTITY_OP,
 +        span,
 +        &format!(
 +            "the operation is ineffective. Consider reducing it to `{}`",
 +            snippet(cx, arg, "..")
 +        ),
 +    );
 +}
index d5430a8c91750784b0033ccb226c9d352c632b60,0000000000000000000000000000000000000000..feb1b1014b180838b74b771c0a852f6cf671497c
mode 100644,000000..100644
--- /dev/null
@@@ -1,388 -1,0 +1,388 @@@
-             ItemKind::Impl(ref impl_) => {
 +use std::borrow::Cow;
 +use std::collections::BTreeMap;
 +
 +use rustc_errors::Diagnostic;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor};
 +use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{Ty, TypeckResults};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::sym;
 +use rustc_typeck::hir_ty_to_ty;
 +
 +use if_chain::if_chain;
 +
 +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 +use clippy_utils::source::{snippet, snippet_opt};
 +use clippy_utils::ty::is_type_diagnostic_item;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for public `impl` or `fn` missing generalization
 +    /// over different hashers and implicitly defaulting to the default hashing
 +    /// algorithm (`SipHash`).
 +    ///
 +    /// ### Why is this bad?
 +    /// `HashMap` or `HashSet` with custom hashers cannot be
 +    /// used with them.
 +    ///
 +    /// ### Known problems
 +    /// Suggestions for replacing constructors can contain
 +    /// false-positives. Also applying suggestions can require modification of other
 +    /// pieces of code, possibly including external crates.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashMap;
 +    /// # use std::hash::{Hash, BuildHasher};
 +    /// # trait Serialize {};
 +    /// impl<K: Hash + Eq, V> Serialize for HashMap<K, V> { }
 +    ///
 +    /// pub fn foo(map: &mut HashMap<i32, i32>) { }
 +    /// ```
 +    /// could be rewritten as
 +    /// ```rust
 +    /// # use std::collections::HashMap;
 +    /// # use std::hash::{Hash, BuildHasher};
 +    /// # trait Serialize {};
 +    /// impl<K: Hash + Eq, V, S: BuildHasher> Serialize for HashMap<K, V, S> { }
 +    ///
 +    /// pub fn foo<S: BuildHasher>(map: &mut HashMap<i32, i32, S>) { }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub IMPLICIT_HASHER,
 +    pedantic,
 +    "missing generalization over different hashers"
 +}
 +
 +declare_lint_pass!(ImplicitHasher => [IMPLICIT_HASHER]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
 +    #[allow(clippy::cast_possible_truncation, clippy::too_many_lines)]
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        use rustc_span::BytePos;
 +
 +        fn suggestion<'tcx>(
 +            cx: &LateContext<'tcx>,
 +            diag: &mut Diagnostic,
 +            generics_span: Span,
 +            generics_suggestion_span: Span,
 +            target: &ImplicitHasherType<'_>,
 +            vis: ImplicitHasherConstructorVisitor<'_, '_, '_>,
 +        ) {
 +            let generics_snip = snippet(cx, generics_span, "");
 +            // trim `<` `>`
 +            let generics_snip = if generics_snip.is_empty() {
 +                ""
 +            } else {
 +                &generics_snip[1..generics_snip.len() - 1]
 +            };
 +
 +            multispan_sugg(
 +                diag,
 +                "consider adding a type parameter",
 +                vec![
 +                    (
 +                        generics_suggestion_span,
 +                        format!(
 +                            "<{}{}S: ::std::hash::BuildHasher{}>",
 +                            generics_snip,
 +                            if generics_snip.is_empty() { "" } else { ", " },
 +                            if vis.suggestions.is_empty() {
 +                                ""
 +                            } else {
 +                                // request users to add `Default` bound so that generic constructors can be used
 +                                " + Default"
 +                            },
 +                        ),
 +                    ),
 +                    (
 +                        target.span(),
 +                        format!("{}<{}, S>", target.type_name(), target.type_arguments(),),
 +                    ),
 +                ],
 +            );
 +
 +            if !vis.suggestions.is_empty() {
 +                multispan_sugg(diag, "...and use generic constructor", vis.suggestions);
 +            }
 +        }
 +
 +        if !cx.access_levels.is_exported(item.def_id) {
 +            return;
 +        }
 +
 +        match item.kind {
-             ItemKind::Fn(ref sig, ref generics, body_id) => {
++            ItemKind::Impl(impl_) => {
 +                let mut vis = ImplicitHasherTypeVisitor::new(cx);
 +                vis.visit_ty(impl_.self_ty);
 +
 +                for target in &vis.found {
 +                    if item.span.ctxt() != target.span().ctxt() {
 +                        return;
 +                    }
 +
 +                    let generics_suggestion_span = impl_.generics.span.substitute_dummy({
 +                        let pos = snippet_opt(cx, item.span.until(target.span()))
 +                            .and_then(|snip| Some(item.span.lo() + BytePos(snip.find("impl")? as u32 + 4)));
 +                        if let Some(pos) = pos {
 +                            Span::new(pos, pos, item.span.ctxt(), item.span.parent())
 +                        } else {
 +                            return;
 +                        }
 +                    });
 +
 +                    let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
 +                    for item in impl_.items.iter().map(|item| cx.tcx.hir().impl_item(item.id)) {
 +                        ctr_vis.visit_impl_item(item);
 +                    }
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        IMPLICIT_HASHER,
 +                        target.span(),
 +                        &format!(
 +                            "impl for `{}` should be generalized over different hashers",
 +                            target.type_name()
 +                        ),
 +                        move |diag| {
 +                            suggestion(cx, diag, impl_.generics.span, generics_suggestion_span, target, ctr_vis);
 +                        },
 +                    );
 +                }
 +            },
++            ItemKind::Fn(ref sig, generics, body_id) => {
 +                let body = cx.tcx.hir().body(body_id);
 +
 +                for ty in sig.decl.inputs {
 +                    let mut vis = ImplicitHasherTypeVisitor::new(cx);
 +                    vis.visit_ty(ty);
 +
 +                    for target in &vis.found {
 +                        if in_external_macro(cx.sess(), generics.span) {
 +                            continue;
 +                        }
 +                        let generics_suggestion_span = generics.span.substitute_dummy({
 +                            let pos = snippet_opt(
 +                                cx,
 +                                Span::new(
 +                                    item.span.lo(),
 +                                    body.params[0].pat.span.lo(),
 +                                    item.span.ctxt(),
 +                                    item.span.parent(),
 +                                ),
 +                            )
 +                            .and_then(|snip| {
 +                                let i = snip.find("fn")?;
 +                                Some(item.span.lo() + BytePos((i + snip[i..].find('(')?) as u32))
 +                            })
 +                            .expect("failed to create span for type parameters");
 +                            Span::new(pos, pos, item.span.ctxt(), item.span.parent())
 +                        });
 +
 +                        let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
 +                        ctr_vis.visit_body(body);
 +
 +                        span_lint_and_then(
 +                            cx,
 +                            IMPLICIT_HASHER,
 +                            target.span(),
 +                            &format!(
 +                                "parameter of type `{}` should be generalized over different hashers",
 +                                target.type_name()
 +                            ),
 +                            move |diag| {
 +                                suggestion(cx, diag, generics.span, generics_suggestion_span, target, ctr_vis);
 +                            },
 +                        );
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +enum ImplicitHasherType<'tcx> {
 +    HashMap(Span, Ty<'tcx>, Cow<'static, str>, Cow<'static, str>),
 +    HashSet(Span, Ty<'tcx>, Cow<'static, str>),
 +}
 +
 +impl<'tcx> ImplicitHasherType<'tcx> {
 +    /// Checks that `ty` is a target type without a `BuildHasher`.
 +    fn new(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Option<Self> {
 +        if let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind {
 +            let params: Vec<_> = path
 +                .segments
 +                .last()
 +                .as_ref()?
 +                .args
 +                .as_ref()?
 +                .args
 +                .iter()
 +                .filter_map(|arg| match arg {
 +                    GenericArg::Type(ty) => Some(ty),
 +                    _ => None,
 +                })
 +                .collect();
 +            let params_len = params.len();
 +
 +            let ty = hir_ty_to_ty(cx.tcx, hir_ty);
 +
 +            if is_type_diagnostic_item(cx, ty, sym::HashMap) && params_len == 2 {
 +                Some(ImplicitHasherType::HashMap(
 +                    hir_ty.span,
 +                    ty,
 +                    snippet(cx, params[0].span, "K"),
 +                    snippet(cx, params[1].span, "V"),
 +                ))
 +            } else if is_type_diagnostic_item(cx, ty, sym::HashSet) && params_len == 1 {
 +                Some(ImplicitHasherType::HashSet(
 +                    hir_ty.span,
 +                    ty,
 +                    snippet(cx, params[0].span, "T"),
 +                ))
 +            } else {
 +                None
 +            }
 +        } else {
 +            None
 +        }
 +    }
 +
 +    fn type_name(&self) -> &'static str {
 +        match *self {
 +            ImplicitHasherType::HashMap(..) => "HashMap",
 +            ImplicitHasherType::HashSet(..) => "HashSet",
 +        }
 +    }
 +
 +    fn type_arguments(&self) -> String {
 +        match *self {
 +            ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{}, {}", k, v),
 +            ImplicitHasherType::HashSet(.., ref t) => format!("{}", t),
 +        }
 +    }
 +
 +    fn ty(&self) -> Ty<'tcx> {
 +        match *self {
 +            ImplicitHasherType::HashMap(_, ty, ..) | ImplicitHasherType::HashSet(_, ty, ..) => ty,
 +        }
 +    }
 +
 +    fn span(&self) -> Span {
 +        match *self {
 +            ImplicitHasherType::HashMap(span, ..) | ImplicitHasherType::HashSet(span, ..) => span,
 +        }
 +    }
 +}
 +
 +struct ImplicitHasherTypeVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    found: Vec<ImplicitHasherType<'tcx>>,
 +}
 +
 +impl<'a, 'tcx> ImplicitHasherTypeVisitor<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self { cx, found: vec![] }
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'a, 'tcx> {
 +    fn visit_ty(&mut self, t: &'tcx hir::Ty<'_>) {
 +        if let Some(target) = ImplicitHasherType::new(self.cx, t) {
 +            self.found.push(target);
 +        }
 +
 +        walk_ty(self, t);
 +    }
 +
 +    fn visit_infer(&mut self, inf: &'tcx hir::InferArg) {
 +        if let Some(target) = ImplicitHasherType::new(self.cx, &inf.to_ty()) {
 +            self.found.push(target);
 +        }
 +
 +        walk_inf(self, inf);
 +    }
 +}
 +
 +/// Looks for default-hasher-dependent constructors like `HashMap::new`.
 +struct ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
 +    target: &'b ImplicitHasherType<'tcx>,
 +    suggestions: BTreeMap<Span, String>,
 +}
 +
 +impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>, target: &'b ImplicitHasherType<'tcx>) -> Self {
 +        Self {
 +            cx,
 +            maybe_typeck_results: cx.maybe_typeck_results(),
 +            target,
 +            suggestions: BTreeMap::new(),
 +        }
 +    }
 +}
 +
 +impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
 +    type NestedFilter = nested_filter::OnlyBodies;
 +
 +    fn visit_body(&mut self, body: &'tcx Body<'_>) {
 +        let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body.id()));
 +        walk_body(self, body);
 +        self.maybe_typeck_results = old_maybe_typeck_results;
 +    }
 +
 +    fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::Call(fun, args) = e.kind;
 +            if let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind;
 +            if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind;
 +            if let Some(ty_did) = ty_path.res.opt_def_id();
 +            then {
 +                if self.target.ty() != self.maybe_typeck_results.unwrap().expr_ty(e) {
 +                    return;
 +                }
 +
 +                if self.cx.tcx.is_diagnostic_item(sym::HashMap, ty_did) {
 +                    if method.ident.name == sym::new {
 +                        self.suggestions
 +                            .insert(e.span, "HashMap::default()".to_string());
 +                    } else if method.ident.name == sym!(with_capacity) {
 +                        self.suggestions.insert(
 +                            e.span,
 +                            format!(
 +                                "HashMap::with_capacity_and_hasher({}, Default::default())",
 +                                snippet(self.cx, args[0].span, "capacity"),
 +                            ),
 +                        );
 +                    }
 +                } else if self.cx.tcx.is_diagnostic_item(sym::HashSet, ty_did) {
 +                    if method.ident.name == sym::new {
 +                        self.suggestions
 +                            .insert(e.span, "HashSet::default()".to_string());
 +                    } else if method.ident.name == sym!(with_capacity) {
 +                        self.suggestions.insert(
 +                            e.span,
 +                            format!(
 +                                "HashSet::with_capacity_and_hasher({}, Default::default())",
 +                                snippet(self.cx, args[0].span, "capacity"),
 +                            ),
 +                        );
 +                    }
 +                }
 +            }
 +        }
 +
 +        walk_expr(self, e);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
index c8ec2f45137072539afbba571cb87541eb34bff7,0000000000000000000000000000000000000000..14b22d2b50d054fdec8e0061666bf02d381bd518
mode 100644,000000..100644
--- /dev/null
@@@ -1,135 -1,0 +1,136 @@@
-                     fields_snippet.push_str(&format!("{}, ", ident));
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet;
 +use if_chain::if_chain;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_errors::Applicability;
 +use rustc_hir::{self as hir, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::Symbol;
++use std::fmt::Write as _;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for struct constructors where all fields are shorthand and
 +    /// the order of the field init shorthand in the constructor is inconsistent
 +    /// with the order in the struct definition.
 +    ///
 +    /// ### Why is this bad?
 +    /// Since the order of fields in a constructor doesn't affect the
 +    /// resulted instance as the below example indicates,
 +    ///
 +    /// ```rust
 +    /// #[derive(Debug, PartialEq, Eq)]
 +    /// struct Foo {
 +    ///     x: i32,
 +    ///     y: i32,
 +    /// }
 +    /// let x = 1;
 +    /// let y = 2;
 +    ///
 +    /// // This assertion never fails:
 +    /// assert_eq!(Foo { x, y }, Foo { y, x });
 +    /// ```
 +    ///
 +    /// inconsistent order can be confusing and decreases readability and consistency.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Foo {
 +    ///     x: i32,
 +    ///     y: i32,
 +    /// }
 +    /// let x = 1;
 +    /// let y = 2;
 +    ///
 +    /// Foo { y, x };
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # struct Foo {
 +    /// #     x: i32,
 +    /// #     y: i32,
 +    /// # }
 +    /// # let x = 1;
 +    /// # let y = 2;
 +    /// Foo { x, y };
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub INCONSISTENT_STRUCT_CONSTRUCTOR,
 +    pedantic,
 +    "the order of the field init shorthand is inconsistent with the order in the struct definition"
 +}
 +
 +declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]);
 +
 +impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if_chain! {
 +            if !expr.span.from_expansion();
 +            if let ExprKind::Struct(qpath, fields, base) = expr.kind;
 +            let ty = cx.typeck_results().expr_ty(expr);
 +            if let Some(adt_def) = ty.ty_adt_def();
 +            if adt_def.is_struct();
 +            if let Some(variant) = adt_def.variants().iter().next();
 +            if fields.iter().all(|f| f.is_shorthand);
 +            then {
 +                let mut def_order_map = FxHashMap::default();
 +                for (idx, field) in variant.fields.iter().enumerate() {
 +                    def_order_map.insert(field.name, idx);
 +                }
 +
 +                if is_consistent_order(fields, &def_order_map) {
 +                    return;
 +                }
 +
 +                let mut ordered_fields: Vec<_> = fields.iter().map(|f| f.ident.name).collect();
 +                ordered_fields.sort_unstable_by_key(|id| def_order_map[id]);
 +
 +                let mut fields_snippet = String::new();
 +                let (last_ident, idents) = ordered_fields.split_last().unwrap();
 +                for ident in idents {
++                    let _ = write!(fields_snippet, "{}, ", ident);
 +                }
 +                fields_snippet.push_str(&last_ident.to_string());
 +
 +                let base_snippet = if let Some(base) = base {
 +                        format!(", ..{}", snippet(cx, base.span, ".."))
 +                    } else {
 +                        String::new()
 +                    };
 +
 +                let sugg = format!("{} {{ {}{} }}",
 +                    snippet(cx, qpath.span(), ".."),
 +                    fields_snippet,
 +                    base_snippet,
 +                    );
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    INCONSISTENT_STRUCT_CONSTRUCTOR,
 +                    expr.span,
 +                    "struct constructor field order is inconsistent with struct definition field order",
 +                    "try",
 +                    sugg,
 +                    Applicability::MachineApplicable,
 +                )
 +            }
 +        }
 +    }
 +}
 +
 +// Check whether the order of the fields in the constructor is consistent with the order in the
 +// definition.
 +fn is_consistent_order<'tcx>(fields: &'tcx [hir::ExprField<'tcx>], def_order_map: &FxHashMap<Symbol, usize>) -> bool {
 +    let mut cur_idx = usize::MIN;
 +    for f in fields {
 +        let next_idx = def_order_map[&f.ident.name];
 +        if cur_idx > next_idx {
 +            return false;
 +        }
 +        cur_idx = next_idx;
 +    }
 +
 +    true
 +}
index 6b62748ffef2e2812195961768e5d416197d6a26,0000000000000000000000000000000000000000..8a84513b7792fdbf0f97db326feac7cc8f2c274b
mode 100644,000000..100644
--- /dev/null
@@@ -1,276 -1,0 +1,276 @@@
-                 // This will need to be adjusted if the lint want to support multable access in the future
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::higher::IfLet;
 +use clippy_utils::ty::is_copy;
 +use clippy_utils::{is_expn_of, is_lint_allowed, meets_msrv, msrvs, path_to_local};
 +use if_chain::if_chain;
 +use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{self, Visitor};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::ty;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{symbol::Ident, Span};
 +use std::convert::TryInto;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// The lint checks for slice bindings in patterns that are only used to
 +    /// access individual slice values.
 +    ///
 +    /// ### Why is this bad?
 +    /// Accessing slice values using indices can lead to panics. Using refutable
 +    /// patterns can avoid these. Binding to individual values also improves the
 +    /// readability as they can be named.
 +    ///
 +    /// ### Limitations
 +    /// This lint currently only checks for immutable access inside `if let`
 +    /// patterns.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let slice: Option<&[u32]> = Some(&[1, 2, 3]);
 +    ///
 +    /// if let Some(slice) = slice {
 +    ///     println!("{}", slice[0]);
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let slice: Option<&[u32]> = Some(&[1, 2, 3]);
 +    ///
 +    /// if let Some(&[first, ..]) = slice {
 +    ///     println!("{}", first);
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub INDEX_REFUTABLE_SLICE,
 +    nursery,
 +    "avoid indexing on slices which could be destructed"
 +}
 +
 +#[derive(Copy, Clone)]
 +pub struct IndexRefutableSlice {
 +    max_suggested_slice: u64,
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl IndexRefutableSlice {
 +    pub fn new(max_suggested_slice_pattern_length: u64, msrv: Option<RustcVersion>) -> Self {
 +        Self {
 +            max_suggested_slice: max_suggested_slice_pattern_length,
 +            msrv,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if_chain! {
 +            if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some();
 +            if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr);
 +            if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id);
 +            if meets_msrv(self.msrv.as_ref(), &msrvs::SLICE_PATTERNS);
 +
 +            let found_slices = find_slice_values(cx, let_pat);
 +            if !found_slices.is_empty();
 +            let filtered_slices = filter_lintable_slices(cx, found_slices, self.max_suggested_slice, if_then);
 +            if !filtered_slices.is_empty();
 +            then {
 +                for slice in filtered_slices.values() {
 +                    lint_slice(cx, slice);
 +                }
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap<hir::HirId, SliceLintInformation> {
 +    let mut removed_pat: FxHashSet<hir::HirId> = FxHashSet::default();
 +    let mut slices: FxIndexMap<hir::HirId, SliceLintInformation> = FxIndexMap::default();
 +    pat.walk_always(|pat| {
 +        if let hir::PatKind::Binding(binding, value_hir_id, ident, sub_pat) = pat.kind {
 +            // We'll just ignore mut and ref mut for simplicity sake right now
 +            if let hir::BindingAnnotation::Mutable | hir::BindingAnnotation::RefMut = binding {
 +                return;
 +            }
 +
 +            // This block catches bindings with sub patterns. It would be hard to build a correct suggestion
 +            // for them and it's likely that the user knows what they are doing in such a case.
 +            if removed_pat.contains(&value_hir_id) {
 +                return;
 +            }
 +            if sub_pat.is_some() {
 +                removed_pat.insert(value_hir_id);
 +                slices.remove(&value_hir_id);
 +                return;
 +            }
 +
 +            let bound_ty = cx.typeck_results().node_type(pat.hir_id);
 +            if let ty::Slice(inner_ty) | ty::Array(inner_ty, _) = bound_ty.peel_refs().kind() {
 +                // The values need to use the `ref` keyword if they can't be copied.
++                // This will need to be adjusted if the lint want to support mutable access in the future
 +                let src_is_ref = bound_ty.is_ref() && binding != hir::BindingAnnotation::Ref;
 +                let needs_ref = !(src_is_ref || is_copy(cx, *inner_ty));
 +
 +                let slice_info = slices
 +                    .entry(value_hir_id)
 +                    .or_insert_with(|| SliceLintInformation::new(ident, needs_ref));
 +                slice_info.pattern_spans.push(pat.span);
 +            }
 +        }
 +    });
 +
 +    slices
 +}
 +
 +fn lint_slice(cx: &LateContext<'_>, slice: &SliceLintInformation) {
 +    let used_indices = slice
 +        .index_use
 +        .iter()
 +        .map(|(index, _)| *index)
 +        .collect::<FxHashSet<_>>();
 +
 +    let value_name = |index| format!("{}_{}", slice.ident.name, index);
 +
 +    if let Some(max_index) = used_indices.iter().max() {
 +        let opt_ref = if slice.needs_ref { "ref " } else { "" };
 +        let pat_sugg_idents = (0..=*max_index)
 +            .map(|index| {
 +                if used_indices.contains(&index) {
 +                    format!("{}{}", opt_ref, value_name(index))
 +                } else {
 +                    "_".to_string()
 +                }
 +            })
 +            .collect::<Vec<_>>();
 +        let pat_sugg = format!("[{}, ..]", pat_sugg_idents.join(", "));
 +
 +        span_lint_and_then(
 +            cx,
 +            INDEX_REFUTABLE_SLICE,
 +            slice.ident.span,
 +            "this binding can be a slice pattern to avoid indexing",
 +            |diag| {
 +                diag.multipart_suggestion(
 +                    "try using a slice pattern here",
 +                    slice
 +                        .pattern_spans
 +                        .iter()
 +                        .map(|span| (*span, pat_sugg.clone()))
 +                        .collect(),
 +                    Applicability::MaybeIncorrect,
 +                );
 +
 +                diag.multipart_suggestion(
 +                    "and replace the index expressions here",
 +                    slice
 +                        .index_use
 +                        .iter()
 +                        .map(|(index, span)| (*span, value_name(*index)))
 +                        .collect(),
 +                    Applicability::MaybeIncorrect,
 +                );
 +
 +                // The lint message doesn't contain a warning about the removed index expression,
 +                // since `filter_lintable_slices` will only return slices where all access indices
 +                // are known at compile time. Therefore, they can be removed without side effects.
 +            },
 +        );
 +    }
 +}
 +
 +#[derive(Debug)]
 +struct SliceLintInformation {
 +    ident: Ident,
 +    needs_ref: bool,
 +    pattern_spans: Vec<Span>,
 +    index_use: Vec<(u64, Span)>,
 +}
 +
 +impl SliceLintInformation {
 +    fn new(ident: Ident, needs_ref: bool) -> Self {
 +        Self {
 +            ident,
 +            needs_ref,
 +            pattern_spans: Vec::new(),
 +            index_use: Vec::new(),
 +        }
 +    }
 +}
 +
 +fn filter_lintable_slices<'a, 'tcx>(
 +    cx: &'a LateContext<'tcx>,
 +    slice_lint_info: FxIndexMap<hir::HirId, SliceLintInformation>,
 +    max_suggested_slice: u64,
 +    scope: &'tcx hir::Expr<'tcx>,
 +) -> FxIndexMap<hir::HirId, SliceLintInformation> {
 +    let mut visitor = SliceIndexLintingVisitor {
 +        cx,
 +        slice_lint_info,
 +        max_suggested_slice,
 +    };
 +
 +    intravisit::walk_expr(&mut visitor, scope);
 +
 +    visitor.slice_lint_info
 +}
 +
 +struct SliceIndexLintingVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    slice_lint_info: FxIndexMap<hir::HirId, SliceLintInformation>,
 +    max_suggested_slice: u64,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> {
 +    type NestedFilter = nested_filter::OnlyBodies;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
 +        if let Some(local_id) = path_to_local(expr) {
 +            let Self {
 +                cx,
 +                ref mut slice_lint_info,
 +                max_suggested_slice,
 +            } = *self;
 +
 +            if_chain! {
 +                // Check if this is even a local we're interested in
 +                if let Some(use_info) = slice_lint_info.get_mut(&local_id);
 +
 +                let map = cx.tcx.hir();
 +
 +                // Checking for slice indexing
 +                let parent_id = map.get_parent_node(expr.hir_id);
 +                if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id);
 +                if let hir::ExprKind::Index(_, index_expr) = parent_expr.kind;
 +                if let Some((Constant::Int(index_value), _)) = constant(cx, cx.typeck_results(), index_expr);
 +                if let Ok(index_value) = index_value.try_into();
 +                if index_value < max_suggested_slice;
 +
 +                // Make sure that this slice index is read only
 +                let maybe_addrof_id = map.get_parent_node(parent_id);
 +                if let Some(hir::Node::Expr(maybe_addrof_expr)) = map.find(maybe_addrof_id);
 +                if let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind;
 +                then {
 +                    use_info.index_use.push((index_value, map.span(parent_expr.hir_id)));
 +                    return;
 +                }
 +            }
 +
 +            // The slice was used for something other than indexing
 +            self.slice_lint_info.remove(&local_id);
 +        }
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
index 254d3f8a4d0f901a672585236c66e19819d963a5,0000000000000000000000000000000000000000..6a031a627df946669e206ae77fb2255d21bf71e4
mode 100644,000000..100644
--- /dev/null
@@@ -1,139 -1,0 +1,139 @@@
-         kind: ItemKind::Impl(ref impl_item),
 +//! lint on inherent implementations
 +
 +use clippy_utils::diagnostics::span_lint_and_note;
 +use clippy_utils::is_lint_allowed;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::{def_id::LocalDefId, Item, ItemKind, Node};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::Span;
 +use std::collections::hash_map::Entry;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for multiple inherent implementations of a struct
 +    ///
 +    /// ### Why is this bad?
 +    /// Splitting the implementation of a type makes the code harder to navigate.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct X;
 +    /// impl X {
 +    ///     fn one() {}
 +    /// }
 +    /// impl X {
 +    ///     fn other() {}
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```rust
 +    /// struct X;
 +    /// impl X {
 +    ///     fn one() {}
 +    ///     fn other() {}
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MULTIPLE_INHERENT_IMPL,
 +    restriction,
 +    "Multiple inherent impl that could be grouped"
 +}
 +
 +declare_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl {
 +    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
 +        // Map from a type to it's first impl block. Needed to distinguish generic arguments.
 +        // e.g. `Foo<Bar>` and `Foo<Baz>`
 +        let mut type_map = FxHashMap::default();
 +        // List of spans to lint. (lint_span, first_span)
 +        let mut lint_spans = Vec::new();
 +
 +        for (_, impl_ids) in cx
 +            .tcx
 +            .crate_inherent_impls(())
 +            .inherent_impls
 +            .iter()
 +            .filter(|(&id, impls)| {
 +                impls.len() > 1
 +                    // Check for `#[allow]` on the type definition
 +                    && !is_lint_allowed(
 +                        cx,
 +                        MULTIPLE_INHERENT_IMPL,
 +                        cx.tcx.hir().local_def_id_to_hir_id(id),
 +                    )
 +            })
 +        {
 +            for impl_id in impl_ids.iter().map(|id| id.expect_local()) {
 +                match type_map.entry(cx.tcx.type_of(impl_id)) {
 +                    Entry::Vacant(e) => {
 +                        // Store the id for the first impl block of this type. The span is retrieved lazily.
 +                        e.insert(IdOrSpan::Id(impl_id));
 +                    },
 +                    Entry::Occupied(mut e) => {
 +                        if let Some(span) = get_impl_span(cx, impl_id) {
 +                            let first_span = match *e.get() {
 +                                IdOrSpan::Span(s) => s,
 +                                IdOrSpan::Id(id) => {
 +                                    if let Some(s) = get_impl_span(cx, id) {
 +                                        // Remember the span of the first block.
 +                                        *e.get_mut() = IdOrSpan::Span(s);
 +                                        s
 +                                    } else {
 +                                        // The first impl block isn't considered by the lint. Replace it with the
 +                                        // current one.
 +                                        *e.get_mut() = IdOrSpan::Span(span);
 +                                        continue;
 +                                    }
 +                                },
 +                            };
 +                            lint_spans.push((span, first_span));
 +                        }
 +                    },
 +                }
 +            }
 +
 +            // Switching to the next type definition, no need to keep the current entries around.
 +            type_map.clear();
 +        }
 +
 +        // `TyCtxt::crate_inherent_impls` doesn't have a defined order. Sort the lint output first.
 +        lint_spans.sort_by_key(|x| x.0.lo());
 +        for (span, first_span) in lint_spans {
 +            span_lint_and_note(
 +                cx,
 +                MULTIPLE_INHERENT_IMPL,
 +                span,
 +                "multiple implementations of this structure",
 +                Some(first_span),
 +                "first implementation here",
 +            );
 +        }
 +    }
 +}
 +
 +/// Gets the span for the given impl block unless it's not being considered by the lint.
 +fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option<Span> {
 +    let id = cx.tcx.hir().local_def_id_to_hir_id(id);
 +    if let Node::Item(&Item {
++        kind: ItemKind::Impl(impl_item),
 +        span,
 +        ..
 +    }) = cx.tcx.hir().get(id)
 +    {
 +        (!span.from_expansion()
 +            && impl_item.generics.params.is_empty()
 +            && !is_lint_allowed(cx, MULTIPLE_INHERENT_IMPL, id))
 +        .then(|| span)
 +    } else {
 +        None
 +    }
 +}
 +
 +enum IdOrSpan {
 +    Id(LocalDefId),
 +    Span(Span),
 +}
index 9284e002409920673763967bb375b5bbf1b1b90a,0000000000000000000000000000000000000000..7e1548531f10cc9301c558d013dcd133c30dc330
mode 100644,000000..100644
--- /dev/null
@@@ -1,79 -1,0 +1,81 @@@
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use rustc_errors::Applicability;
++use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use std::borrow::Cow;
 +use std::cmp::Reverse;
 +use std::collections::BinaryHeap;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for tuple structs initialized with field syntax.
 +    /// It will however not lint if a base initializer is present.
 +    /// The lint will also ignore code in macros.
 +    ///
 +    /// ### Why is this bad?
 +    /// This may be confusing to the uninitiated and adds no
 +    /// benefit as opposed to tuple initializers
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct TupleStruct(u8, u16);
 +    ///
 +    /// let _ = TupleStruct {
 +    ///     0: 1,
 +    ///     1: 23,
 +    /// };
 +    ///
 +    /// // should be written as
 +    /// let base = TupleStruct(1, 23);
 +    ///
 +    /// // This is OK however
 +    /// let _ = TupleStruct { 0: 42, ..base };
 +    /// ```
 +    #[clippy::version = "1.59.0"]
 +    pub INIT_NUMBERED_FIELDS,
 +    style,
 +    "numbered fields in tuple struct initializer"
 +}
 +
 +declare_lint_pass!(NumberedFields => [INIT_NUMBERED_FIELDS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for NumberedFields {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if let ExprKind::Struct(path, fields, None) = e.kind {
 +            if !fields.is_empty()
 +                && !e.span.from_expansion()
 +                && fields
 +                    .iter()
 +                    .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit))
++                && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..))
 +            {
 +                let expr_spans = fields
 +                    .iter()
 +                    .map(|f| (Reverse(f.ident.as_str().parse::<usize>().unwrap()), f.expr.span))
 +                    .collect::<BinaryHeap<_>>();
 +                let mut appl = Applicability::MachineApplicable;
 +                let snippet = format!(
 +                    "{}({})",
 +                    snippet_with_applicability(cx, path.span(), "..", &mut appl),
 +                    expr_spans
 +                        .into_iter_sorted()
 +                        .map(|(_, span)| snippet_with_applicability(cx, span, "..", &mut appl))
 +                        .intersperse(Cow::Borrowed(", "))
 +                        .collect::<String>()
 +                );
 +                span_lint_and_sugg(
 +                    cx,
 +                    INIT_NUMBERED_FIELDS,
 +                    e.span,
 +                    "used a field initializer for a tuple struct",
 +                    "try this instead",
 +                    snippet,
 +                    appl,
 +                );
 +            }
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8bef13c682dbfe86c92da5ba743fcc1af6efa975
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,86 @@@
++use clippy_utils::diagnostics::span_lint_and_note;
++use clippy_utils::is_lint_allowed;
++use clippy_utils::macros::root_macro_call_first_node;
++use rustc_ast::LitKind;
++use rustc_hir::Expr;
++use rustc_hir::ExprKind;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::sym;
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for the inclusion of large files via `include_bytes!()`
++    /// and `include_str!()`
++    ///
++    /// ### Why is this bad?
++    /// Including large files can increase the size of the binary
++    ///
++    /// ### Example
++    /// ```rust,ignore
++    /// let included_str = include_str!("very_large_file.txt");
++    /// let included_bytes = include_bytes!("very_large_file.txt");
++    /// ```
++    ///
++    /// Instead, you can load the file at runtime:
++    /// ```rust,ignore
++    /// use std::fs;
++    ///
++    /// let string = fs::read_to_string("very_large_file.txt")?;
++    /// let bytes = fs::read("very_large_file.txt")?;
++    /// ```
++    #[clippy::version = "1.62.0"]
++    pub LARGE_INCLUDE_FILE,
++    restriction,
++    "including a large file"
++}
++
++pub struct LargeIncludeFile {
++    max_file_size: u64,
++}
++
++impl LargeIncludeFile {
++    #[must_use]
++    pub fn new(max_file_size: u64) -> Self {
++        Self { max_file_size }
++    }
++}
++
++impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]);
++
++impl LateLintPass<'_> for LargeIncludeFile {
++    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
++        if_chain! {
++            if let Some(macro_call) = root_macro_call_first_node(cx, expr);
++            if !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id);
++            if cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
++            || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id);
++            if let ExprKind::Lit(lit) = &expr.kind;
++            then {
++                let len = match &lit.node {
++                    // include_bytes
++                    LitKind::ByteStr(bstr) => bstr.len(),
++                    // include_str
++                    LitKind::Str(sym, _) => sym.as_str().len(),
++                    _ => return,
++                };
++
++                if len as u64 <= self.max_file_size {
++                    return;
++                }
++
++                span_lint_and_note(
++                    cx,
++                    LARGE_INCLUDE_FILE,
++                    expr.span,
++                    "attempted to include a large file",
++                    None,
++                    &format!(
++                        "the configuration allows a maximum size of {} bytes",
++                        self.max_file_size
++                    ),
++                );
++            }
++        }
++    }
++}
index 14ca93b5f3c14b3629a0168b421a3527797ffc8d,0000000000000000000000000000000000000000..e68718f9fdf99165efdb6682b8e0ea4d5a2efe18
mode 100644,000000..100644
--- /dev/null
@@@ -1,331 -1,0 +1,337 @@@
-     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::all", Some("clippy_all"), vec![
 +    LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
 +    LintId::of(approx_const::APPROX_CONSTANT),
 +    LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
 +    LintId::of(assign_ops::ASSIGN_OP_PATTERN),
 +    LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
 +    LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
 +    LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
 +    LintId::of(attrs::DEPRECATED_CFG_ATTR),
 +    LintId::of(attrs::DEPRECATED_SEMVER),
 +    LintId::of(attrs::MISMATCHED_TARGET_OS),
 +    LintId::of(attrs::USELESS_ATTRIBUTE),
++    LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
 +    LintId::of(bit_mask::BAD_BIT_MASK),
 +    LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
 +    LintId::of(blacklisted_name::BLACKLISTED_NAME),
 +    LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
 +    LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
 +    LintId::of(booleans::LOGIC_BUG),
 +    LintId::of(booleans::NONMINIMAL_BOOL),
++    LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
 +    LintId::of(casts::CAST_ABS_TO_UNSIGNED),
 +    LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
 +    LintId::of(casts::CAST_ENUM_TRUNCATION),
 +    LintId::of(casts::CAST_REF_TO_MUT),
 +    LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
 +    LintId::of(casts::CHAR_LIT_AS_U8),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
 +    LintId::of(casts::UNNECESSARY_CAST),
 +    LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
 +    LintId::of(collapsible_if::COLLAPSIBLE_IF),
 +    LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
 +    LintId::of(comparison_chain::COMPARISON_CHAIN),
 +    LintId::of(copies::IFS_SAME_COND),
 +    LintId::of(copies::IF_SAME_THEN_ELSE),
 +    LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
 +    LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
 +    LintId::of(dereference::NEEDLESS_BORROW),
 +    LintId::of(derivable_impls::DERIVABLE_IMPLS),
 +    LintId::of(derive::DERIVE_HASH_XOR_EQ),
 +    LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
 +    LintId::of(disallowed_methods::DISALLOWED_METHODS),
 +    LintId::of(disallowed_types::DISALLOWED_TYPES),
 +    LintId::of(doc::MISSING_SAFETY_DOC),
 +    LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
 +    LintId::of(double_comparison::DOUBLE_COMPARISONS),
 +    LintId::of(double_parens::DOUBLE_PARENS),
 +    LintId::of(drop_forget_ref::DROP_COPY),
 +    LintId::of(drop_forget_ref::DROP_NON_DROP),
 +    LintId::of(drop_forget_ref::DROP_REF),
 +    LintId::of(drop_forget_ref::FORGET_COPY),
 +    LintId::of(drop_forget_ref::FORGET_NON_DROP),
 +    LintId::of(drop_forget_ref::FORGET_REF),
 +    LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
 +    LintId::of(duration_subsec::DURATION_SUBSEC),
 +    LintId::of(entry::MAP_ENTRY),
 +    LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
 +    LintId::of(enum_variants::ENUM_VARIANT_NAMES),
 +    LintId::of(enum_variants::MODULE_INCEPTION),
 +    LintId::of(eq_op::EQ_OP),
 +    LintId::of(eq_op::OP_REF),
 +    LintId::of(erasing_op::ERASING_OP),
 +    LintId::of(escape::BOXED_LOCAL),
 +    LintId::of(eta_reduction::REDUNDANT_CLOSURE),
 +    LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION),
 +    LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
 +    LintId::of(explicit_write::EXPLICIT_WRITE),
 +    LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
 +    LintId::of(float_literal::EXCESSIVE_PRECISION),
 +    LintId::of(format::USELESS_FORMAT),
 +    LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
 +    LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
 +    LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
 +    LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
++    LintId::of(format_push_string::FORMAT_PUSH_STRING),
 +    LintId::of(formatting::POSSIBLE_MISSING_COMMA),
 +    LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
 +    LintId::of(from_over_into::FROM_OVER_INTO),
 +    LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
 +    LintId::of(functions::DOUBLE_MUST_USE),
 +    LintId::of(functions::MUST_USE_UNIT),
 +    LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
 +    LintId::of(functions::RESULT_UNIT_ERR),
 +    LintId::of(functions::TOO_MANY_ARGUMENTS),
 +    LintId::of(get_last_with_len::GET_LAST_WITH_LEN),
 +    LintId::of(identity_op::IDENTITY_OP),
 +    LintId::of(if_let_mutex::IF_LET_MUTEX),
 +    LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
 +    LintId::of(infinite_iter::INFINITE_ITER),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
 +    LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
 +    LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
 +    LintId::of(int_plus_one::INT_PLUS_ONE),
 +    LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
 +    LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
 +    LintId::of(len_zero::COMPARISON_TO_EMPTY),
 +    LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
 +    LintId::of(len_zero::LEN_ZERO),
 +    LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
 +    LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
 +    LintId::of(lifetimes::NEEDLESS_LIFETIMES),
 +    LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
 +    LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
 +    LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
 +    LintId::of(loops::EMPTY_LOOP),
 +    LintId::of(loops::EXPLICIT_COUNTER_LOOP),
 +    LintId::of(loops::FOR_KV_MAP),
 +    LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
 +    LintId::of(loops::ITER_NEXT_LOOP),
 +    LintId::of(loops::MANUAL_FLATTEN),
 +    LintId::of(loops::MANUAL_MEMCPY),
 +    LintId::of(loops::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_map::MANUAL_MAP),
 +    LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
 +    LintId::of(manual_strip::MANUAL_STRIP),
 +    LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
 +    LintId::of(map_clone::MAP_CLONE),
 +    LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
 +    LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
 +    LintId::of(match_result_ok::MATCH_RESULT_OK),
 +    LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH),
 +    LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
 +    LintId::of(matches::MATCH_AS_REF),
 +    LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
 +    LintId::of(matches::MATCH_OVERLAPPING_ARM),
 +    LintId::of(matches::MATCH_REF_PATS),
 +    LintId::of(matches::MATCH_SINGLE_BINDING),
 +    LintId::of(matches::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_NTH),
 +    LintId::of(methods::CHARS_LAST_CMP),
 +    LintId::of(methods::CHARS_NEXT_CMP),
 +    LintId::of(methods::CLONE_DOUBLE_REF),
 +    LintId::of(methods::CLONE_ON_COPY),
 +    LintId::of(methods::ERR_EXPECT),
 +    LintId::of(methods::EXPECT_FUN_CALL),
 +    LintId::of(methods::EXTEND_WITH_DRAIN),
 +    LintId::of(methods::FILTER_MAP_IDENTITY),
 +    LintId::of(methods::FILTER_NEXT),
 +    LintId::of(methods::FLAT_MAP_IDENTITY),
 +    LintId::of(methods::INSPECT_FOR_EACH),
 +    LintId::of(methods::INTO_ITER_ON_REF),
++    LintId::of(methods::IS_DIGIT_ASCII_RADIX),
 +    LintId::of(methods::ITERATOR_STEP_BY_ZERO),
 +    LintId::of(methods::ITER_CLONED_COLLECT),
 +    LintId::of(methods::ITER_COUNT),
 +    LintId::of(methods::ITER_NEXT_SLICE),
 +    LintId::of(methods::ITER_NTH),
 +    LintId::of(methods::ITER_NTH_ZERO),
 +    LintId::of(methods::ITER_OVEREAGER_CLONED),
 +    LintId::of(methods::ITER_SKIP_NEXT),
 +    LintId::of(methods::MANUAL_FILTER_MAP),
 +    LintId::of(methods::MANUAL_FIND_MAP),
 +    LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
 +    LintId::of(methods::MANUAL_SPLIT_ONCE),
 +    LintId::of(methods::MANUAL_STR_REPEAT),
 +    LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
 +    LintId::of(methods::MAP_FLATTEN),
 +    LintId::of(methods::MAP_IDENTITY),
 +    LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
++    LintId::of(methods::NEEDLESS_OPTION_TAKE),
 +    LintId::of(methods::NEEDLESS_SPLITN),
 +    LintId::of(methods::NEW_RET_NO_SELF),
 +    LintId::of(methods::OK_EXPECT),
 +    LintId::of(methods::OPTION_AS_REF_DEREF),
 +    LintId::of(methods::OPTION_FILTER_MAP),
 +    LintId::of(methods::OPTION_MAP_OR_NONE),
 +    LintId::of(methods::OR_FUN_CALL),
 +    LintId::of(methods::OR_THEN_UNWRAP),
 +    LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
 +    LintId::of(methods::SEARCH_IS_SOME),
 +    LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
 +    LintId::of(methods::SINGLE_CHAR_ADD_STR),
 +    LintId::of(methods::SINGLE_CHAR_PATTERN),
 +    LintId::of(methods::SKIP_WHILE_NEXT),
 +    LintId::of(methods::STRING_EXTEND_CHARS),
 +    LintId::of(methods::SUSPICIOUS_MAP),
 +    LintId::of(methods::SUSPICIOUS_SPLITN),
 +    LintId::of(methods::UNINIT_ASSUMED_INIT),
 +    LintId::of(methods::UNNECESSARY_FILTER_MAP),
 +    LintId::of(methods::UNNECESSARY_FIND_MAP),
 +    LintId::of(methods::UNNECESSARY_FOLD),
 +    LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
 +    LintId::of(methods::UNNECESSARY_TO_OWNED),
 +    LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
 +    LintId::of(methods::USELESS_ASREF),
 +    LintId::of(methods::WRONG_SELF_CONVENTION),
 +    LintId::of(methods::ZST_OFFSET),
 +    LintId::of(minmax::MIN_MAX),
 +    LintId::of(misc::CMP_NAN),
 +    LintId::of(misc::CMP_OWNED),
 +    LintId::of(misc::MODULO_ONE),
 +    LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
 +    LintId::of(misc::TOPLEVEL_REF_ARG),
 +    LintId::of(misc::ZERO_PTR),
 +    LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
 +    LintId::of(misc_early::DOUBLE_NEG),
 +    LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
 +    LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
 +    LintId::of(misc_early::REDUNDANT_PATTERN),
 +    LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
 +    LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
 +    LintId::of(mut_key::MUTABLE_KEY_TYPE),
 +    LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
 +    LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
 +    LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
 +    LintId::of(needless_bool::BOOL_COMPARISON),
 +    LintId::of(needless_bool::NEEDLESS_BOOL),
 +    LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
 +    LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
 +    LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
 +    LintId::of(needless_update::NEEDLESS_UPDATE),
 +    LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
 +    LintId::of(neg_multiply::NEG_MULTIPLY),
 +    LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
 +    LintId::of(no_effect::NO_EFFECT),
 +    LintId::of(no_effect::UNNECESSARY_OPERATION),
 +    LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
 +    LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
 +    LintId::of(octal_escapes::OCTAL_ESCAPES),
-     LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
 +    LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
 +    LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
 +    LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
 +    LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
 +    LintId::of(precedence::PRECEDENCE),
 +    LintId::of(ptr::CMP_NULL),
 +    LintId::of(ptr::INVALID_NULL_PTR_USAGE),
 +    LintId::of(ptr::MUT_FROM_REF),
 +    LintId::of(ptr::PTR_ARG),
 +    LintId::of(ptr_eq::PTR_EQ),
 +    LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
 +    LintId::of(question_mark::QUESTION_MARK),
 +    LintId::of(ranges::MANUAL_RANGE_CONTAINS),
 +    LintId::of(ranges::RANGE_ZIP_WITH_LEN),
 +    LintId::of(ranges::REVERSED_EMPTY_RANGES),
 +    LintId::of(redundant_clone::REDUNDANT_CLONE),
 +    LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
 +    LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
 +    LintId::of(redundant_slicing::REDUNDANT_SLICING),
 +    LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
 +    LintId::of(reference::DEREF_ADDROF),
 +    LintId::of(regex::INVALID_REGEX),
 +    LintId::of(repeat_once::REPEAT_ONCE),
 +    LintId::of(returns::LET_AND_RETURN),
 +    LintId::of(returns::NEEDLESS_RETURN),
 +    LintId::of(self_assignment::SELF_ASSIGNMENT),
 +    LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
 +    LintId::of(serde_api::SERDE_API_MISUSE),
 +    LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
 +    LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
 +    LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
 +    LintId::of(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(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
 +    LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
 +    LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
 +    LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
 +    LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
 +    LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
 +    LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
 +    LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
 +    LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
 +    LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
 +    LintId::of(transmute::WRONG_TRANSMUTE),
 +    LintId::of(transmuting_null::TRANSMUTING_NULL),
 +    LintId::of(types::BORROWED_BOX),
 +    LintId::of(types::BOX_COLLECTION),
 +    LintId::of(types::REDUNDANT_ALLOCATION),
 +    LintId::of(types::TYPE_COMPLEXITY),
 +    LintId::of(types::VEC_BOX),
 +    LintId::of(unicode::INVISIBLE_CHARACTERS),
 +    LintId::of(uninit_vec::UNINIT_VEC),
 +    LintId::of(unit_hash::UNIT_HASH),
 +    LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
++    LintId::of(unit_types::LET_UNIT_VALUE),
 +    LintId::of(unit_types::UNIT_ARG),
 +    LintId::of(unit_types::UNIT_CMP),
 +    LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
 +    LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
++    LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
 +    LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
 +    LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
 +    LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
 +    LintId::of(unused_unit::UNUSED_UNIT),
 +    LintId::of(unwrap::PANICKING_UNWRAP),
 +    LintId::of(unwrap::UNNECESSARY_UNWRAP),
 +    LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
 +    LintId::of(useless_conversion::USELESS_CONVERSION),
 +    LintId::of(vec::USELESS_VEC),
 +    LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
 +    LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
 +    LintId::of(write::PRINTLN_EMPTY_STRING),
 +    LintId::of(write::PRINT_LITERAL),
 +    LintId::of(write::PRINT_WITH_NEWLINE),
 +    LintId::of(write::WRITELN_EMPTY_STRING),
 +    LintId::of(write::WRITE_LITERAL),
 +    LintId::of(write::WRITE_WITH_NEWLINE),
 +    LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
 +])
index 10369a855ae6e007f43469e7c58e4f38e4368376,0000000000000000000000000000000000000000..6f3c433af31a6287b9fce5dee51ad0daaf5f8fcf
mode 100644,000000..100644
--- /dev/null
@@@ -1,100 -1,0 +1,101 @@@
-     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::complexity", Some("clippy_complexity"), vec![
 +    LintId::of(attrs::DEPRECATED_CFG_ATTR),
 +    LintId::of(booleans::NONMINIMAL_BOOL),
++    LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
 +    LintId::of(casts::CHAR_LIT_AS_U8),
 +    LintId::of(casts::UNNECESSARY_CAST),
 +    LintId::of(derivable_impls::DERIVABLE_IMPLS),
 +    LintId::of(double_comparison::DOUBLE_COMPARISONS),
 +    LintId::of(double_parens::DOUBLE_PARENS),
 +    LintId::of(duration_subsec::DURATION_SUBSEC),
 +    LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION),
 +    LintId::of(explicit_write::EXPLICIT_WRITE),
 +    LintId::of(format::USELESS_FORMAT),
 +    LintId::of(functions::TOO_MANY_ARGUMENTS),
 +    LintId::of(get_last_with_len::GET_LAST_WITH_LEN),
 +    LintId::of(identity_op::IDENTITY_OP),
 +    LintId::of(int_plus_one::INT_PLUS_ONE),
 +    LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
 +    LintId::of(lifetimes::NEEDLESS_LIFETIMES),
 +    LintId::of(loops::EXPLICIT_COUNTER_LOOP),
 +    LintId::of(loops::MANUAL_FLATTEN),
 +    LintId::of(loops::SINGLE_ELEMENT_LOOP),
 +    LintId::of(loops::WHILE_LET_LOOP),
 +    LintId::of(manual_strip::MANUAL_STRIP),
 +    LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
 +    LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
 +    LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
 +    LintId::of(matches::MATCH_AS_REF),
 +    LintId::of(matches::MATCH_SINGLE_BINDING),
 +    LintId::of(matches::NEEDLESS_MATCH),
 +    LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
 +    LintId::of(methods::BIND_INSTEAD_OF_MAP),
 +    LintId::of(methods::CLONE_ON_COPY),
 +    LintId::of(methods::FILTER_MAP_IDENTITY),
 +    LintId::of(methods::FILTER_NEXT),
 +    LintId::of(methods::FLAT_MAP_IDENTITY),
 +    LintId::of(methods::INSPECT_FOR_EACH),
 +    LintId::of(methods::ITER_COUNT),
 +    LintId::of(methods::MANUAL_FILTER_MAP),
 +    LintId::of(methods::MANUAL_FIND_MAP),
 +    LintId::of(methods::MANUAL_SPLIT_ONCE),
 +    LintId::of(methods::MAP_FLATTEN),
 +    LintId::of(methods::MAP_IDENTITY),
 +    LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
++    LintId::of(methods::NEEDLESS_OPTION_TAKE),
 +    LintId::of(methods::NEEDLESS_SPLITN),
 +    LintId::of(methods::OPTION_AS_REF_DEREF),
 +    LintId::of(methods::OPTION_FILTER_MAP),
 +    LintId::of(methods::OR_THEN_UNWRAP),
 +    LintId::of(methods::SEARCH_IS_SOME),
 +    LintId::of(methods::SKIP_WHILE_NEXT),
 +    LintId::of(methods::UNNECESSARY_FILTER_MAP),
 +    LintId::of(methods::UNNECESSARY_FIND_MAP),
 +    LintId::of(methods::USELESS_ASREF),
 +    LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
 +    LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
 +    LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
 +    LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
 +    LintId::of(needless_bool::BOOL_COMPARISON),
 +    LintId::of(needless_bool::NEEDLESS_BOOL),
 +    LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
 +    LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
 +    LintId::of(needless_update::NEEDLESS_UPDATE),
 +    LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
 +    LintId::of(no_effect::NO_EFFECT),
 +    LintId::of(no_effect::UNNECESSARY_OPERATION),
 +    LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
 +    LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
 +    LintId::of(precedence::PRECEDENCE),
 +    LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
 +    LintId::of(ranges::RANGE_ZIP_WITH_LEN),
 +    LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
 +    LintId::of(redundant_slicing::REDUNDANT_SLICING),
 +    LintId::of(reference::DEREF_ADDROF),
 +    LintId::of(repeat_once::REPEAT_ONCE),
 +    LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
 +    LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
 +    LintId::of(swap::MANUAL_SWAP),
 +    LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
 +    LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
 +    LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
 +    LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
 +    LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
 +    LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
 +    LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
 +    LintId::of(types::BORROWED_BOX),
 +    LintId::of(types::TYPE_COMPLEXITY),
 +    LintId::of(types::VEC_BOX),
 +    LintId::of(unit_types::UNIT_ARG),
 +    LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
 +    LintId::of(unwrap::UNNECESSARY_UNWRAP),
 +    LintId::of(useless_conversion::USELESS_CONVERSION),
 +    LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
 +])
index 532590aaa5a3d294d96dc5cf831318ee8a97c276,0000000000000000000000000000000000000000..5768edc501884feee3e529445ff01f79188cf533
mode 100644,000000..100644
--- /dev/null
@@@ -1,558 -1,0 +1,568 @@@
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_lints(&[
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::CLIPPY_LINTS_INTERNAL,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::COMPILER_LINT_FUNCTIONS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::DEFAULT_LINT,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::IF_CHAIN_STYLE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::INTERNING_DEFINED_SYMBOL,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::INVALID_PATHS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::LINT_WITHOUT_LINT_PASS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::MISSING_MSRV_ATTR_IMPL,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::OUTER_EXPN_EXPN_DATA,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::PRODUCE_ICE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::UNNECESSARY_SYMBOL_STR,
 +    absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
 +    approx_const::APPROX_CONSTANT,
 +    arithmetic::FLOAT_ARITHMETIC,
 +    arithmetic::INTEGER_ARITHMETIC,
 +    as_conversions::AS_CONVERSIONS,
 +    asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
 +    asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
 +    assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
 +    assign_ops::ASSIGN_OP_PATTERN,
 +    assign_ops::MISREFACTORED_ASSIGN_OP,
 +    async_yields_async::ASYNC_YIELDS_ASYNC,
 +    attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON,
 +    attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
 +    attrs::DEPRECATED_CFG_ATTR,
 +    attrs::DEPRECATED_SEMVER,
 +    attrs::EMPTY_LINE_AFTER_OUTER_ATTR,
 +    attrs::INLINE_ALWAYS,
 +    attrs::MISMATCHED_TARGET_OS,
 +    attrs::USELESS_ATTRIBUTE,
++    await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE,
 +    await_holding_invalid::AWAIT_HOLDING_LOCK,
 +    await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
 +    bit_mask::BAD_BIT_MASK,
 +    bit_mask::INEFFECTIVE_BIT_MASK,
 +    bit_mask::VERBOSE_BIT_MASK,
 +    blacklisted_name::BLACKLISTED_NAME,
 +    blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
 +    bool_assert_comparison::BOOL_ASSERT_COMPARISON,
 +    booleans::LOGIC_BUG,
 +    booleans::NONMINIMAL_BOOL,
 +    borrow_as_ptr::BORROW_AS_PTR,
 +    bytecount::NAIVE_BYTECOUNT,
++    bytes_count_to_len::BYTES_COUNT_TO_LEN,
 +    cargo::CARGO_COMMON_METADATA,
 +    cargo::MULTIPLE_CRATE_VERSIONS,
 +    cargo::NEGATIVE_FEATURE_NAMES,
 +    cargo::REDUNDANT_FEATURE_NAMES,
 +    cargo::WILDCARD_DEPENDENCIES,
 +    case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
 +    casts::CAST_ABS_TO_UNSIGNED,
 +    casts::CAST_ENUM_CONSTRUCTOR,
 +    casts::CAST_ENUM_TRUNCATION,
 +    casts::CAST_LOSSLESS,
 +    casts::CAST_POSSIBLE_TRUNCATION,
 +    casts::CAST_POSSIBLE_WRAP,
 +    casts::CAST_PRECISION_LOSS,
 +    casts::CAST_PTR_ALIGNMENT,
 +    casts::CAST_REF_TO_MUT,
 +    casts::CAST_SIGN_LOSS,
 +    casts::CAST_SLICE_DIFFERENT_SIZES,
 +    casts::CHAR_LIT_AS_U8,
 +    casts::FN_TO_NUMERIC_CAST,
 +    casts::FN_TO_NUMERIC_CAST_ANY,
 +    casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
 +    casts::PTR_AS_PTR,
 +    casts::UNNECESSARY_CAST,
 +    checked_conversions::CHECKED_CONVERSIONS,
 +    cognitive_complexity::COGNITIVE_COMPLEXITY,
 +    collapsible_if::COLLAPSIBLE_ELSE_IF,
 +    collapsible_if::COLLAPSIBLE_IF,
 +    collapsible_match::COLLAPSIBLE_MATCH,
 +    comparison_chain::COMPARISON_CHAIN,
 +    copies::BRANCHES_SHARING_CODE,
 +    copies::IFS_SAME_COND,
 +    copies::IF_SAME_THEN_ELSE,
 +    copies::SAME_FUNCTIONS_IN_IF_CONDITION,
 +    copy_iterator::COPY_ITERATOR,
 +    crate_in_macro_def::CRATE_IN_MACRO_DEF,
 +    create_dir::CREATE_DIR,
 +    dbg_macro::DBG_MACRO,
 +    default::DEFAULT_TRAIT_ACCESS,
 +    default::FIELD_REASSIGN_WITH_DEFAULT,
 +    default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
 +    default_union_representation::DEFAULT_UNION_REPRESENTATION,
 +    dereference::EXPLICIT_DEREF_METHODS,
 +    dereference::NEEDLESS_BORROW,
 +    dereference::REF_BINDING_TO_REFERENCE,
 +    derivable_impls::DERIVABLE_IMPLS,
 +    derive::DERIVE_HASH_XOR_EQ,
 +    derive::DERIVE_ORD_XOR_PARTIAL_ORD,
 +    derive::EXPL_IMPL_CLONE_ON_COPY,
 +    derive::UNSAFE_DERIVE_DESERIALIZE,
 +    disallowed_methods::DISALLOWED_METHODS,
 +    disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
 +    disallowed_types::DISALLOWED_TYPES,
 +    doc::DOC_MARKDOWN,
 +    doc::MISSING_ERRORS_DOC,
 +    doc::MISSING_PANICS_DOC,
 +    doc::MISSING_SAFETY_DOC,
 +    doc::NEEDLESS_DOCTEST_MAIN,
 +    double_comparison::DOUBLE_COMPARISONS,
 +    double_parens::DOUBLE_PARENS,
 +    drop_forget_ref::DROP_COPY,
 +    drop_forget_ref::DROP_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,
 +    duration_subsec::DURATION_SUBSEC,
 +    else_if_without_else::ELSE_IF_WITHOUT_ELSE,
++    empty_drop::EMPTY_DROP,
 +    empty_enum::EMPTY_ENUM,
 +    empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS,
 +    entry::MAP_ENTRY,
 +    enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
 +    enum_variants::ENUM_VARIANT_NAMES,
 +    enum_variants::MODULE_INCEPTION,
 +    enum_variants::MODULE_NAME_REPETITIONS,
 +    eq_op::EQ_OP,
 +    eq_op::OP_REF,
 +    equatable_if_let::EQUATABLE_IF_LET,
 +    erasing_op::ERASING_OP,
 +    escape::BOXED_LOCAL,
 +    eta_reduction::REDUNDANT_CLOSURE,
 +    eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
 +    eval_order_dependence::DIVERGING_SUB_EXPRESSION,
 +    eval_order_dependence::EVAL_ORDER_DEPENDENCE,
 +    excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
 +    excessive_bools::STRUCT_EXCESSIVE_BOOLS,
 +    exhaustive_items::EXHAUSTIVE_ENUMS,
 +    exhaustive_items::EXHAUSTIVE_STRUCTS,
 +    exit::EXIT,
 +    explicit_write::EXPLICIT_WRITE,
 +    fallible_impl_from::FALLIBLE_IMPL_FROM,
 +    float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS,
 +    float_literal::EXCESSIVE_PRECISION,
 +    float_literal::LOSSY_FLOAT_LITERAL,
 +    floating_point_arithmetic::IMPRECISE_FLOPS,
 +    floating_point_arithmetic::SUBOPTIMAL_FLOPS,
 +    format::USELESS_FORMAT,
 +    format_args::FORMAT_IN_FORMAT_ARGS,
 +    format_args::TO_STRING_IN_FORMAT_ARGS,
 +    format_impl::PRINT_IN_FORMAT_IMPL,
 +    format_impl::RECURSIVE_FORMAT_IMPL,
++    format_push_string::FORMAT_PUSH_STRING,
 +    formatting::POSSIBLE_MISSING_COMMA,
 +    formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
 +    formatting::SUSPICIOUS_ELSE_FORMATTING,
 +    formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
 +    from_over_into::FROM_OVER_INTO,
 +    from_str_radix_10::FROM_STR_RADIX_10,
 +    functions::DOUBLE_MUST_USE,
 +    functions::MUST_USE_CANDIDATE,
 +    functions::MUST_USE_UNIT,
 +    functions::NOT_UNSAFE_PTR_ARG_DEREF,
 +    functions::RESULT_UNIT_ERR,
 +    functions::TOO_MANY_ARGUMENTS,
 +    functions::TOO_MANY_LINES,
 +    future_not_send::FUTURE_NOT_SEND,
 +    get_last_with_len::GET_LAST_WITH_LEN,
 +    identity_op::IDENTITY_OP,
 +    if_let_mutex::IF_LET_MUTEX,
 +    if_not_else::IF_NOT_ELSE,
 +    if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
 +    implicit_hasher::IMPLICIT_HASHER,
 +    implicit_return::IMPLICIT_RETURN,
 +    implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
 +    inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
 +    index_refutable_slice::INDEX_REFUTABLE_SLICE,
 +    indexing_slicing::INDEXING_SLICING,
 +    indexing_slicing::OUT_OF_BOUNDS_INDEXING,
 +    infinite_iter::INFINITE_ITER,
 +    infinite_iter::MAYBE_INFINITE_ITER,
 +    inherent_impl::MULTIPLE_INHERENT_IMPL,
 +    inherent_to_string::INHERENT_TO_STRING,
 +    inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY,
 +    init_numbered_fields::INIT_NUMBERED_FIELDS,
 +    inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
 +    int_plus_one::INT_PLUS_ONE,
 +    integer_division::INTEGER_DIVISION,
 +    invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
 +    items_after_statements::ITEMS_AFTER_STATEMENTS,
 +    iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
 +    large_const_arrays::LARGE_CONST_ARRAYS,
 +    large_enum_variant::LARGE_ENUM_VARIANT,
++    large_include_file::LARGE_INCLUDE_FILE,
 +    large_stack_arrays::LARGE_STACK_ARRAYS,
 +    len_zero::COMPARISON_TO_EMPTY,
 +    len_zero::LEN_WITHOUT_IS_EMPTY,
 +    len_zero::LEN_ZERO,
 +    let_if_seq::USELESS_LET_IF_SEQ,
 +    let_underscore::LET_UNDERSCORE_DROP,
 +    let_underscore::LET_UNDERSCORE_LOCK,
 +    let_underscore::LET_UNDERSCORE_MUST_USE,
 +    lifetimes::EXTRA_UNUSED_LIFETIMES,
 +    lifetimes::NEEDLESS_LIFETIMES,
 +    literal_representation::DECIMAL_LITERAL_REPRESENTATION,
 +    literal_representation::INCONSISTENT_DIGIT_GROUPING,
 +    literal_representation::LARGE_DIGIT_GROUPS,
 +    literal_representation::MISTYPED_LITERAL_SUFFIXES,
 +    literal_representation::UNREADABLE_LITERAL,
 +    literal_representation::UNUSUAL_BYTE_GROUPINGS,
 +    loops::EMPTY_LOOP,
 +    loops::EXPLICIT_COUNTER_LOOP,
 +    loops::EXPLICIT_INTO_ITER_LOOP,
 +    loops::EXPLICIT_ITER_LOOP,
 +    loops::FOR_KV_MAP,
 +    loops::FOR_LOOPS_OVER_FALLIBLES,
 +    loops::ITER_NEXT_LOOP,
 +    loops::MANUAL_FLATTEN,
 +    loops::MANUAL_MEMCPY,
 +    loops::MISSING_SPIN_LOOP,
 +    loops::MUT_RANGE_BOUND,
 +    loops::NEEDLESS_COLLECT,
 +    loops::NEEDLESS_RANGE_LOOP,
 +    loops::NEVER_LOOP,
 +    loops::SAME_ITEM_PUSH,
 +    loops::SINGLE_ELEMENT_LOOP,
 +    loops::WHILE_IMMUTABLE_CONDITION,
 +    loops::WHILE_LET_LOOP,
 +    loops::WHILE_LET_ON_ITERATOR,
 +    macro_use::MACRO_USE_IMPORTS,
 +    main_recursion::MAIN_RECURSION,
 +    manual_assert::MANUAL_ASSERT,
 +    manual_async_fn::MANUAL_ASYNC_FN,
 +    manual_bits::MANUAL_BITS,
 +    manual_map::MANUAL_MAP,
 +    manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
 +    manual_ok_or::MANUAL_OK_OR,
 +    manual_strip::MANUAL_STRIP,
 +    manual_unwrap_or::MANUAL_UNWRAP_OR,
 +    map_clone::MAP_CLONE,
 +    map_err_ignore::MAP_ERR_IGNORE,
 +    map_unit_fn::OPTION_MAP_UNIT_FN,
 +    map_unit_fn::RESULT_MAP_UNIT_FN,
 +    match_on_vec_items::MATCH_ON_VEC_ITEMS,
 +    match_result_ok::MATCH_RESULT_OK,
 +    match_str_case_mismatch::MATCH_STR_CASE_MISMATCH,
 +    matches::INFALLIBLE_DESTRUCTURING_MATCH,
 +    matches::MATCH_AS_REF,
 +    matches::MATCH_BOOL,
 +    matches::MATCH_LIKE_MATCHES_MACRO,
 +    matches::MATCH_OVERLAPPING_ARM,
 +    matches::MATCH_REF_PATS,
 +    matches::MATCH_SAME_ARMS,
 +    matches::MATCH_SINGLE_BINDING,
 +    matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +    matches::MATCH_WILD_ERR_ARM,
 +    matches::NEEDLESS_MATCH,
 +    matches::REDUNDANT_PATTERN_MATCHING,
 +    matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +    matches::SINGLE_MATCH,
 +    matches::SINGLE_MATCH_ELSE,
 +    matches::WILDCARD_ENUM_MATCH_ARM,
 +    matches::WILDCARD_IN_OR_PATTERNS,
 +    mem_forget::MEM_FORGET,
 +    mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
 +    mem_replace::MEM_REPLACE_WITH_DEFAULT,
 +    mem_replace::MEM_REPLACE_WITH_UNINIT,
 +    methods::BIND_INSTEAD_OF_MAP,
 +    methods::BYTES_NTH,
 +    methods::CHARS_LAST_CMP,
 +    methods::CHARS_NEXT_CMP,
 +    methods::CLONED_INSTEAD_OF_COPIED,
 +    methods::CLONE_DOUBLE_REF,
 +    methods::CLONE_ON_COPY,
 +    methods::CLONE_ON_REF_PTR,
 +    methods::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_UNWRAP,
 +    methods::IMPLICIT_CLONE,
 +    methods::INEFFICIENT_TO_STRING,
 +    methods::INSPECT_FOR_EACH,
 +    methods::INTO_ITER_ON_REF,
++    methods::IS_DIGIT_ASCII_RADIX,
 +    methods::ITERATOR_STEP_BY_ZERO,
 +    methods::ITER_CLONED_COLLECT,
 +    methods::ITER_COUNT,
 +    methods::ITER_NEXT_SLICE,
 +    methods::ITER_NTH,
 +    methods::ITER_NTH_ZERO,
 +    methods::ITER_OVEREAGER_CLONED,
 +    methods::ITER_SKIP_NEXT,
 +    methods::ITER_WITH_DRAIN,
 +    methods::MANUAL_FILTER_MAP,
 +    methods::MANUAL_FIND_MAP,
 +    methods::MANUAL_SATURATING_ARITHMETIC,
 +    methods::MANUAL_SPLIT_ONCE,
 +    methods::MANUAL_STR_REPEAT,
 +    methods::MAP_COLLECT_RESULT_UNIT,
 +    methods::MAP_FLATTEN,
 +    methods::MAP_IDENTITY,
 +    methods::MAP_UNWRAP_OR,
 +    methods::NEEDLESS_OPTION_AS_DEREF,
++    methods::NEEDLESS_OPTION_TAKE,
 +    methods::NEEDLESS_SPLITN,
 +    methods::NEW_RET_NO_SELF,
 +    methods::OK_EXPECT,
 +    methods::OPTION_AS_REF_DEREF,
 +    methods::OPTION_FILTER_MAP,
 +    methods::OPTION_MAP_OR_NONE,
 +    methods::OR_FUN_CALL,
 +    methods::OR_THEN_UNWRAP,
 +    methods::RESULT_MAP_OR_INTO_OPTION,
 +    methods::SEARCH_IS_SOME,
 +    methods::SHOULD_IMPLEMENT_TRAIT,
 +    methods::SINGLE_CHAR_ADD_STR,
 +    methods::SINGLE_CHAR_PATTERN,
 +    methods::SKIP_WHILE_NEXT,
 +    methods::STRING_EXTEND_CHARS,
 +    methods::SUSPICIOUS_MAP,
 +    methods::SUSPICIOUS_SPLITN,
 +    methods::UNINIT_ASSUMED_INIT,
 +    methods::UNNECESSARY_FILTER_MAP,
 +    methods::UNNECESSARY_FIND_MAP,
 +    methods::UNNECESSARY_FOLD,
 +    methods::UNNECESSARY_JOIN,
 +    methods::UNNECESSARY_LAZY_EVALUATIONS,
 +    methods::UNNECESSARY_TO_OWNED,
 +    methods::UNWRAP_OR_ELSE_DEFAULT,
 +    methods::UNWRAP_USED,
 +    methods::USELESS_ASREF,
 +    methods::WRONG_SELF_CONVENTION,
 +    methods::ZST_OFFSET,
 +    minmax::MIN_MAX,
 +    misc::CMP_NAN,
 +    misc::CMP_OWNED,
 +    misc::FLOAT_CMP,
 +    misc::FLOAT_CMP_CONST,
 +    misc::MODULO_ONE,
 +    misc::SHORT_CIRCUIT_STATEMENT,
 +    misc::TOPLEVEL_REF_ARG,
 +    misc::USED_UNDERSCORE_BINDING,
 +    misc::ZERO_PTR,
 +    misc_early::BUILTIN_TYPE_SHADOW,
 +    misc_early::DOUBLE_NEG,
 +    misc_early::DUPLICATE_UNDERSCORE_ARGUMENT,
 +    misc_early::MIXED_CASE_HEX_LITERALS,
 +    misc_early::REDUNDANT_PATTERN,
 +    misc_early::SEPARATED_LITERAL_SUFFIX,
 +    misc_early::UNNEEDED_FIELD_PATTERN,
 +    misc_early::UNNEEDED_WILDCARD_PATTERN,
 +    misc_early::UNSEPARATED_LITERAL_SUFFIX,
 +    misc_early::ZERO_PREFIXED_LITERAL,
 +    missing_const_for_fn::MISSING_CONST_FOR_FN,
 +    missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
 +    missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
 +    missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
 +    module_style::MOD_MODULE_FILES,
 +    module_style::SELF_NAMED_MODULE_FILES,
 +    modulo_arithmetic::MODULO_ARITHMETIC,
 +    mut_key::MUTABLE_KEY_TYPE,
 +    mut_mut::MUT_MUT,
 +    mut_mutex_lock::MUT_MUTEX_LOCK,
 +    mut_reference::UNNECESSARY_MUT_PASSED,
 +    mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
 +    mutex_atomic::MUTEX_ATOMIC,
 +    mutex_atomic::MUTEX_INTEGER,
 +    needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
 +    needless_bitwise_bool::NEEDLESS_BITWISE_BOOL,
 +    needless_bool::BOOL_COMPARISON,
 +    needless_bool::NEEDLESS_BOOL,
 +    needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
 +    needless_continue::NEEDLESS_CONTINUE,
 +    needless_for_each::NEEDLESS_FOR_EACH,
 +    needless_late_init::NEEDLESS_LATE_INIT,
 +    needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
 +    needless_question_mark::NEEDLESS_QUESTION_MARK,
 +    needless_update::NEEDLESS_UPDATE,
 +    neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD,
 +    neg_multiply::NEG_MULTIPLY,
 +    new_without_default::NEW_WITHOUT_DEFAULT,
 +    no_effect::NO_EFFECT,
 +    no_effect::NO_EFFECT_UNDERSCORE_BINDING,
 +    no_effect::UNNECESSARY_OPERATION,
 +    non_copy_const::BORROW_INTERIOR_MUTABLE_CONST,
 +    non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST,
 +    non_expressive_names::JUST_UNDERSCORES_AND_DIGITS,
 +    non_expressive_names::MANY_SINGLE_CHAR_NAMES,
 +    non_expressive_names::SIMILAR_NAMES,
 +    non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
 +    non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY,
 +    nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
 +    octal_escapes::OCTAL_ESCAPES,
 +    only_used_in_recursion::ONLY_USED_IN_RECURSION,
 +    open_options::NONSENSICAL_OPEN_OPTIONS,
 +    option_env_unwrap::OPTION_ENV_UNWRAP,
 +    option_if_let_else::OPTION_IF_LET_ELSE,
 +    overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
 +    panic_in_result_fn::PANIC_IN_RESULT_FN,
 +    panic_unimplemented::PANIC,
 +    panic_unimplemented::TODO,
 +    panic_unimplemented::UNIMPLEMENTED,
 +    panic_unimplemented::UNREACHABLE,
 +    partialeq_ne_impl::PARTIALEQ_NE_IMPL,
 +    pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
 +    pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
 +    path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
 +    pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
 +    precedence::PRECEDENCE,
 +    ptr::CMP_NULL,
 +    ptr::INVALID_NULL_PTR_USAGE,
 +    ptr::MUT_FROM_REF,
 +    ptr::PTR_ARG,
 +    ptr_eq::PTR_EQ,
 +    ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
++    pub_use::PUB_USE,
 +    question_mark::QUESTION_MARK,
 +    ranges::MANUAL_RANGE_CONTAINS,
 +    ranges::RANGE_MINUS_ONE,
 +    ranges::RANGE_PLUS_ONE,
 +    ranges::RANGE_ZIP_WITH_LEN,
 +    ranges::REVERSED_EMPTY_RANGES,
 +    redundant_clone::REDUNDANT_CLONE,
 +    redundant_closure_call::REDUNDANT_CLOSURE_CALL,
 +    redundant_else::REDUNDANT_ELSE,
 +    redundant_field_names::REDUNDANT_FIELD_NAMES,
 +    redundant_pub_crate::REDUNDANT_PUB_CRATE,
 +    redundant_slicing::DEREF_BY_SLICING,
 +    redundant_slicing::REDUNDANT_SLICING,
 +    redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
 +    ref_option_ref::REF_OPTION_REF,
 +    reference::DEREF_ADDROF,
 +    regex::INVALID_REGEX,
 +    regex::TRIVIAL_REGEX,
 +    repeat_once::REPEAT_ONCE,
 +    return_self_not_must_use::RETURN_SELF_NOT_MUST_USE,
 +    returns::LET_AND_RETURN,
 +    returns::NEEDLESS_RETURN,
 +    same_name_method::SAME_NAME_METHOD,
 +    self_assignment::SELF_ASSIGNMENT,
 +    self_named_constructors::SELF_NAMED_CONSTRUCTORS,
 +    semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
 +    serde_api::SERDE_API_MISUSE,
 +    shadow::SHADOW_REUSE,
 +    shadow::SHADOW_SAME,
 +    shadow::SHADOW_UNRELATED,
 +    single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES,
 +    single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
 +    size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
 +    slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
 +    stable_sort_primitive::STABLE_SORT_PRIMITIVE,
 +    strings::STRING_ADD,
 +    strings::STRING_ADD_ASSIGN,
 +    strings::STRING_FROM_UTF8_AS_BYTES,
 +    strings::STRING_LIT_AS_BYTES,
 +    strings::STRING_SLICE,
 +    strings::STRING_TO_STRING,
 +    strings::STR_TO_STRING,
++    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,
 +    tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
 +    temporary_assignment::TEMPORARY_ASSIGNMENT,
 +    to_digit_is_some::TO_DIGIT_IS_SOME,
 +    trailing_empty_array::TRAILING_EMPTY_ARRAY,
 +    trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS,
 +    trait_bounds::TYPE_REPETITION_IN_BOUNDS,
 +    transmute::CROSSPOINTER_TRANSMUTE,
 +    transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
 +    transmute::TRANSMUTE_BYTES_TO_STR,
 +    transmute::TRANSMUTE_FLOAT_TO_INT,
 +    transmute::TRANSMUTE_INT_TO_BOOL,
 +    transmute::TRANSMUTE_INT_TO_CHAR,
 +    transmute::TRANSMUTE_INT_TO_FLOAT,
 +    transmute::TRANSMUTE_NUM_TO_BYTES,
 +    transmute::TRANSMUTE_PTR_TO_PTR,
 +    transmute::TRANSMUTE_PTR_TO_REF,
 +    transmute::TRANSMUTE_UNDEFINED_REPR,
 +    transmute::UNSOUND_COLLECTION_TRANSMUTE,
 +    transmute::USELESS_TRANSMUTE,
 +    transmute::WRONG_TRANSMUTE,
 +    transmuting_null::TRANSMUTING_NULL,
 +    try_err::TRY_ERR,
 +    types::BORROWED_BOX,
 +    types::BOX_COLLECTION,
 +    types::LINKEDLIST,
 +    types::OPTION_OPTION,
 +    types::RC_BUFFER,
 +    types::RC_MUTEX,
 +    types::REDUNDANT_ALLOCATION,
 +    types::TYPE_COMPLEXITY,
 +    types::VEC_BOX,
 +    undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS,
 +    unicode::INVISIBLE_CHARACTERS,
 +    unicode::NON_ASCII_LITERAL,
 +    unicode::UNICODE_NOT_NFC,
 +    uninit_vec::UNINIT_VEC,
 +    unit_hash::UNIT_HASH,
 +    unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
 +    unit_types::LET_UNIT_VALUE,
 +    unit_types::UNIT_ARG,
 +    unit_types::UNIT_CMP,
 +    unnamed_address::FN_ADDRESS_COMPARISONS,
 +    unnamed_address::VTABLE_ADDRESS_COMPARISONS,
++    unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
 +    unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
 +    unnecessary_sort_by::UNNECESSARY_SORT_BY,
 +    unnecessary_wraps::UNNECESSARY_WRAPS,
 +    unnested_or_patterns::UNNESTED_OR_PATTERNS,
 +    unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
 +    unused_async::UNUSED_ASYNC,
 +    unused_io_amount::UNUSED_IO_AMOUNT,
 +    unused_self::UNUSED_SELF,
 +    unused_unit::UNUSED_UNIT,
 +    unwrap::PANICKING_UNWRAP,
 +    unwrap::UNNECESSARY_UNWRAP,
 +    unwrap_in_result::UNWRAP_IN_RESULT,
 +    upper_case_acronyms::UPPER_CASE_ACRONYMS,
 +    use_self::USE_SELF,
 +    useless_conversion::USELESS_CONVERSION,
 +    vec::USELESS_VEC,
 +    vec_init_then_push::VEC_INIT_THEN_PUSH,
 +    vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
 +    verbose_file_reads::VERBOSE_FILE_READS,
 +    wildcard_imports::ENUM_GLOB_USE,
 +    wildcard_imports::WILDCARD_IMPORTS,
 +    write::PRINTLN_EMPTY_STRING,
 +    write::PRINT_LITERAL,
 +    write::PRINT_STDERR,
 +    write::PRINT_STDOUT,
 +    write::PRINT_WITH_NEWLINE,
 +    write::USE_DEBUG,
 +    write::WRITELN_EMPTY_STRING,
 +    write::WRITE_LITERAL,
 +    write::WRITE_WITH_NEWLINE,
 +    zero_div_zero::ZERO_DIVIDED_BY_ZERO,
 +    zero_sized_map_values::ZERO_SIZED_MAP_VALUES,
 +])
index c2fc67afba517f4447d33df8bfc5d504015f6c77,0000000000000000000000000000000000000000..ec187563b3f645a1c3c50d774cc8f2e26bf0d2ab
mode 100644,000000..100644
--- /dev/null
@@@ -1,33 -1,0 +1,36 @@@
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
 +    LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
 +    LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
 +    LintId::of(copies::BRANCHES_SHARING_CODE),
 +    LintId::of(equatable_if_let::EQUATABLE_IF_LET),
 +    LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
 +    LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
 +    LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS),
 +    LintId::of(future_not_send::FUTURE_NOT_SEND),
 +    LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE),
 +    LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
 +    LintId::of(methods::ITER_WITH_DRAIN),
 +    LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
 +    LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
 +    LintId::of(mutex_atomic::MUTEX_ATOMIC),
 +    LintId::of(mutex_atomic::MUTEX_INTEGER),
 +    LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
 +    LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
++    LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
 +    LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
 +    LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
 +    LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
 +    LintId::of(regex::TRIVIAL_REGEX),
 +    LintId::of(strings::STRING_LIT_AS_BYTES),
 +    LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
 +    LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY),
++    LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
++    LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
 +    LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR),
 +    LintId::of(transmute::USELESS_TRANSMUTE),
 +    LintId::of(use_self::USE_SELF),
 +])
index eb6534cb8cae761433a0a979ff932f78d063f5a7,0000000000000000000000000000000000000000..2ee2c6e3358cd030ffd32a1b8287fc379932e348
mode 100644,000000..100644
--- /dev/null
@@@ -1,100 -1,0 +1,98 @@@
-     LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
-     LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
 +    LintId::of(attrs::INLINE_ALWAYS),
 +    LintId::of(bit_mask::VERBOSE_BIT_MASK),
 +    LintId::of(borrow_as_ptr::BORROW_AS_PTR),
 +    LintId::of(bytecount::NAIVE_BYTECOUNT),
 +    LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
 +    LintId::of(casts::CAST_LOSSLESS),
 +    LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
 +    LintId::of(casts::CAST_POSSIBLE_WRAP),
 +    LintId::of(casts::CAST_PRECISION_LOSS),
 +    LintId::of(casts::CAST_PTR_ALIGNMENT),
 +    LintId::of(casts::CAST_SIGN_LOSS),
 +    LintId::of(casts::PTR_AS_PTR),
 +    LintId::of(checked_conversions::CHECKED_CONVERSIONS),
 +    LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION),
 +    LintId::of(copy_iterator::COPY_ITERATOR),
 +    LintId::of(default::DEFAULT_TRAIT_ACCESS),
 +    LintId::of(dereference::EXPLICIT_DEREF_METHODS),
 +    LintId::of(dereference::REF_BINDING_TO_REFERENCE),
 +    LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY),
 +    LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE),
 +    LintId::of(doc::DOC_MARKDOWN),
 +    LintId::of(doc::MISSING_ERRORS_DOC),
 +    LintId::of(doc::MISSING_PANICS_DOC),
 +    LintId::of(empty_enum::EMPTY_ENUM),
 +    LintId::of(enum_variants::MODULE_NAME_REPETITIONS),
 +    LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),
 +    LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS),
 +    LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS),
 +    LintId::of(functions::MUST_USE_CANDIDATE),
 +    LintId::of(functions::TOO_MANY_LINES),
 +    LintId::of(if_not_else::IF_NOT_ELSE),
 +    LintId::of(implicit_hasher::IMPLICIT_HASHER),
 +    LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
 +    LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR),
 +    LintId::of(infinite_iter::MAYBE_INFINITE_ITER),
 +    LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS),
 +    LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS),
 +    LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR),
 +    LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS),
 +    LintId::of(let_underscore::LET_UNDERSCORE_DROP),
 +    LintId::of(literal_representation::LARGE_DIGIT_GROUPS),
 +    LintId::of(literal_representation::UNREADABLE_LITERAL),
 +    LintId::of(loops::EXPLICIT_INTO_ITER_LOOP),
 +    LintId::of(loops::EXPLICIT_ITER_LOOP),
 +    LintId::of(macro_use::MACRO_USE_IMPORTS),
 +    LintId::of(manual_assert::MANUAL_ASSERT),
 +    LintId::of(manual_ok_or::MANUAL_OK_OR),
 +    LintId::of(match_on_vec_items::MATCH_ON_VEC_ITEMS),
 +    LintId::of(matches::MATCH_BOOL),
 +    LintId::of(matches::MATCH_SAME_ARMS),
 +    LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
 +    LintId::of(matches::MATCH_WILD_ERR_ARM),
 +    LintId::of(matches::SINGLE_MATCH_ELSE),
 +    LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
 +    LintId::of(methods::FILTER_MAP_NEXT),
 +    LintId::of(methods::FLAT_MAP_OPTION),
 +    LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
 +    LintId::of(methods::IMPLICIT_CLONE),
 +    LintId::of(methods::INEFFICIENT_TO_STRING),
 +    LintId::of(methods::MAP_UNWRAP_OR),
 +    LintId::of(methods::UNNECESSARY_JOIN),
 +    LintId::of(misc::FLOAT_CMP),
 +    LintId::of(misc::USED_UNDERSCORE_BINDING),
 +    LintId::of(mut_mut::MUT_MUT),
 +    LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL),
 +    LintId::of(needless_continue::NEEDLESS_CONTINUE),
 +    LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
 +    LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
 +    LintId::of(no_effect::NO_EFFECT_UNDERSCORE_BINDING),
 +    LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
 +    LintId::of(non_expressive_names::SIMILAR_NAMES),
 +    LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
 +    LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
 +    LintId::of(ranges::RANGE_MINUS_ONE),
 +    LintId::of(ranges::RANGE_PLUS_ONE),
 +    LintId::of(redundant_else::REDUNDANT_ELSE),
 +    LintId::of(ref_option_ref::REF_OPTION_REF),
 +    LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
 +    LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
++    LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
 +    LintId::of(strings::STRING_ADD_ASSIGN),
-     LintId::of(unit_types::LET_UNIT_VALUE),
 +    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 f2f5c988d8f9056b557e05ac61e43287b01ef2d6,0000000000000000000000000000000000000000..82431863e6cfd56cf3482b2f9f03c6d3ebbd003c
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,31 @@@
-     LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
 +// 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(format_push_string::FORMAT_PUSH_STRING),
 +    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(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(misc::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 4802dd877e99d12d90d85d124d164d034bad14ef,0000000000000000000000000000000000000000..77ec6c83ba4b4d8b5d6691fb0391382582e14686
mode 100644,000000..100644
--- /dev/null
@@@ -1,77 -1,0 +1,80 @@@
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
 +    LintId::of(arithmetic::FLOAT_ARITHMETIC),
 +    LintId::of(arithmetic::INTEGER_ARITHMETIC),
 +    LintId::of(as_conversions::AS_CONVERSIONS),
 +    LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
 +    LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
 +    LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
 +    LintId::of(create_dir::CREATE_DIR),
 +    LintId::of(dbg_macro::DBG_MACRO),
 +    LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),
 +    LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION),
 +    LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
 +    LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
++    LintId::of(empty_drop::EMPTY_DROP),
 +    LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS),
 +    LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
 +    LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
 +    LintId::of(exit::EXIT),
 +    LintId::of(float_literal::LOSSY_FLOAT_LITERAL),
 +    LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE),
 +    LintId::of(implicit_return::IMPLICIT_RETURN),
 +    LintId::of(indexing_slicing::INDEXING_SLICING),
 +    LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL),
 +    LintId::of(integer_division::INTEGER_DIVISION),
++    LintId::of(large_include_file::LARGE_INCLUDE_FILE),
 +    LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
 +    LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
 +    LintId::of(map_err_ignore::MAP_ERR_IGNORE),
 +    LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
 +    LintId::of(matches::WILDCARD_ENUM_MATCH_ARM),
 +    LintId::of(mem_forget::MEM_FORGET),
 +    LintId::of(methods::CLONE_ON_REF_PTR),
 +    LintId::of(methods::EXPECT_USED),
 +    LintId::of(methods::FILETYPE_IS_FILE),
 +    LintId::of(methods::GET_UNWRAP),
 +    LintId::of(methods::UNWRAP_USED),
 +    LintId::of(misc::FLOAT_CMP_CONST),
 +    LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
 +    LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
 +    LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
 +    LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
 +    LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES),
 +    LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
 +    LintId::of(module_style::MOD_MODULE_FILES),
 +    LintId::of(module_style::SELF_NAMED_MODULE_FILES),
 +    LintId::of(modulo_arithmetic::MODULO_ARITHMETIC),
 +    LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
 +    LintId::of(panic_unimplemented::PANIC),
 +    LintId::of(panic_unimplemented::TODO),
 +    LintId::of(panic_unimplemented::UNIMPLEMENTED),
 +    LintId::of(panic_unimplemented::UNREACHABLE),
 +    LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
++    LintId::of(pub_use::PUB_USE),
 +    LintId::of(redundant_slicing::DEREF_BY_SLICING),
 +    LintId::of(same_name_method::SAME_NAME_METHOD),
 +    LintId::of(shadow::SHADOW_REUSE),
 +    LintId::of(shadow::SHADOW_SAME),
 +    LintId::of(shadow::SHADOW_UNRELATED),
 +    LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES),
 +    LintId::of(strings::STRING_ADD),
 +    LintId::of(strings::STRING_SLICE),
 +    LintId::of(strings::STRING_TO_STRING),
 +    LintId::of(strings::STR_TO_STRING),
 +    LintId::of(try_err::TRY_ERR),
 +    LintId::of(types::RC_BUFFER),
 +    LintId::of(types::RC_MUTEX),
 +    LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS),
 +    LintId::of(unicode::NON_ASCII_LITERAL),
 +    LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
 +    LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
 +    LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
 +    LintId::of(write::PRINT_STDERR),
 +    LintId::of(write::PRINT_STDOUT),
 +    LintId::of(write::USE_DEBUG),
 +])
index 3114afac8863e69f574e172383fcc8f50414f50b,0000000000000000000000000000000000000000..d183c0449cd5fd6fb7059de9c8076c5bdcb52f70
mode 100644,000000..100644
--- /dev/null
@@@ -1,118 -1,0 +1,122 @@@
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::style", Some("clippy_style"), vec![
 +    LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
 +    LintId::of(assign_ops::ASSIGN_OP_PATTERN),
 +    LintId::of(blacklisted_name::BLACKLISTED_NAME),
 +    LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
 +    LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
 +    LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
 +    LintId::of(collapsible_if::COLLAPSIBLE_IF),
 +    LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
 +    LintId::of(comparison_chain::COMPARISON_CHAIN),
 +    LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
 +    LintId::of(dereference::NEEDLESS_BORROW),
 +    LintId::of(disallowed_methods::DISALLOWED_METHODS),
 +    LintId::of(disallowed_types::DISALLOWED_TYPES),
 +    LintId::of(doc::MISSING_SAFETY_DOC),
 +    LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
 +    LintId::of(enum_variants::ENUM_VARIANT_NAMES),
 +    LintId::of(enum_variants::MODULE_INCEPTION),
 +    LintId::of(eq_op::OP_REF),
 +    LintId::of(eta_reduction::REDUNDANT_CLOSURE),
 +    LintId::of(float_literal::EXCESSIVE_PRECISION),
 +    LintId::of(from_over_into::FROM_OVER_INTO),
 +    LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
 +    LintId::of(functions::DOUBLE_MUST_USE),
 +    LintId::of(functions::MUST_USE_UNIT),
 +    LintId::of(functions::RESULT_UNIT_ERR),
 +    LintId::of(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_map::MANUAL_MAP),
 +    LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
 +    LintId::of(map_clone::MAP_CLONE),
 +    LintId::of(match_result_ok::MATCH_RESULT_OK),
 +    LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
 +    LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
 +    LintId::of(matches::MATCH_OVERLAPPING_ARM),
 +    LintId::of(matches::MATCH_REF_PATS),
 +    LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
 +    LintId::of(matches::SINGLE_MATCH),
 +    LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
 +    LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
 +    LintId::of(methods::BYTES_NTH),
 +    LintId::of(methods::CHARS_LAST_CMP),
 +    LintId::of(methods::CHARS_NEXT_CMP),
 +    LintId::of(methods::ERR_EXPECT),
 +    LintId::of(methods::INTO_ITER_ON_REF),
++    LintId::of(methods::IS_DIGIT_ASCII_RADIX),
 +    LintId::of(methods::ITER_CLONED_COLLECT),
 +    LintId::of(methods::ITER_NEXT_SLICE),
 +    LintId::of(methods::ITER_NTH_ZERO),
 +    LintId::of(methods::ITER_SKIP_NEXT),
 +    LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
 +    LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
 +    LintId::of(methods::NEW_RET_NO_SELF),
 +    LintId::of(methods::OK_EXPECT),
 +    LintId::of(methods::OPTION_MAP_OR_NONE),
 +    LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
 +    LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
 +    LintId::of(methods::SINGLE_CHAR_ADD_STR),
 +    LintId::of(methods::STRING_EXTEND_CHARS),
 +    LintId::of(methods::UNNECESSARY_FOLD),
 +    LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
 +    LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
 +    LintId::of(methods::WRONG_SELF_CONVENTION),
 +    LintId::of(misc::TOPLEVEL_REF_ARG),
 +    LintId::of(misc::ZERO_PTR),
 +    LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
 +    LintId::of(misc_early::DOUBLE_NEG),
 +    LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
 +    LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
 +    LintId::of(misc_early::REDUNDANT_PATTERN),
 +    LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
 +    LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
 +    LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
 +    LintId::of(neg_multiply::NEG_MULTIPLY),
 +    LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
 +    LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
 +    LintId::of(ptr::CMP_NULL),
 +    LintId::of(ptr::PTR_ARG),
 +    LintId::of(ptr_eq::PTR_EQ),
 +    LintId::of(question_mark::QUESTION_MARK),
 +    LintId::of(ranges::MANUAL_RANGE_CONTAINS),
 +    LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
 +    LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
 +    LintId::of(returns::LET_AND_RETURN),
 +    LintId::of(returns::NEEDLESS_RETURN),
 +    LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
 +    LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
++    LintId::of(strings::TRIM_SPLIT_WHITESPACE),
 +    LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
 +    LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
++    LintId::of(unit_types::LET_UNIT_VALUE),
++    LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
 +    LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
 +    LintId::of(unused_unit::UNUSED_UNIT),
 +    LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
 +    LintId::of(write::PRINTLN_EMPTY_STRING),
 +    LintId::of(write::PRINT_LITERAL),
 +    LintId::of(write::PRINT_WITH_NEWLINE),
 +    LintId::of(write::WRITELN_EMPTY_STRING),
 +    LintId::of(write::WRITE_LITERAL),
 +    LintId::of(write::WRITE_WITH_NEWLINE),
 +])
index 82f45b5fd58b9691e7c13360c9fe5821de283f4d,0000000000000000000000000000000000000000..7a881bfe3991265ea34c021512e42a783e1783a3
mode 100644,000000..100644
--- /dev/null
@@@ -1,30 -1,0 +1,31 @@@
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![
 +    LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
 +    LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
++    LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
 +    LintId::of(casts::CAST_ABS_TO_UNSIGNED),
 +    LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
 +    LintId::of(casts::CAST_ENUM_TRUNCATION),
 +    LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
 +    LintId::of(drop_forget_ref::DROP_NON_DROP),
 +    LintId::of(drop_forget_ref::FORGET_NON_DROP),
 +    LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
 +    LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
 +    LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
 +    LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
 +    LintId::of(loops::EMPTY_LOOP),
 +    LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
 +    LintId::of(loops::MUT_RANGE_BOUND),
 +    LintId::of(methods::SUSPICIOUS_MAP),
 +    LintId::of(mut_key::MUTABLE_KEY_TYPE),
 +    LintId::of(octal_escapes::OCTAL_ESCAPES),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
 +])
index c9b836f95808a623d02b567e5b5ca2f502aa8e33,0000000000000000000000000000000000000000..3bb821a14829535e924d08c81fadab601d68ce53
mode 100644,000000..100644
--- /dev/null
@@@ -1,966 -1,0 +1,954 @@@
-         store.register_late_pass(|| Box::new(utils::inspector::DeepCodeInspector));
 +// error-pattern:cargo-clippy
 +
 +#![feature(array_windows)]
 +#![feature(binary_heap_into_iter_sorted)]
 +#![feature(box_patterns)]
 +#![feature(control_flow_enum)]
 +#![feature(drain_filter)]
 +#![feature(iter_intersperse)]
 +#![feature(let_chains)]
 +#![feature(let_else)]
 +#![feature(once_cell)]
 +#![feature(rustc_private)]
 +#![feature(stmt_expr_attributes)]
 +#![recursion_limit = "512"]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
 +#![warn(trivial_casts, trivial_numeric_casts)]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +// Disable this rustc lint for now, as it was also done in rustc
 +#![allow(rustc::potential_query_instability)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_arena;
 +extern crate rustc_ast;
 +extern crate rustc_ast_pretty;
 +extern crate rustc_attr;
 +extern crate rustc_data_structures;
 +extern crate rustc_driver;
 +extern crate rustc_errors;
 +extern crate rustc_hir;
 +extern crate rustc_hir_pretty;
 +extern crate rustc_index;
 +extern crate rustc_infer;
 +extern crate rustc_lexer;
 +extern crate rustc_lint;
 +extern crate rustc_middle;
 +extern crate rustc_mir_dataflow;
 +extern crate rustc_parse;
 +extern crate rustc_parse_format;
 +extern crate rustc_session;
 +extern crate rustc_span;
 +extern crate rustc_target;
 +extern crate rustc_trait_selection;
 +extern crate rustc_typeck;
 +
 +#[macro_use]
 +extern crate clippy_utils;
 +
 +use clippy_utils::parse_msrv;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_lint::LintId;
 +use rustc_session::Session;
 +
 +/// Macro used to declare a Clippy lint.
 +///
 +/// Every lint declaration consists of 4 parts:
 +///
 +/// 1. The documentation, which is used for the website
 +/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions.
 +/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or
 +///    `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of.
 +/// 4. The `description` that contains a short explanation on what's wrong with code where the
 +///    lint is triggered.
 +///
 +/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are
 +/// enabled by default. As said in the README.md of this repository, if the lint level mapping
 +/// changes, please update README.md.
 +///
 +/// # Example
 +///
 +/// ```
 +/// #![feature(rustc_private)]
 +/// extern crate rustc_session;
 +/// use rustc_session::declare_tool_lint;
 +/// use clippy_lints::declare_clippy_lint;
 +///
 +/// declare_clippy_lint! {
 +///     /// ### What it does
 +///     /// Checks for ... (describe what the lint matches).
 +///     ///
 +///     /// ### Why is this bad?
 +///     /// Supply the reason for linting the code.
 +///     ///
 +///     /// ### Example
 +///     /// ```rust
 +///     /// // Bad
 +///     /// Insert a short example of code that triggers the lint
 +///     ///
 +///     /// // Good
 +///     /// Insert a short example of improved code that doesn't trigger the lint
 +///     /// ```
 +///     pub LINT_NAME,
 +///     pedantic,
 +///     "description"
 +/// }
 +/// ```
 +/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
 +#[macro_export]
 +macro_rules! declare_clippy_lint {
 +    { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, suspicious, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +}
 +
 +#[cfg(feature = "internal")]
 +mod deprecated_lints;
 +#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
 +mod utils;
 +
++mod renamed_lints;
++
 +// begin lints modules, do not remove this comment, it’s used in `update_lints`
 +mod absurd_extreme_comparisons;
 +mod approx_const;
 +mod arithmetic;
 +mod as_conversions;
 +mod asm_syntax;
 +mod assertions_on_constants;
 +mod assign_ops;
 +mod async_yields_async;
 +mod attrs;
 +mod await_holding_invalid;
 +mod bit_mask;
 +mod blacklisted_name;
 +mod blocks_in_if_conditions;
 +mod bool_assert_comparison;
 +mod booleans;
 +mod borrow_as_ptr;
 +mod bytecount;
++mod bytes_count_to_len;
 +mod cargo;
 +mod case_sensitive_file_extension_comparisons;
 +mod casts;
 +mod checked_conversions;
 +mod cognitive_complexity;
 +mod collapsible_if;
 +mod collapsible_match;
 +mod comparison_chain;
 +mod copies;
 +mod copy_iterator;
 +mod crate_in_macro_def;
 +mod create_dir;
 +mod dbg_macro;
 +mod default;
 +mod default_numeric_fallback;
 +mod default_union_representation;
 +mod dereference;
 +mod derivable_impls;
 +mod derive;
 +mod disallowed_methods;
 +mod disallowed_script_idents;
 +mod disallowed_types;
 +mod doc;
 +mod double_comparison;
 +mod double_parens;
 +mod drop_forget_ref;
 +mod duration_subsec;
 +mod else_if_without_else;
++mod empty_drop;
 +mod empty_enum;
 +mod empty_structs_with_brackets;
 +mod entry;
 +mod enum_clike;
 +mod enum_variants;
 +mod eq_op;
 +mod equatable_if_let;
 +mod erasing_op;
 +mod escape;
 +mod eta_reduction;
 +mod eval_order_dependence;
 +mod excessive_bools;
 +mod exhaustive_items;
 +mod exit;
 +mod explicit_write;
 +mod fallible_impl_from;
 +mod float_equality_without_abs;
 +mod float_literal;
 +mod floating_point_arithmetic;
 +mod format;
 +mod format_args;
 +mod format_impl;
++mod format_push_string;
 +mod formatting;
 +mod from_over_into;
 +mod from_str_radix_10;
 +mod functions;
 +mod future_not_send;
 +mod get_last_with_len;
 +mod identity_op;
 +mod if_let_mutex;
 +mod if_not_else;
 +mod if_then_some_else_none;
 +mod implicit_hasher;
 +mod implicit_return;
 +mod implicit_saturating_sub;
 +mod inconsistent_struct_constructor;
 +mod index_refutable_slice;
 +mod indexing_slicing;
 +mod infinite_iter;
 +mod inherent_impl;
 +mod inherent_to_string;
 +mod init_numbered_fields;
 +mod inline_fn_without_body;
 +mod int_plus_one;
 +mod integer_division;
 +mod invalid_upcast_comparisons;
 +mod items_after_statements;
 +mod iter_not_returning_iterator;
 +mod large_const_arrays;
 +mod large_enum_variant;
++mod large_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_map;
 +mod manual_non_exhaustive;
 +mod manual_ok_or;
 +mod manual_strip;
 +mod manual_unwrap_or;
 +mod map_clone;
 +mod map_err_ignore;
 +mod map_unit_fn;
 +mod match_on_vec_items;
 +mod match_result_ok;
 +mod match_str_case_mismatch;
 +mod matches;
 +mod mem_forget;
 +mod mem_replace;
 +mod methods;
 +mod minmax;
 +mod misc;
 +mod misc_early;
 +mod missing_const_for_fn;
 +mod missing_doc;
 +mod missing_enforced_import_rename;
 +mod missing_inline;
 +mod module_style;
 +mod modulo_arithmetic;
 +mod mut_key;
 +mod mut_mut;
 +mod mut_mutex_lock;
 +mod mut_reference;
 +mod mutable_debug_assertion;
 +mod mutex_atomic;
 +mod needless_arbitrary_self_type;
 +mod needless_bitwise_bool;
 +mod needless_bool;
 +mod needless_borrowed_ref;
 +mod needless_continue;
 +mod needless_for_each;
 +mod needless_late_init;
 +mod needless_pass_by_value;
 +mod needless_question_mark;
 +mod needless_update;
 +mod neg_cmp_op_on_partial_ord;
 +mod neg_multiply;
 +mod new_without_default;
 +mod no_effect;
 +mod non_copy_const;
 +mod non_expressive_names;
 +mod non_octal_unix_permissions;
 +mod non_send_fields_in_send_ty;
 +mod nonstandard_macro_braces;
 +mod octal_escapes;
 +mod only_used_in_recursion;
 +mod open_options;
 +mod option_env_unwrap;
 +mod option_if_let_else;
 +mod overflow_check_conditional;
 +mod panic_in_result_fn;
 +mod panic_unimplemented;
 +mod partialeq_ne_impl;
 +mod pass_by_ref_or_value;
 +mod path_buf_push_overwrite;
 +mod pattern_type_mismatch;
 +mod precedence;
 +mod ptr;
 +mod ptr_eq;
 +mod ptr_offset_with_cast;
++mod pub_use;
 +mod question_mark;
 +mod ranges;
 +mod redundant_clone;
 +mod redundant_closure_call;
 +mod redundant_else;
 +mod redundant_field_names;
 +mod redundant_pub_crate;
 +mod redundant_slicing;
 +mod redundant_static_lifetimes;
 +mod ref_option_ref;
 +mod reference;
 +mod regex;
 +mod repeat_once;
 +mod return_self_not_must_use;
 +mod returns;
 +mod same_name_method;
 +mod self_assignment;
 +mod self_named_constructors;
 +mod semicolon_if_nothing_returned;
 +mod serde_api;
 +mod shadow;
 +mod single_char_lifetime_names;
 +mod single_component_path_imports;
 +mod size_of_in_element_count;
 +mod slow_vector_initialization;
 +mod stable_sort_primitive;
 +mod strings;
 +mod strlen_on_c_strings;
 +mod suspicious_operation_groupings;
 +mod suspicious_trait_impl;
 +mod swap;
 +mod tabs_in_doc_comments;
 +mod temporary_assignment;
 +mod to_digit_is_some;
 +mod trailing_empty_array;
 +mod trait_bounds;
 +mod transmute;
 +mod transmuting_null;
 +mod try_err;
 +mod types;
 +mod undocumented_unsafe_blocks;
 +mod unicode;
 +mod uninit_vec;
 +mod unit_hash;
 +mod unit_return_expecting_ord;
 +mod unit_types;
 +mod unnamed_address;
++mod unnecessary_owned_empty_strings;
 +mod unnecessary_self_imports;
 +mod unnecessary_sort_by;
 +mod unnecessary_wraps;
 +mod unnested_or_patterns;
 +mod unsafe_removed_from_name;
 +mod unused_async;
 +mod unused_io_amount;
 +mod unused_self;
 +mod unused_unit;
 +mod unwrap;
 +mod unwrap_in_result;
 +mod upper_case_acronyms;
 +mod use_self;
 +mod useless_conversion;
 +mod vec;
 +mod vec_init_then_push;
 +mod vec_resize_to_zero;
 +mod verbose_file_reads;
 +mod wildcard_imports;
 +mod write;
 +mod zero_div_zero;
 +mod zero_sized_map_values;
 +// end lints modules, do not remove this comment, it’s used in `update_lints`
 +
 +pub use crate::utils::conf::Conf;
 +use crate::utils::conf::TryConf;
 +
 +/// Register all pre expansion lints
 +///
 +/// Pre-expansion lints run before any macro expansion has happened.
 +///
 +/// Note that due to the architecture of the compiler, currently `cfg_attr` attributes on crate
 +/// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
 +///
 +/// Used in `./src/driver.rs`.
 +pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
 +    // NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
 +
 +    let msrv = conf.msrv.as_ref().and_then(|s| {
 +        parse_msrv(s, None, None).or_else(|| {
 +            sess.err(&format!(
 +                "error reading Clippy's configuration file. `{}` is not a valid Rust version",
 +                s
 +            ));
 +            None
 +        })
 +    });
 +
 +    store.register_pre_expansion_pass(|| Box::new(write::Write::default()));
 +    store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv }));
 +}
 +
 +#[doc(hidden)]
 +pub fn read_conf(sess: &Session) -> Conf {
 +    let file_name = match utils::conf::lookup_conf_file() {
 +        Ok(Some(path)) => path,
 +        Ok(None) => return Conf::default(),
 +        Err(error) => {
 +            sess.struct_err(&format!("error finding Clippy's configuration file: {}", error))
 +                .emit();
 +            return Conf::default();
 +        },
 +    };
 +
 +    let TryConf { conf, errors } = utils::conf::read(&file_name);
 +    // all conf errors are non-fatal, we just use the default conf in case of error
 +    for error in errors {
 +        sess.struct_err(&format!(
 +            "error reading Clippy's configuration file `{}`: {}",
 +            file_name.display(),
 +            error
 +        ))
 +        .emit();
 +    }
 +
 +    conf
 +}
 +
 +/// Register all lints and lint groups with the rustc plugin registry
 +///
 +/// Used in `./src/driver.rs`.
 +#[allow(clippy::too_many_lines)]
 +pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
 +    register_removed_non_tool_lints(store);
 +
 +    include!("lib.deprecated.rs");
 +
 +    include!("lib.register_lints.rs");
 +    include!("lib.register_restriction.rs");
 +    include!("lib.register_pedantic.rs");
 +
 +    #[cfg(feature = "internal")]
 +    include!("lib.register_internal.rs");
 +
 +    include!("lib.register_all.rs");
 +    include!("lib.register_style.rs");
 +    include!("lib.register_complexity.rs");
 +    include!("lib.register_correctness.rs");
 +    include!("lib.register_suspicious.rs");
 +    include!("lib.register_perf.rs");
 +    include!("lib.register_cargo.rs");
 +    include!("lib.register_nursery.rs");
 +
 +    #[cfg(feature = "internal")]
 +    {
 +        if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
 +            store.register_late_pass(|| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
 +            return;
 +        }
 +    }
 +
 +    // all the internal lints
 +    #[cfg(feature = "internal")]
 +    {
 +        store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal));
 +        store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce));
-     store.register_late_pass(|| Box::new(await_holding_invalid::AwaitHolding));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::CollapsibleCalls));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::CompilerLintFunctions::new()));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::IfChainStyle));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::InvalidPaths));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::InterningDefinedSymbol::default()));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::LintWithoutLintPass::default()));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::MatchTypeOnDiagItem));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::OuterExpnDataPass));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl));
 +    }
 +
++    store.register_late_pass(|| Box::new(utils::dump_hir::DumpHir));
 +    store.register_late_pass(|| Box::new(utils::author::Author));
-     store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustive::new(msrv)));
++    let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
++    store.register_late_pass(move || {
++        Box::new(await_holding_invalid::AwaitHolding::new(
++            await_holding_invalid_types.clone(),
++        ))
++    });
 +    store.register_late_pass(|| Box::new(serde_api::SerdeApi));
 +    let vec_box_size_threshold = conf.vec_box_size_threshold;
 +    let type_complexity_threshold = conf.type_complexity_threshold;
 +    let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
 +    store.register_late_pass(move || {
 +        Box::new(types::Types::new(
 +            vec_box_size_threshold,
 +            type_complexity_threshold,
 +            avoid_breaking_exported_api,
 +        ))
 +    });
 +    store.register_late_pass(|| Box::new(booleans::NonminimalBool));
 +    store.register_late_pass(|| Box::new(needless_bitwise_bool::NeedlessBitwiseBool));
 +    store.register_late_pass(|| Box::new(eq_op::EqOp));
 +    store.register_late_pass(|| Box::new(enum_clike::UnportableVariant));
 +    store.register_late_pass(|| Box::new(float_literal::FloatLiteral));
 +    let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
 +    store.register_late_pass(move || Box::new(bit_mask::BitMask::new(verbose_bit_mask_threshold)));
 +    store.register_late_pass(|| Box::new(ptr::Ptr));
 +    store.register_late_pass(|| Box::new(ptr_eq::PtrEq));
 +    store.register_late_pass(|| Box::new(needless_bool::NeedlessBool));
 +    store.register_late_pass(|| Box::new(needless_bool::BoolComparison));
 +    store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach));
 +    store.register_late_pass(|| Box::new(misc::MiscLints));
 +    store.register_late_pass(|| Box::new(eta_reduction::EtaReduction));
 +    store.register_late_pass(|| Box::new(identity_op::IdentityOp));
 +    store.register_late_pass(|| Box::new(erasing_op::ErasingOp));
 +    store.register_late_pass(|| Box::new(mut_mut::MutMut));
 +    store.register_late_pass(|| Box::new(mut_reference::UnnecessaryMutPassed));
 +    store.register_late_pass(|| Box::new(len_zero::LenZero));
 +    store.register_late_pass(|| Box::new(attrs::Attributes));
 +    store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
 +    store.register_late_pass(|| Box::new(collapsible_match::CollapsibleMatch));
 +    store.register_late_pass(|| Box::new(unicode::Unicode));
 +    store.register_late_pass(|| Box::new(uninit_vec::UninitVec));
 +    store.register_late_pass(|| Box::new(unit_hash::UnitHash));
 +    store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
 +    store.register_late_pass(|| Box::new(strings::StringAdd));
 +    store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn));
 +    store.register_late_pass(|| Box::new(implicit_saturating_sub::ImplicitSaturatingSub));
 +    store.register_late_pass(|| Box::new(default_numeric_fallback::DefaultNumericFallback));
 +    store.register_late_pass(|| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor));
 +    store.register_late_pass(|| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
 +    store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
 +
 +    let msrv = conf.msrv.as_ref().and_then(|s| {
 +        parse_msrv(s, None, None).or_else(|| {
 +            sess.err(&format!(
 +                "error reading Clippy's configuration file. `{}` is not a valid Rust version",
 +                s
 +            ));
 +            None
 +        })
 +    });
 +
 +    let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
 +    store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv)));
 +    store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv)));
 +    store.register_late_pass(move || Box::new(matches::Matches::new(msrv)));
-     // NOTE: when renaming a lint, add a corresponding test to tests/ui/rename.rs
-     ls.register_renamed("clippy::stutter", "clippy::module_name_repetitions");
-     ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default");
-     ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity");
-     ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes");
-     ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map");
-     ls.register_renamed("clippy::box_vec", "clippy::box_collection");
-     ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions");
-     ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions");
-     ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or");
-     ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or");
-     ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or");
-     ls.register_renamed("clippy::option_unwrap_used", "clippy::unwrap_used");
-     ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used");
-     ls.register_renamed("clippy::option_expect_used", "clippy::expect_used");
-     ls.register_renamed("clippy::result_expect_used", "clippy::expect_used");
-     ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles");
-     ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles");
-     ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
-     ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
-     ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
-     ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok");
-     ls.register_renamed("clippy::disallowed_type", "clippy::disallowed_types");
-     ls.register_renamed("clippy::disallowed_method", "clippy::disallowed_methods");
-     ls.register_renamed("clippy::ref_in_deref", "clippy::needless_borrow");
-     ls.register_renamed("clippy::to_string_in_display", "clippy::recursive_format_impl");
-     // uplifted lints
-     ls.register_renamed("clippy::invalid_ref", "invalid_value");
-     ls.register_renamed("clippy::into_iter_on_array", "array_into_iter");
-     ls.register_renamed("clippy::unused_label", "unused_labels");
-     ls.register_renamed("clippy::drop_bounds", "drop_bounds");
-     ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr");
-     ls.register_renamed("clippy::panic_params", "non_fmt_panics");
-     ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints");
-     ls.register_renamed("clippy::invalid_atomic_ordering", "invalid_atomic_ordering");
-     ls.register_renamed("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums");
++    store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv)));
++    store.register_late_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv)));
 +    store.register_late_pass(move || Box::new(manual_strip::ManualStrip::new(msrv)));
 +    store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)));
 +    store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv)));
 +    store.register_late_pass(move || Box::new(checked_conversions::CheckedConversions::new(msrv)));
 +    store.register_late_pass(move || Box::new(mem_replace::MemReplace::new(msrv)));
 +    store.register_late_pass(move || Box::new(ranges::Ranges::new(msrv)));
 +    store.register_late_pass(move || Box::new(from_over_into::FromOverInto::new(msrv)));
 +    store.register_late_pass(move || Box::new(use_self::UseSelf::new(msrv)));
 +    store.register_late_pass(move || Box::new(missing_const_for_fn::MissingConstForFn::new(msrv)));
 +    store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark));
 +    store.register_late_pass(move || Box::new(casts::Casts::new(msrv)));
 +    store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
 +    store.register_late_pass(move || Box::new(map_clone::MapClone::new(msrv)));
 +
 +    store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
 +    store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
 +    let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
 +    store.register_late_pass(move || {
 +        Box::new(index_refutable_slice::IndexRefutableSlice::new(
 +            max_suggested_slice_pattern_length,
 +            msrv,
 +        ))
 +    });
 +    store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
 +    store.register_late_pass(|| Box::new(shadow::Shadow::default()));
 +    store.register_late_pass(|| Box::new(unit_types::UnitTypes));
 +    store.register_late_pass(|| Box::new(loops::Loops));
 +    store.register_late_pass(|| Box::new(main_recursion::MainRecursion::default()));
 +    store.register_late_pass(|| Box::new(lifetimes::Lifetimes));
 +    store.register_late_pass(|| Box::new(entry::HashMapPass));
 +    store.register_late_pass(|| Box::new(minmax::MinMaxPass));
 +    store.register_late_pass(|| Box::new(open_options::OpenOptions));
 +    store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv));
 +    store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
 +    store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate));
 +    store.register_late_pass(|| Box::new(needless_borrowed_ref::NeedlessBorrowedRef));
 +    store.register_late_pass(|| Box::new(no_effect::NoEffect));
 +    store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment));
 +    store.register_late_pass(|| Box::new(transmute::Transmute));
 +    let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
 +    store.register_late_pass(move || {
 +        Box::new(cognitive_complexity::CognitiveComplexity::new(
 +            cognitive_complexity_threshold,
 +        ))
 +    });
 +    let too_large_for_stack = conf.too_large_for_stack;
 +    store.register_late_pass(move || Box::new(escape::BoxedLocal { too_large_for_stack }));
 +    store.register_late_pass(move || Box::new(vec::UselessVec { too_large_for_stack }));
 +    store.register_late_pass(|| Box::new(panic_unimplemented::PanicUnimplemented));
 +    store.register_late_pass(|| Box::new(strings::StringLitAsBytes));
 +    store.register_late_pass(|| Box::new(derive::Derive));
 +    store.register_late_pass(|| Box::new(derivable_impls::DerivableImpls));
 +    store.register_late_pass(|| Box::new(get_last_with_len::GetLastWithLen));
 +    store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef));
 +    store.register_late_pass(|| Box::new(empty_enum::EmptyEnum));
 +    store.register_late_pass(|| Box::new(absurd_extreme_comparisons::AbsurdExtremeComparisons));
 +    store.register_late_pass(|| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
 +    store.register_late_pass(|| Box::new(regex::Regex));
 +    store.register_late_pass(|| Box::new(copies::CopyAndPaste));
 +    store.register_late_pass(|| Box::new(copy_iterator::CopyIterator));
 +    store.register_late_pass(|| Box::new(format::UselessFormat));
 +    store.register_late_pass(|| Box::new(swap::Swap));
 +    store.register_late_pass(|| Box::new(overflow_check_conditional::OverflowCheckConditional));
 +    store.register_late_pass(|| Box::new(new_without_default::NewWithoutDefault::default()));
 +    let blacklisted_names = conf.blacklisted_names.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_late_pass(move || Box::new(blacklisted_name::BlacklistedName::new(blacklisted_names.clone())));
 +    let too_many_arguments_threshold = conf.too_many_arguments_threshold;
 +    let too_many_lines_threshold = conf.too_many_lines_threshold;
 +    store.register_late_pass(move || {
 +        Box::new(functions::Functions::new(
 +            too_many_arguments_threshold,
 +            too_many_lines_threshold,
 +        ))
 +    });
 +    let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_late_pass(move || Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
 +    store.register_late_pass(|| Box::new(neg_multiply::NegMultiply));
 +    store.register_late_pass(|| Box::new(mem_forget::MemForget));
 +    store.register_late_pass(|| Box::new(arithmetic::Arithmetic::default()));
 +    store.register_late_pass(|| Box::new(assign_ops::AssignOps));
 +    store.register_late_pass(|| Box::new(let_if_seq::LetIfSeq));
 +    store.register_late_pass(|| Box::new(eval_order_dependence::EvalOrderDependence));
 +    store.register_late_pass(|| Box::new(missing_doc::MissingDoc::new()));
 +    store.register_late_pass(|| Box::new(missing_inline::MissingInline));
 +    store.register_late_pass(move || Box::new(exhaustive_items::ExhaustiveItems));
 +    store.register_late_pass(|| Box::new(match_result_ok::MatchResultOk));
 +    store.register_late_pass(|| Box::new(partialeq_ne_impl::PartialEqNeImpl));
 +    store.register_late_pass(|| Box::new(unused_io_amount::UnusedIoAmount));
 +    let enum_variant_size_threshold = conf.enum_variant_size_threshold;
 +    store.register_late_pass(move || Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)));
 +    store.register_late_pass(|| Box::new(explicit_write::ExplicitWrite));
 +    store.register_late_pass(|| Box::new(needless_pass_by_value::NeedlessPassByValue));
 +    let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new(
 +        conf.trivial_copy_size_limit,
 +        conf.pass_by_value_size_limit,
 +        conf.avoid_breaking_exported_api,
 +        &sess.target,
 +    );
 +    store.register_late_pass(move || Box::new(pass_by_ref_or_value));
 +    store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef));
 +    store.register_late_pass(|| Box::new(try_err::TryErr));
 +    store.register_late_pass(|| Box::new(bytecount::ByteCount));
 +    store.register_late_pass(|| Box::new(infinite_iter::InfiniteIter));
 +    store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody));
 +    store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default()));
 +    store.register_late_pass(|| Box::new(implicit_hasher::ImplicitHasher));
 +    store.register_late_pass(|| Box::new(fallible_impl_from::FallibleImplFrom));
 +    store.register_late_pass(|| Box::new(double_comparison::DoubleComparisons));
 +    store.register_late_pass(|| Box::new(question_mark::QuestionMark));
 +    store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
 +    store.register_late_pass(|| Box::new(suspicious_trait_impl::SuspiciousImpl));
 +    store.register_late_pass(|| Box::new(map_unit_fn::MapUnit));
 +    store.register_late_pass(|| Box::new(inherent_impl::MultipleInherentImpl));
 +    store.register_late_pass(|| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd));
 +    store.register_late_pass(|| Box::new(unwrap::Unwrap));
 +    store.register_late_pass(|| Box::new(duration_subsec::DurationSubsec));
 +    store.register_late_pass(|| Box::new(indexing_slicing::IndexingSlicing));
 +    store.register_late_pass(|| Box::new(non_copy_const::NonCopyConst));
 +    store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
 +    store.register_late_pass(|| Box::new(redundant_clone::RedundantClone));
 +    store.register_late_pass(|| Box::new(slow_vector_initialization::SlowVectorInit));
 +    store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
 +    store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
 +    store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
 +    store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
 +    store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
 +    store.register_late_pass(|| Box::new(integer_division::IntegerDivision));
 +    store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
 +    let max_trait_bounds = conf.max_trait_bounds;
 +    store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
 +    store.register_late_pass(|| Box::new(comparison_chain::ComparisonChain));
 +    store.register_late_pass(|| Box::new(mut_key::MutableKeyType));
 +    store.register_late_pass(|| Box::new(modulo_arithmetic::ModuloArithmetic));
 +    store.register_early_pass(|| Box::new(reference::DerefAddrOf));
 +    store.register_early_pass(|| Box::new(double_parens::DoubleParens));
 +    store.register_late_pass(|| Box::new(format_impl::FormatImpl::new()));
 +    store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
 +    store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse));
 +    store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne));
 +    store.register_early_pass(|| Box::new(formatting::Formatting));
 +    store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints));
 +    store.register_early_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
 +    store.register_late_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
 +    store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
 +    store.register_late_pass(|| Box::new(returns::Return));
 +    store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf));
 +    store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements));
 +    store.register_early_pass(|| Box::new(precedence::Precedence));
 +    store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue));
 +    store.register_early_pass(|| Box::new(redundant_else::RedundantElse));
 +    store.register_late_pass(|| Box::new(create_dir::CreateDir));
 +    store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType));
 +    let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions;
 +    store.register_early_pass(move || {
 +        Box::new(literal_representation::LiteralDigitGrouping::new(
 +            literal_representation_lint_fraction_readability,
 +        ))
 +    });
 +    let literal_representation_threshold = conf.literal_representation_threshold;
 +    store.register_early_pass(move || {
 +        Box::new(literal_representation::DecimalLiteralRepresentation::new(
 +            literal_representation_threshold,
 +        ))
 +    });
 +    let enum_variant_name_threshold = conf.enum_variant_name_threshold;
 +    store.register_late_pass(move || {
 +        Box::new(enum_variants::EnumVariantNames::new(
 +            enum_variant_name_threshold,
 +            avoid_breaking_exported_api,
 +        ))
 +    });
 +    store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
 +    let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive;
 +    store.register_late_pass(move || {
 +        Box::new(upper_case_acronyms::UpperCaseAcronyms::new(
 +            avoid_breaking_exported_api,
 +            upper_case_acronyms_aggressive,
 +        ))
 +    });
 +    store.register_late_pass(|| Box::new(default::Default::default()));
 +    store.register_late_pass(|| Box::new(unused_self::UnusedSelf));
 +    store.register_late_pass(|| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
 +    store.register_late_pass(|| Box::new(exit::Exit));
 +    store.register_late_pass(|| Box::new(to_digit_is_some::ToDigitIsSome));
 +    let array_size_threshold = conf.array_size_threshold;
 +    store.register_late_pass(move || Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold)));
 +    store.register_late_pass(move || Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold)));
 +    store.register_late_pass(|| Box::new(floating_point_arithmetic::FloatingPointArithmetic));
 +    store.register_early_pass(|| Box::new(as_conversions::AsConversions));
 +    store.register_late_pass(|| Box::new(let_underscore::LetUnderscore));
 +    store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports));
 +    let max_fn_params_bools = conf.max_fn_params_bools;
 +    let max_struct_bools = conf.max_struct_bools;
 +    store.register_early_pass(move || {
 +        Box::new(excessive_bools::ExcessiveBools::new(
 +            max_struct_bools,
 +            max_fn_params_bools,
 +        ))
 +    });
 +    store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
 +    let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
 +    store.register_late_pass(move || Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
 +    store.register_late_pass(|| Box::new(verbose_file_reads::VerboseFileReads));
 +    store.register_late_pass(|| Box::new(redundant_pub_crate::RedundantPubCrate::default()));
 +    store.register_late_pass(|| Box::new(unnamed_address::UnnamedAddress));
 +    store.register_late_pass(|| Box::new(dereference::Dereferencing::default()));
 +    store.register_late_pass(|| Box::new(option_if_let_else::OptionIfLetElse));
 +    store.register_late_pass(|| Box::new(future_not_send::FutureNotSend));
 +    store.register_late_pass(|| Box::new(if_let_mutex::IfLetMutex));
 +    store.register_late_pass(|| Box::new(if_not_else::IfNotElse));
 +    store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality));
 +    store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock));
 +    store.register_late_pass(|| Box::new(match_on_vec_items::MatchOnVecItems));
 +    store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn));
 +    store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
 +    store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn));
 +    let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
 +    store.register_early_pass(move || {
 +        Box::new(non_expressive_names::NonExpressiveNames {
 +            single_char_binding_names_threshold,
 +        })
 +    });
 +    let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(&macro_matcher)));
 +    store.register_late_pass(|| Box::new(macro_use::MacroUseImports::default()));
 +    store.register_late_pass(|| Box::new(pattern_type_mismatch::PatternTypeMismatch));
 +    store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive));
 +    store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
 +    store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
 +    store.register_late_pass(|| Box::new(self_assignment::SelfAssignment));
 +    store.register_late_pass(|| Box::new(manual_unwrap_or::ManualUnwrapOr));
 +    store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
 +    store.register_late_pass(|| Box::new(float_equality_without_abs::FloatEqualityWithoutAbs));
 +    store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
 +    store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
 +    let disallowed_methods = conf.disallowed_methods.clone();
 +    store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone())));
 +    store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
 +    store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
++    store.register_late_pass(|| Box::new(empty_drop::EmptyDrop));
 +    store.register_late_pass(|| Box::new(strings::StrToString));
 +    store.register_late_pass(|| Box::new(strings::StringToString));
 +    store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
 +    store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default()));
 +    store.register_late_pass(|| {
 +        Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons)
 +    });
 +    store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
 +    store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
 +    store.register_late_pass(|| Box::new(manual_map::ManualMap));
 +    store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
 +    store.register_late_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison));
 +    store.register_early_pass(move || Box::new(module_style::ModStyle));
 +    store.register_late_pass(|| Box::new(unused_async::UnusedAsync));
 +    let disallowed_types = conf.disallowed_types.clone();
 +    store.register_late_pass(move || Box::new(disallowed_types::DisallowedTypes::new(disallowed_types.clone())));
 +    let import_renames = conf.enforced_import_renames.clone();
 +    store.register_late_pass(move || {
 +        Box::new(missing_enforced_import_rename::ImportRename::new(
 +            import_renames.clone(),
 +        ))
 +    });
 +    let scripts = conf.allowed_scripts.clone();
 +    store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(&scripts)));
 +    store.register_late_pass(|| Box::new(strlen_on_c_strings::StrlenOnCStrings));
 +    store.register_late_pass(move || Box::new(self_named_constructors::SelfNamedConstructors));
 +    store.register_late_pass(move || Box::new(iter_not_returning_iterator::IterNotReturningIterator));
 +    store.register_late_pass(move || Box::new(manual_assert::ManualAssert));
 +    let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send;
 +    store.register_late_pass(move || {
 +        Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(
 +            enable_raw_pointer_heuristic_for_send,
 +        ))
 +    });
 +    store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks));
 +    store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch));
 +    store.register_late_pass(move || Box::new(format_args::FormatArgs));
 +    store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray));
 +    store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
 +    store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit));
 +    store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
 +    store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields));
 +    store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
 +    store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv)));
 +    store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
 +    store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
 +    store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion));
 +    store.register_late_pass(|| Box::new(dbg_macro::DbgMacro));
 +    let cargo_ignore_publish = conf.cargo_ignore_publish;
 +    store.register_late_pass(move || {
 +        Box::new(cargo::Cargo {
 +            ignore_publish: cargo_ignore_publish,
 +        })
 +    });
 +    store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
 +    store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
++    store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
++    store.register_early_pass(|| Box::new(pub_use::PubUse));
++    store.register_late_pass(|| Box::new(format_push_string::FormatPushString));
++    store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen));
++    let max_include_file_size = conf.max_include_file_size;
++    store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
++    store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace));
 +    // 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 662a561f171e91c49450f05d4818ff68ce973531,0000000000000000000000000000000000000000..ab5d3fa7b6d9c98b55a5c82c0a192e75f68dde13
mode 100644,000000..100644
--- /dev/null
@@@ -1,546 -1,0 +1,598 @@@
-     walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty, Visitor,
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::trait_ref_of_method;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
++use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
 +use rustc_hir::intravisit::{
-     BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem,
++    walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
++    walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor,
 +};
 +use rustc_hir::FnRetTy::Return;
 +use rustc_hir::{
-         if let ItemKind::Fn(ref sig, ref generics, id) = item.kind {
++    BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem,
 +    ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier,
 +    TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::nested_filter as middle_nested_filter;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::{kw, Ident, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for lifetime annotations which can be removed by
 +    /// relying on lifetime elision.
 +    ///
 +    /// ### Why is this bad?
 +    /// The additional lifetimes make the code look more
 +    /// complicated, while there is nothing out of the ordinary going on. Removing
 +    /// them leads to more readable code.
 +    ///
 +    /// ### Known problems
 +    /// - We bail out if the function has a `where` clause where lifetimes
 +    /// are mentioned due to potential false positives.
 +    /// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the
 +    /// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad: unnecessary lifetime annotations
 +    /// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {
 +    ///     x
 +    /// }
 +    ///
 +    /// // Good
 +    /// fn elided(x: &u8, y: u8) -> &u8 {
 +    ///     x
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEEDLESS_LIFETIMES,
 +    complexity,
 +    "using explicit lifetimes for references in function arguments when elision rules \
 +     would allow omitting them"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for lifetimes in generics that are never used
 +    /// anywhere else.
 +    ///
 +    /// ### Why is this bad?
 +    /// The additional lifetimes make the code look more
 +    /// complicated, while there is nothing out of the ordinary going on. Removing
 +    /// them leads to more readable code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad: unnecessary lifetimes
 +    /// fn unused_lifetime<'a>(x: u8) {
 +    ///     // ..
 +    /// }
 +    ///
 +    /// // Good
 +    /// fn no_lifetime(x: u8) {
 +    ///     // ...
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXTRA_UNUSED_LIFETIMES,
 +    complexity,
 +    "unused lifetimes in function definitions"
 +}
 +
 +declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Lifetimes {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-                 &item.generics,
++        if let ItemKind::Fn(ref sig, generics, id) = item.kind {
 +            check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true);
++        } else if let ItemKind::Impl(impl_) = item.kind {
++            report_extra_impl_lifetimes(cx, impl_);
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
 +        if let ImplItemKind::Fn(ref sig, id) = item.kind {
 +            let report_extra_lifetimes = trait_ref_of_method(cx, item.def_id).is_none();
 +            check_fn_inner(
 +                cx,
 +                sig.decl,
 +                Some(id),
 +                None,
-             check_fn_inner(cx, sig.decl, body, trait_sig, &item.generics, item.span, true);
++                item.generics,
 +                item.span,
 +                report_extra_lifetimes,
 +            );
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if let TraitItemKind::Fn(ref sig, ref body) = item.kind {
 +            let (body, trait_sig) = match *body {
 +                TraitFn::Required(sig) => (None, Some(sig)),
 +                TraitFn::Provided(id) => (Some(id), None),
 +            };
-         }
-         else {
++            check_fn_inner(cx, sig.decl, body, trait_sig, item.generics, item.span, true);
 +        }
 +    }
 +}
 +
 +/// The lifetime of a &-reference.
 +#[derive(PartialEq, Eq, Hash, Debug, Clone)]
 +enum RefLt {
 +    Unnamed,
 +    Static,
 +    Named(Symbol),
 +}
 +
 +fn check_fn_inner<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    decl: &'tcx FnDecl<'_>,
 +    body: Option<BodyId>,
 +    trait_sig: Option<&[Ident]>,
 +    generics: &'tcx Generics<'_>,
 +    span: Span,
 +    report_extra_lifetimes: bool,
 +) {
 +    if span.from_expansion() || has_where_lifetimes(cx, generics) {
 +        return;
 +    }
 +
 +    let types = generics
 +        .params
 +        .iter()
 +        .filter(|param| matches!(param.kind, GenericParamKind::Type { .. }));
 +    for typ in types {
 +        for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) {
 +            if pred.in_where_clause {
 +                // has_where_lifetimes checked that this predicate contains no lifetime.
 +                continue;
 +            }
 +
 +            for bound in pred.bounds {
 +                let mut visitor = RefVisitor::new(cx);
 +                walk_param_bound(&mut visitor, bound);
 +                if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) {
 +                    return;
 +                }
 +                if let GenericBound::Trait(ref trait_ref, _) = *bound {
 +                    let params = &trait_ref
 +                        .trait_ref
 +                        .path
 +                        .segments
 +                        .last()
 +                        .expect("a path must have at least one segment")
 +                        .args;
 +                    if let Some(params) = *params {
 +                        let lifetimes = params.args.iter().filter_map(|arg| match arg {
 +                            GenericArg::Lifetime(lt) => Some(lt),
 +                            _ => None,
 +                        });
 +                        for bound in lifetimes {
 +                            if bound.name != LifetimeName::Static && !bound.is_elided() {
 +                                return;
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    if could_use_elision(cx, decl, body, trait_sig, generics.params) {
 +        span_lint(
 +            cx,
 +            NEEDLESS_LIFETIMES,
 +            span.with_hi(decl.output.span().hi()),
 +            "explicit lifetimes given in parameter types where they could be elided \
 +             (or replaced with `'_` if needed by type declaration)",
 +        );
 +    }
 +    if report_extra_lifetimes {
 +        self::report_extra_lifetimes(cx, decl, generics);
 +    }
 +}
 +
 +// elision doesn't work for explicit self types, see rust-lang/rust#69064
 +fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool {
 +    if_chain! {
 +        if let Some(ident) = ident;
 +        if ident.name == kw::SelfLower;
 +        if !func.implicit_self.has_implicit_self();
 +
 +        if let Some(self_ty) = func.inputs.first();
 +        then {
 +            let mut visitor = RefVisitor::new(cx);
 +            visitor.visit_ty(self_ty);
 +
 +            !visitor.all_lts().is_empty()
- struct LifetimeChecker {
++        } else {
 +            false
 +        }
 +    }
 +}
 +
 +fn could_use_elision<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    func: &'tcx FnDecl<'_>,
 +    body: Option<BodyId>,
 +    trait_sig: Option<&[Ident]>,
 +    named_generics: &'tcx [GenericParam<'_>],
 +) -> bool {
 +    // There are two scenarios where elision works:
 +    // * no output references, all input references have different LT
 +    // * output references, exactly one input reference with same LT
 +    // All lifetimes must be unnamed, 'static or defined without bounds on the
 +    // level of the current item.
 +
 +    // check named LTs
 +    let allowed_lts = allowed_lts_from(named_generics);
 +
 +    // these will collect all the lifetimes for references in arg/return types
 +    let mut input_visitor = RefVisitor::new(cx);
 +    let mut output_visitor = RefVisitor::new(cx);
 +
 +    // extract lifetimes in input argument types
 +    for arg in func.inputs {
 +        input_visitor.visit_ty(arg);
 +    }
 +    // extract lifetimes in output type
 +    if let Return(ty) = func.output {
 +        output_visitor.visit_ty(ty);
 +    }
 +    for lt in named_generics {
 +        input_visitor.visit_generic_param(lt);
 +    }
 +
 +    if input_visitor.abort() || output_visitor.abort() {
 +        return false;
 +    }
 +
 +    if allowed_lts
 +        .intersection(
 +            &input_visitor
 +                .nested_elision_site_lts
 +                .iter()
 +                .chain(output_visitor.nested_elision_site_lts.iter())
 +                .cloned()
 +                .filter(|v| matches!(v, RefLt::Named(_)))
 +                .collect(),
 +        )
 +        .next()
 +        .is_some()
 +    {
 +        return false;
 +    }
 +
 +    let input_lts = input_visitor.lts;
 +    let output_lts = output_visitor.lts;
 +
 +    if let Some(trait_sig) = trait_sig {
 +        if explicit_self_type(cx, func, trait_sig.first().copied()) {
 +            return false;
 +        }
 +    }
 +
 +    if let Some(body_id) = body {
 +        let body = cx.tcx.hir().body(body_id);
 +
 +        let first_ident = body.params.first().and_then(|param| param.pat.simple_ident());
 +        if explicit_self_type(cx, func, first_ident) {
 +            return false;
 +        }
 +
 +        let mut checker = BodyLifetimeChecker {
 +            lifetimes_used_in_body: false,
 +        };
 +        checker.visit_expr(&body.value);
 +        if checker.lifetimes_used_in_body {
 +            return false;
 +        }
 +    }
 +
 +    // check for lifetimes from higher scopes
 +    for lt in input_lts.iter().chain(output_lts.iter()) {
 +        if !allowed_lts.contains(lt) {
 +            return false;
 +        }
 +    }
 +
 +    // no input lifetimes? easy case!
 +    if input_lts.is_empty() {
 +        false
 +    } else if output_lts.is_empty() {
 +        // no output lifetimes, check distinctness of input lifetimes
 +
 +        // only unnamed and static, ok
 +        let unnamed_and_static = input_lts.iter().all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static);
 +        if unnamed_and_static {
 +            return false;
 +        }
 +        // we have no output reference, so we only need all distinct lifetimes
 +        input_lts.len() == unique_lifetimes(&input_lts)
 +    } else {
 +        // we have output references, so we need one input reference,
 +        // and all output lifetimes must be the same
 +        if unique_lifetimes(&output_lts) > 1 {
 +            return false;
 +        }
 +        if input_lts.len() == 1 {
 +            match (&input_lts[0], &output_lts[0]) {
 +                (&RefLt::Named(n1), &RefLt::Named(n2)) if n1 == n2 => true,
 +                (&RefLt::Named(_), &RefLt::Unnamed) => true,
 +                _ => false, /* already elided, different named lifetimes
 +                             * or something static going on */
 +            }
 +        } else {
 +            false
 +        }
 +    }
 +}
 +
 +fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
 +    let mut allowed_lts = FxHashSet::default();
 +    for par in named_generics.iter() {
 +        if let GenericParamKind::Lifetime { .. } = par.kind {
 +            allowed_lts.insert(RefLt::Named(par.name.ident().name));
 +        }
 +    }
 +    allowed_lts.insert(RefLt::Unnamed);
 +    allowed_lts.insert(RefLt::Static);
 +    allowed_lts
 +}
 +
 +/// Number of unique lifetimes in the given vector.
 +#[must_use]
 +fn unique_lifetimes(lts: &[RefLt]) -> usize {
 +    lts.iter().collect::<FxHashSet<_>>().len()
 +}
 +
 +const CLOSURE_TRAIT_BOUNDS: [LangItem; 3] = [LangItem::Fn, LangItem::FnMut, LangItem::FnOnce];
 +
 +/// A visitor usable for `rustc_front::visit::walk_ty()`.
 +struct RefVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    lts: Vec<RefLt>,
 +    nested_elision_site_lts: Vec<RefLt>,
 +    unelided_trait_object_lifetime: bool,
 +}
 +
 +impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            cx,
 +            lts: Vec::new(),
 +            nested_elision_site_lts: Vec::new(),
 +            unelided_trait_object_lifetime: false,
 +        }
 +    }
 +
 +    fn record(&mut self, lifetime: &Option<Lifetime>) {
 +        if let Some(ref lt) = *lifetime {
 +            if lt.name == LifetimeName::Static {
 +                self.lts.push(RefLt::Static);
 +            } else if let LifetimeName::Param(ParamName::Fresh(_)) = lt.name {
 +                // Fresh lifetimes generated should be ignored.
 +            } else if lt.is_elided() {
 +                self.lts.push(RefLt::Unnamed);
 +            } else {
 +                self.lts.push(RefLt::Named(lt.name.ident().name));
 +            }
 +        } else {
 +            self.lts.push(RefLt::Unnamed);
 +        }
 +    }
 +
 +    fn all_lts(&self) -> Vec<RefLt> {
 +        self.lts
 +            .iter()
 +            .chain(self.nested_elision_site_lts.iter())
 +            .cloned()
 +            .collect::<Vec<_>>()
 +    }
 +
 +    fn abort(&self) -> bool {
 +        self.unelided_trait_object_lifetime
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
 +    // for lifetimes as parameters of generics
 +    fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
 +        self.record(&Some(*lifetime));
 +    }
 +
 +    fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>, tbm: TraitBoundModifier) {
 +        let trait_ref = &poly_tref.trait_ref;
 +        if CLOSURE_TRAIT_BOUNDS.iter().any(|&item| {
 +            self.cx
 +                .tcx
 +                .lang_items()
 +                .require(item)
 +                .map_or(false, |id| Some(id) == trait_ref.trait_def_id())
 +        }) {
 +            let mut sub_visitor = RefVisitor::new(self.cx);
 +            sub_visitor.visit_trait_ref(trait_ref);
 +            self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
 +        } else {
 +            walk_poly_trait_ref(self, poly_tref, tbm);
 +        }
 +    }
 +
 +    fn visit_ty(&mut self, ty: &'tcx Ty<'_>) {
 +        match ty.kind {
 +            TyKind::OpaqueDef(item, bounds) => {
 +                let map = self.cx.tcx.hir();
 +                let item = map.item(item);
 +                walk_item(self, item);
 +                walk_ty(self, ty);
 +                self.lts.extend(bounds.iter().filter_map(|bound| match bound {
 +                    GenericArg::Lifetime(l) => Some(RefLt::Named(l.name.ident().name)),
 +                    _ => None,
 +                }));
 +            },
 +            TyKind::BareFn(&BareFnTy { decl, .. }) => {
 +                let mut sub_visitor = RefVisitor::new(self.cx);
 +                sub_visitor.visit_fn_decl(decl);
 +                self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
 +                return;
 +            },
 +            TyKind::TraitObject(bounds, ref lt, _) => {
 +                if !lt.is_elided() {
 +                    self.unelided_trait_object_lifetime = true;
 +                }
 +                for bound in bounds {
 +                    self.visit_poly_trait_ref(bound, TraitBoundModifier::None);
 +                }
 +                return;
 +            },
 +            _ => (),
 +        }
 +        walk_ty(self, ty);
 +    }
 +}
 +
 +/// Are any lifetimes mentioned in the `where` clause? If so, we don't try to
 +/// reason about elision.
 +fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) -> bool {
 +    for predicate in generics.predicates {
 +        match *predicate {
 +            WherePredicate::RegionPredicate(..) => return true,
 +            WherePredicate::BoundPredicate(ref pred) => {
 +                // a predicate like F: Trait or F: for<'a> Trait<'a>
 +                let mut visitor = RefVisitor::new(cx);
 +                // walk the type F, it may not contain LT refs
 +                walk_ty(&mut visitor, pred.bounded_ty);
 +                if !visitor.all_lts().is_empty() {
 +                    return true;
 +                }
 +                // if the bounds define new lifetimes, they are fine to occur
 +                let allowed_lts = allowed_lts_from(pred.bound_generic_params);
 +                // now walk the bounds
 +                for bound in pred.bounds.iter() {
 +                    walk_param_bound(&mut visitor, bound);
 +                }
 +                // and check that all lifetimes are allowed
 +                if visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)) {
 +                    return true;
 +                }
 +            },
 +            WherePredicate::EqPredicate(ref pred) => {
 +                let mut visitor = RefVisitor::new(cx);
 +                walk_ty(&mut visitor, pred.lhs_ty);
 +                walk_ty(&mut visitor, pred.rhs_ty);
 +                if !visitor.lts.is_empty() {
 +                    return true;
 +                }
 +            },
 +        }
 +    }
 +    false
 +}
 +
- impl<'tcx> Visitor<'tcx> for LifetimeChecker {
++struct LifetimeChecker<'cx, 'tcx, F> {
++    cx: &'cx LateContext<'tcx>,
 +    map: FxHashMap<Symbol, Span>,
++    phantom: std::marker::PhantomData<F>,
 +}
 +
-     let mut checker = LifetimeChecker { map: hs };
++impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> {
++    fn new(cx: &'cx LateContext<'tcx>, map: FxHashMap<Symbol, Span>) -> LifetimeChecker<'cx, 'tcx, F> {
++        Self {
++            cx,
++            map,
++            phantom: std::marker::PhantomData,
++        }
++    }
++}
++
++impl<'cx, 'tcx, F> Visitor<'tcx> for LifetimeChecker<'cx, 'tcx, F>
++where
++    F: NestedFilter<'tcx>,
++{
++    type Map = rustc_middle::hir::map::Map<'tcx>;
++    type NestedFilter = F;
++
 +    // for lifetimes as parameters of generics
 +    fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
 +        self.map.remove(&lifetime.name.ident().name);
 +    }
 +
 +    fn visit_generic_param(&mut self, param: &'tcx GenericParam<'_>) {
 +        // don't actually visit `<'a>` or `<'a: 'b>`
 +        // we've already visited the `'a` declarations and
 +        // don't want to spuriously remove them
 +        // `'b` in `'a: 'b` is useless unless used elsewhere in
 +        // a non-lifetime bound
 +        if let GenericParamKind::Type { .. } = param.kind {
 +            walk_generic_param(self, param);
 +        }
 +    }
++
++    fn nested_visit_map(&mut self) -> Self::Map {
++        self.cx.tcx.hir()
++    }
 +}
 +
 +fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) {
 +    let hs = generics
 +        .params
 +        .iter()
 +        .filter_map(|par| match par.kind {
 +            GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)),
 +            _ => None,
 +        })
 +        .collect();
++    let mut checker = LifetimeChecker::<hir_nested_filter::None>::new(cx, hs);
 +
 +    walk_generics(&mut checker, generics);
 +    walk_fn_decl(&mut checker, func);
 +
 +    for &v in checker.map.values() {
 +        span_lint(
 +            cx,
 +            EXTRA_UNUSED_LIFETIMES,
 +            v,
 +            "this lifetime isn't used in the function definition",
 +        );
 +    }
 +}
 +
++fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>) {
++    let hs = impl_
++        .generics
++        .params
++        .iter()
++        .filter_map(|par| match par.kind {
++            GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)),
++            _ => None,
++        })
++        .collect();
++    let mut checker = LifetimeChecker::<middle_nested_filter::All>::new(cx, hs);
++
++    walk_generics(&mut checker, impl_.generics);
++    if let Some(ref trait_ref) = impl_.of_trait {
++        walk_trait_ref(&mut checker, trait_ref);
++    }
++    walk_ty(&mut checker, impl_.self_ty);
++    for item in impl_.items {
++        walk_impl_item_ref(&mut checker, item);
++    }
++
++    for &v in checker.map.values() {
++        span_lint(cx, EXTRA_UNUSED_LIFETIMES, v, "this lifetime isn't used in the impl");
++    }
++}
++
 +struct BodyLifetimeChecker {
 +    lifetimes_used_in_body: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker {
 +    // for lifetimes as parameters of generics
 +    fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
 +        if lifetime.name.ident().name != kw::Empty && lifetime.name.ident().name != kw::StaticLifetime {
 +            self.lifetimes_used_in_body = true;
 +        }
 +    }
 +}
index b7430f49229ae6e673a98fd596f717b3324418c0,0000000000000000000000000000000000000000..269d3c62eafcd5d0e9692f80bfb27a250cbbc0d3
mode 100644,000000..100644
--- /dev/null
@@@ -1,498 -1,0 +1,522 @@@
-     /// - Recommends a signed suffix, even though the number might be too big and an unsigned
-     ///   suffix is required
 +//! Lints concerned with the grouping of digits with underscores in integral or
 +//! floating-point literal expressions.
 +
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::numeric_literal::{NumericLiteral, Radix};
 +use clippy_utils::source::snippet_opt;
 +use if_chain::if_chain;
 +use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use std::iter;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns if a long integral or floating-point constant does
 +    /// not contain underscores.
 +    ///
 +    /// ### Why is this bad?
 +    /// Reading long numbers is difficult without separators.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let x: u64 = 61864918973511;
 +    ///
 +    /// // Good
 +    /// let x: u64 = 61_864_918_973_511;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNREADABLE_LITERAL,
 +    pedantic,
 +    "long literal without underscores"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns for mistyped suffix in literals
 +    ///
 +    /// ### Why is this bad?
 +    /// This is most probably a typo
 +    ///
 +    /// ### Known problems
-     /// ```rust
-     /// // Probably mistyped
-     /// 2_32;
-     ///
-     /// // Good
-     /// 2_i32;
++    /// - Does not match on integers too large to fit in the corresponding unsigned type
 +    /// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers
 +    ///
 +    /// ### Example
-         let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent {
-             (exponent, &["32", "64"][..], 'f')
++    /// `2_32` => `2_i32`
++    /// `250_8 => `250_u8`
 +    /// ```
 +    #[clippy::version = "1.30.0"]
 +    pub MISTYPED_LITERAL_SUFFIXES,
 +    correctness,
 +    "mistyped literal suffix"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns if an integral or floating-point constant is
 +    /// grouped inconsistently with underscores.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readers may incorrectly interpret inconsistently
 +    /// grouped digits.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let x: u64 = 618_64_9189_73_511;
 +    ///
 +    /// // Good
 +    /// let x: u64 = 61_864_918_973_511;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub INCONSISTENT_DIGIT_GROUPING,
 +    style,
 +    "integer literals with digits grouped inconsistently"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns if hexadecimal or binary literals are not grouped
 +    /// by nibble or byte.
 +    ///
 +    /// ### Why is this bad?
 +    /// Negatively impacts readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: u32 = 0xFFF_FFF;
 +    /// let y: u8 = 0b01_011_101;
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub UNUSUAL_BYTE_GROUPINGS,
 +    style,
 +    "binary or hex literals that aren't grouped by four"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns if the digits of an integral or floating-point
 +    /// constant are grouped into groups that
 +    /// are too large.
 +    ///
 +    /// ### Why is this bad?
 +    /// Negatively impacts readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: u64 = 6186491_8973511;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub LARGE_DIGIT_GROUPS,
 +    pedantic,
 +    "grouping digits into groups that are too large"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns if there is a better representation for a numeric literal.
 +    ///
 +    /// ### Why is this bad?
 +    /// Especially for big powers of 2 a hexadecimal representation is more
 +    /// readable than a decimal representation.
 +    ///
 +    /// ### Example
 +    /// `255` => `0xFF`
 +    /// `65_535` => `0xFFFF`
 +    /// `4_042_322_160` => `0xF0F0_F0F0`
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DECIMAL_LITERAL_REPRESENTATION,
 +    restriction,
 +    "using decimal representation when hexadecimal would be better"
 +}
 +
 +enum WarningType {
 +    UnreadableLiteral,
 +    InconsistentDigitGrouping,
 +    LargeDigitGroups,
 +    DecimalRepresentation,
 +    MistypedLiteralSuffix,
 +    UnusualByteGroupings,
 +}
 +
 +impl WarningType {
 +    fn display(&self, suggested_format: String, cx: &EarlyContext<'_>, span: rustc_span::Span) {
 +        match self {
 +            Self::MistypedLiteralSuffix => span_lint_and_sugg(
 +                cx,
 +                MISTYPED_LITERAL_SUFFIXES,
 +                span,
 +                "mistyped literal suffix",
 +                "did you mean to write",
 +                suggested_format,
 +                Applicability::MaybeIncorrect,
 +            ),
 +            Self::UnreadableLiteral => span_lint_and_sugg(
 +                cx,
 +                UNREADABLE_LITERAL,
 +                span,
 +                "long literal lacking separators",
 +                "consider",
 +                suggested_format,
 +                Applicability::MachineApplicable,
 +            ),
 +            Self::LargeDigitGroups => span_lint_and_sugg(
 +                cx,
 +                LARGE_DIGIT_GROUPS,
 +                span,
 +                "digit groups should be smaller",
 +                "consider",
 +                suggested_format,
 +                Applicability::MachineApplicable,
 +            ),
 +            Self::InconsistentDigitGrouping => span_lint_and_sugg(
 +                cx,
 +                INCONSISTENT_DIGIT_GROUPING,
 +                span,
 +                "digits grouped inconsistently by underscores",
 +                "consider",
 +                suggested_format,
 +                Applicability::MachineApplicable,
 +            ),
 +            Self::DecimalRepresentation => span_lint_and_sugg(
 +                cx,
 +                DECIMAL_LITERAL_REPRESENTATION,
 +                span,
 +                "integer literal has a better hexadecimal representation",
 +                "consider",
 +                suggested_format,
 +                Applicability::MachineApplicable,
 +            ),
 +            Self::UnusualByteGroupings => span_lint_and_sugg(
 +                cx,
 +                UNUSUAL_BYTE_GROUPINGS,
 +                span,
 +                "digits of hex or binary literal not grouped by four",
 +                "consider",
 +                suggested_format,
 +                Applicability::MachineApplicable,
 +            ),
 +        };
 +    }
 +}
 +
 +#[allow(clippy::module_name_repetitions)]
 +#[derive(Copy, Clone)]
 +pub struct LiteralDigitGrouping {
 +    lint_fraction_readability: bool,
 +}
 +
 +impl_lint_pass!(LiteralDigitGrouping => [
 +    UNREADABLE_LITERAL,
 +    INCONSISTENT_DIGIT_GROUPING,
 +    LARGE_DIGIT_GROUPS,
 +    MISTYPED_LITERAL_SUFFIXES,
 +    UNUSUAL_BYTE_GROUPINGS,
 +]);
 +
 +impl EarlyLintPass for LiteralDigitGrouping {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
 +        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
 +
 +        if let ExprKind::Lit(ref lit) = expr.kind {
 +            self.check_lit(cx, lit);
 +        }
 +    }
 +}
 +
 +// Length of each UUID hyphenated group in hex digits.
 +const UUID_GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12];
 +
 +impl LiteralDigitGrouping {
 +    pub fn new(lint_fraction_readability: bool) -> Self {
 +        Self {
 +            lint_fraction_readability,
 +        }
 +    }
 +
 +    fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
 +        if_chain! {
 +            if let Some(src) = snippet_opt(cx, lit.span);
 +            if let Some(mut num_lit) = NumericLiteral::from_lit(&src, lit);
 +            then {
 +                if !Self::check_for_mistyped_suffix(cx, lit.span, &mut num_lit) {
 +                    return;
 +                }
 +
 +                if Self::is_literal_uuid_formatted(&mut num_lit) {
 +                    return;
 +                }
 +
 +                let result = (|| {
 +
 +                    let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix, true)?;
 +                    if let Some(fraction) = num_lit.fraction {
 +                        let fractional_group_size = Self::get_group_size(
 +                            fraction.rsplit('_'),
 +                            num_lit.radix,
 +                            self.lint_fraction_readability)?;
 +
 +                        let consistent = Self::parts_consistent(integral_group_size,
 +                                                                fractional_group_size,
 +                                                                num_lit.integer.len(),
 +                                                                fraction.len());
 +                        if !consistent {
 +                            return Err(WarningType::InconsistentDigitGrouping);
 +                        };
 +                    }
 +
 +                    Ok(())
 +                })();
 +
 +
 +                if let Err(warning_type) = result {
 +                    let should_warn = match warning_type {
 +                        | WarningType::UnreadableLiteral
 +                        | WarningType::InconsistentDigitGrouping
 +                        | WarningType::UnusualByteGroupings
 +                        | WarningType::LargeDigitGroups => {
 +                            !lit.span.from_expansion()
 +                        }
 +                        WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => {
 +                            true
 +                        }
 +                    };
 +                    if should_warn {
 +                        warning_type.display(num_lit.format(), cx, lit.span);
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    // Returns `false` if the check fails
 +    fn check_for_mistyped_suffix(
 +        cx: &EarlyContext<'_>,
 +        span: rustc_span::Span,
 +        num_lit: &mut NumericLiteral<'_>,
 +    ) -> bool {
 +        if num_lit.suffix.is_some() {
 +            return true;
 +        }
 +
-             (&mut num_lit.integer, &["32", "64"][..], 'f')
++        let (part, mistyped_suffixes, is_float) = if let Some((_, exponent)) = &mut num_lit.exponent {
++            (exponent, &["32", "64"][..], true)
 +        } else if num_lit.fraction.is_some() {
-             (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i')
++            return true;
 +        } else {
-             *part = &part[..part.len() - last_group.len()];
++            (&mut num_lit.integer, &["8", "16", "32", "64"][..], false)
 +        };
 +
 +        let mut split = part.rsplit('_');
 +        let last_group = split.next().expect("At least one group");
 +        if split.next().is_some() && mistyped_suffixes.contains(&last_group) {
++            let main_part = &part[..part.len() - last_group.len()];
++            let missing_char;
++            if is_float {
++                missing_char = 'f';
++            } else {
++                let radix = match num_lit.radix {
++                    Radix::Binary => 2,
++                    Radix::Octal => 8,
++                    Radix::Decimal => 10,
++                    Radix::Hexadecimal => 16,
++                };
++                if let Ok(int) = u64::from_str_radix(&main_part.replace('_', ""), radix) {
++                    missing_char = match (last_group, int) {
++                        ("8", i) if i8::try_from(i).is_ok() => 'i',
++                        ("16", i) if i16::try_from(i).is_ok() => 'i',
++                        ("32", i) if i32::try_from(i).is_ok() => 'i',
++                        ("64", i) if i64::try_from(i).is_ok() => 'i',
++                        ("8", u) if u8::try_from(u).is_ok() => 'u',
++                        ("16", u) if u16::try_from(u).is_ok() => 'u',
++                        ("32", u) if u32::try_from(u).is_ok() => 'u',
++                        ("64", _) => 'u',
++                        _ => {
++                            return true;
++                        },
++                    }
++                } else {
++                    return true;
++                }
++            }
++            *part = main_part;
 +            let mut sugg = num_lit.format();
 +            sugg.push('_');
 +            sugg.push(missing_char);
 +            sugg.push_str(last_group);
 +            WarningType::MistypedLiteralSuffix.display(sugg, cx, span);
 +            false
 +        } else {
 +            true
 +        }
 +    }
 +
 +    /// Checks whether the numeric literal matches the formatting of a UUID.
 +    ///
 +    /// Returns `true` if the radix is hexadecimal, and the groups match the
 +    /// UUID format of 8-4-4-4-12.
 +    fn is_literal_uuid_formatted(num_lit: &mut NumericLiteral<'_>) -> bool {
 +        if num_lit.radix != Radix::Hexadecimal {
 +            return false;
 +        }
 +
 +        // UUIDs should not have a fraction
 +        if num_lit.fraction.is_some() {
 +            return false;
 +        }
 +
 +        let group_sizes: Vec<usize> = num_lit.integer.split('_').map(str::len).collect();
 +        if UUID_GROUP_LENS.len() == group_sizes.len() {
 +            iter::zip(&UUID_GROUP_LENS, &group_sizes).all(|(&a, &b)| a == b)
 +        } else {
 +            false
 +        }
 +    }
 +
 +    /// Given the sizes of the digit groups of both integral and fractional
 +    /// parts, and the length
 +    /// of both parts, determine if the digits have been grouped consistently.
 +    #[must_use]
 +    fn parts_consistent(
 +        int_group_size: Option<usize>,
 +        frac_group_size: Option<usize>,
 +        int_size: usize,
 +        frac_size: usize,
 +    ) -> bool {
 +        match (int_group_size, frac_group_size) {
 +            // No groups on either side of decimal point - trivially consistent.
 +            (None, None) => true,
 +            // Integral part has grouped digits, fractional part does not.
 +            (Some(int_group_size), None) => frac_size <= int_group_size,
 +            // Fractional part has grouped digits, integral part does not.
 +            (None, Some(frac_group_size)) => int_size <= frac_group_size,
 +            // Both parts have grouped digits. Groups should be the same size.
 +            (Some(int_group_size), Some(frac_group_size)) => int_group_size == frac_group_size,
 +        }
 +    }
 +
 +    /// Returns the size of the digit groups (or None if ungrouped) if successful,
 +    /// otherwise returns a `WarningType` for linting.
 +    fn get_group_size<'a>(
 +        groups: impl Iterator<Item = &'a str>,
 +        radix: Radix,
 +        lint_unreadable: bool,
 +    ) -> Result<Option<usize>, WarningType> {
 +        let mut groups = groups.map(str::len);
 +
 +        let first = groups.next().expect("At least one group");
 +
 +        if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i != 4 && i != 2) {
 +            return Err(WarningType::UnusualByteGroupings);
 +        }
 +
 +        if let Some(second) = groups.next() {
 +            if !groups.all(|x| x == second) || first > second {
 +                Err(WarningType::InconsistentDigitGrouping)
 +            } else if second > 4 {
 +                Err(WarningType::LargeDigitGroups)
 +            } else {
 +                Ok(Some(second))
 +            }
 +        } else if first > 5 && lint_unreadable {
 +            Err(WarningType::UnreadableLiteral)
 +        } else {
 +            Ok(None)
 +        }
 +    }
 +}
 +
 +#[allow(clippy::module_name_repetitions)]
 +#[derive(Copy, Clone)]
 +pub struct DecimalLiteralRepresentation {
 +    threshold: u64,
 +}
 +
 +impl_lint_pass!(DecimalLiteralRepresentation => [DECIMAL_LITERAL_REPRESENTATION]);
 +
 +impl EarlyLintPass for DecimalLiteralRepresentation {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
 +        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
 +
 +        if let ExprKind::Lit(ref lit) = expr.kind {
 +            self.check_lit(cx, lit);
 +        }
 +    }
 +}
 +
 +impl DecimalLiteralRepresentation {
 +    #[must_use]
 +    pub fn new(threshold: u64) -> Self {
 +        Self { threshold }
 +    }
 +    fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
 +        // Lint integral literals.
 +        if_chain! {
 +            if let LitKind::Int(val, _) = lit.kind;
 +            if let Some(src) = snippet_opt(cx, lit.span);
 +            if let Some(num_lit) = NumericLiteral::from_lit(&src, lit);
 +            if num_lit.radix == Radix::Decimal;
 +            if val >= u128::from(self.threshold);
 +            then {
 +                let hex = format!("{:#X}", val);
 +                let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false);
 +                let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| {
 +                    warning_type.display(num_lit.format(), cx, lit.span);
 +                });
 +            }
 +        }
 +    }
 +
 +    fn do_lint(digits: &str) -> Result<(), WarningType> {
 +        if digits.len() == 1 {
 +            // Lint for 1 digit literals, if someone really sets the threshold that low
 +            if digits == "1"
 +                || digits == "2"
 +                || digits == "4"
 +                || digits == "8"
 +                || digits == "3"
 +                || digits == "7"
 +                || digits == "F"
 +            {
 +                return Err(WarningType::DecimalRepresentation);
 +            }
 +        } else if digits.len() < 4 {
 +            // Lint for Literals with a hex-representation of 2 or 3 digits
 +            let f = &digits[0..1]; // first digit
 +            let s = &digits[1..]; // suffix
 +
 +            // Powers of 2
 +            if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0'))
 +                // Powers of 2 minus 1
 +                || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F'))
 +            {
 +                return Err(WarningType::DecimalRepresentation);
 +            }
 +        } else {
 +            // Lint for Literals with a hex-representation of 4 digits or more
 +            let f = &digits[0..1]; // first digit
 +            let m = &digits[1..digits.len() - 1]; // middle digits, except last
 +            let s = &digits[1..]; // suffix
 +
 +            // Powers of 2 with a margin of +15/-16
 +            if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0'))
 +                || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F'))
 +                // Lint for representations with only 0s and Fs, while allowing 7 as the first
 +                // digit
 +                || ((f.eq("7") || f.eq("F")) && s.chars().all(|c| c == '0' || c == 'F'))
 +            {
 +                return Err(WarningType::DecimalRepresentation);
 +            }
 +        }
 +
 +        Ok(())
 +    }
 +}
index 9ba9642fcc833691fabcd4489b206e6ddb16a37a,0000000000000000000000000000000000000000..70a118d6b353930ea8c751c0eef2fe0a96b47e0b
mode 100644,000000..100644
--- /dev/null
@@@ -1,211 -1,0 +1,212 @@@
-                 InlineAsmOperand::In { expr, .. }
-                 | InlineAsmOperand::InOut { expr, .. } => never_loop_expr(expr, main_loop_id),
 +use super::utils::make_iterator_snippet;
 +use super::NEVER_LOOP;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::higher::ForLoop;
 +use clippy_utils::source::snippet;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind};
 +use rustc_lint::LateContext;
 +use rustc_span::Span;
 +use std::iter::{once, Iterator};
 +
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    block: &Block<'_>,
 +    loop_id: HirId,
 +    span: Span,
 +    for_loop: Option<&ForLoop<'_>>,
 +) {
 +    match never_loop_block(block, loop_id) {
 +        NeverLoopResult::AlwaysBreak => {
 +            span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| {
 +                if let Some(ForLoop {
 +                    arg: iterator,
 +                    pat,
 +                    span: for_span,
 +                    ..
 +                }) = for_loop
 +                {
 +                    // Suggests using an `if let` instead. This is `Unspecified` because the
 +                    // loop may (probably) contain `break` statements which would be invalid
 +                    // in an `if let`.
 +                    diag.span_suggestion_verbose(
 +                        for_span.with_hi(iterator.span.hi()),
 +                        "if you need the first element of the iterator, try writing",
 +                        for_to_if_let_sugg(cx, iterator, pat),
 +                        Applicability::Unspecified,
 +                    );
 +                }
 +            });
 +        },
 +        NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (),
 +    }
 +}
 +
 +enum NeverLoopResult {
 +    // A break/return always get triggered but not necessarily for the main loop.
 +    AlwaysBreak,
 +    // A continue may occur for the main loop.
 +    MayContinueMainLoop,
 +    Otherwise,
 +}
 +
 +#[must_use]
 +fn absorb_break(arg: &NeverLoopResult) -> NeverLoopResult {
 +    match *arg {
 +        NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise,
 +        NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop,
 +    }
 +}
 +
 +// Combine two results for parts that are called in order.
 +#[must_use]
 +fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult {
 +    match first {
 +        NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop => first,
 +        NeverLoopResult::Otherwise => second,
 +    }
 +}
 +
 +// Combine two results where both parts are called but not necessarily in order.
 +#[must_use]
 +fn combine_both(left: NeverLoopResult, right: NeverLoopResult) -> NeverLoopResult {
 +    match (left, right) {
 +        (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
 +            NeverLoopResult::MayContinueMainLoop
 +        },
 +        (NeverLoopResult::AlwaysBreak, _) | (_, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
 +        (NeverLoopResult::Otherwise, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
 +    }
 +}
 +
 +// Combine two results where only one of the part may have been executed.
 +#[must_use]
 +fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult {
 +    match (b1, b2) {
 +        (NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
 +        (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
 +            NeverLoopResult::MayContinueMainLoop
 +        },
 +        (NeverLoopResult::Otherwise, _) | (_, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
 +    }
 +}
 +
 +fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult {
 +    let mut iter = block.stmts.iter().filter_map(stmt_to_expr).chain(block.expr);
 +    never_loop_expr_seq(&mut iter, main_loop_id)
 +}
 +
 +fn never_loop_expr_seq<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult {
 +    es.map(|e| never_loop_expr(e, main_loop_id))
 +        .fold(NeverLoopResult::Otherwise, combine_seq)
 +}
 +
 +fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +    match stmt.kind {
 +        StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some(e),
 +        StmtKind::Local(local) => local.init,
 +        StmtKind::Item(..) => None,
 +    }
 +}
 +
 +fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
 +    match expr.kind {
 +        ExprKind::Box(e)
 +        | ExprKind::Unary(_, e)
 +        | ExprKind::Cast(e, _)
 +        | ExprKind::Type(e, _)
 +        | ExprKind::Field(e, _)
 +        | ExprKind::AddrOf(_, _, e)
 +        | ExprKind::Struct(_, _, Some(e))
 +        | ExprKind::Repeat(e, _)
 +        | ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id),
 +        ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, main_loop_id),
 +        ExprKind::Array(es) | ExprKind::MethodCall(_, es, _) | ExprKind::Tup(es) => {
 +            never_loop_expr_all(&mut es.iter(), main_loop_id)
 +        },
 +        ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id),
 +        ExprKind::Binary(_, e1, e2)
 +        | ExprKind::Assign(e1, e2, _)
 +        | ExprKind::AssignOp(_, e1, e2)
 +        | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id),
 +        ExprKind::Loop(b, _, _, _) => {
 +            // Break can come from the inner loop so remove them.
 +            absorb_break(&never_loop_block(b, main_loop_id))
 +        },
 +        ExprKind::If(e, e2, e3) => {
 +            let e1 = never_loop_expr(e, main_loop_id);
 +            let e2 = never_loop_expr(e2, main_loop_id);
 +            let e3 = e3
 +                .as_ref()
 +                .map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id));
 +            combine_seq(e1, combine_branches(e2, e3))
 +        },
 +        ExprKind::Match(e, arms, _) => {
 +            let e = never_loop_expr(e, main_loop_id);
 +            if arms.is_empty() {
 +                e
 +            } else {
 +                let arms = never_loop_expr_branch(&mut arms.iter().map(|a| &*a.body), main_loop_id);
 +                combine_seq(e, arms)
 +            }
 +        },
 +        ExprKind::Block(b, _) => never_loop_block(b, main_loop_id),
 +        ExprKind::Continue(d) => {
 +            let id = d
 +                .target_id
 +                .expect("target ID can only be missing in the presence of compilation errors");
 +            if id == main_loop_id {
 +                NeverLoopResult::MayContinueMainLoop
 +            } else {
 +                NeverLoopResult::AlwaysBreak
 +            }
 +        },
 +        ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
 +            combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
 +        }),
 +        ExprKind::InlineAsm(asm) => asm
 +            .operands
 +            .iter()
 +            .map(|(o, _)| match o {
++                InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
++                    never_loop_expr(expr, main_loop_id)
++                },
 +                InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter(), main_loop_id),
 +                InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
 +                    never_loop_expr_all(&mut once(in_expr).chain(out_expr.iter()), main_loop_id)
 +                },
 +                InlineAsmOperand::Const { .. }
 +                | InlineAsmOperand::SymFn { .. }
 +                | InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise,
 +            })
 +            .fold(NeverLoopResult::Otherwise, combine_both),
 +        ExprKind::Struct(_, _, None)
 +        | ExprKind::Yield(_, _)
 +        | ExprKind::Closure(_, _, _, _, _)
 +        | ExprKind::Path(_)
 +        | ExprKind::ConstBlock(_)
 +        | ExprKind::Lit(_)
 +        | ExprKind::Err => NeverLoopResult::Otherwise,
 +    }
 +}
 +
 +fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult {
 +    es.map(|e| never_loop_expr(e, main_loop_id))
 +        .fold(NeverLoopResult::Otherwise, combine_both)
 +}
 +
 +fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(e: &mut T, main_loop_id: HirId) -> NeverLoopResult {
 +    e.map(|e| never_loop_expr(e, main_loop_id))
 +        .fold(NeverLoopResult::AlwaysBreak, combine_branches)
 +}
 +
 +fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) -> String {
 +    let pat_snippet = snippet(cx, pat.span, "_");
 +    let iter_snippet = make_iterator_snippet(cx, iterator, &mut Applicability::Unspecified);
 +
 +    format!(
 +        "if let Some({pat}) = {iter}.next()",
 +        pat = pat_snippet,
 +        iter = iter_snippet
 +    )
 +}
index dcf44303cf449f2a0b83322414643fdd7243e6db,0000000000000000000000000000000000000000..ac3d9447b6bd3d35fc2472e35ca7dd00ad84eb9e
mode 100644,000000..100644
--- /dev/null
@@@ -1,110 -1,0 +1,146 @@@
- use clippy_utils::source::snippet_opt;
- use clippy_utils::{meets_msrv, msrvs};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
-     /// usize::BITS;
++use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::{get_parent_expr, meets_msrv, msrvs};
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::{self, Ty};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for uses of `std::mem::size_of::<T>() * 8` when
 +    /// `T::BITS` is available.
 +    ///
 +    /// ### Why is this bad?
 +    /// Can be written as the shorter `T::BITS`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// std::mem::size_of::<usize>() * 8;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
++    /// usize::BITS as usize;
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub MANUAL_BITS,
 +    style,
 +    "manual implementation of `size_of::<T>() * 8` can be simplified with `T::BITS`"
 +}
 +
 +#[derive(Clone)]
 +pub struct ManualBits {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl ManualBits {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(ManualBits => [MANUAL_BITS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ManualBits {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if !meets_msrv(self.msrv.as_ref(), &msrvs::MANUAL_BITS) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind;
 +            if let BinOpKind::Mul = &bin_op.node;
 +            if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr);
 +            if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_));
 +            if let ExprKind::Lit(lit) = &other_expr.kind;
 +            if let LitKind::Int(8, _) = lit.node;
-                     format!("{}::BITS", snippet_opt(cx, real_ty.span).unwrap()),
-                     Applicability::MachineApplicable,
 +            then {
++                let mut app = Applicability::MachineApplicable;
++                let ty_snip = snippet_with_applicability(cx, real_ty.span, "..", &mut app);
++                let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS"));
++
 +                span_lint_and_sugg(
 +                    cx,
 +                    MANUAL_BITS,
 +                    expr.span,
 +                    "usage of `mem::size_of::<T>()` to obtain the size of `T` in bits",
 +                    "consider using",
++                    sugg,
++                    app,
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn get_one_size_of_ty<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr1: &'tcx Expr<'_>,
 +    expr2: &'tcx Expr<'_>,
 +) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>, &'tcx Expr<'tcx>)> {
 +    match (get_size_of_ty(cx, expr1), get_size_of_ty(cx, expr2)) {
 +        (Some((real_ty, resolved_ty)), None) => Some((real_ty, resolved_ty, expr2)),
 +        (None, Some((real_ty, resolved_ty))) => Some((real_ty, resolved_ty, expr1)),
 +        _ => None,
 +    }
 +}
 +
 +fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> {
 +    if_chain! {
 +        if let ExprKind::Call(count_func, _func_args) = expr.kind;
 +        if let ExprKind::Path(ref count_func_qpath) = count_func.kind;
 +
 +        if let QPath::Resolved(_, count_func_path) = count_func_qpath;
 +        if let Some(segment_zero) = count_func_path.segments.get(0);
 +        if let Some(args) = segment_zero.args;
 +        if let Some(GenericArg::Type(real_ty)) = args.args.get(0);
 +
 +        if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
 +        if cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id);
 +        then {
 +            cx.typeck_results().node_substs(count_func.hir_id).types().next().map(|resolved_ty| (real_ty, resolved_ty))
 +        } else {
 +            None
 +        }
 +    }
 +}
++
++fn create_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, base_sugg: String) -> String {
++    if let Some(parent_expr) = get_parent_expr(cx, expr) {
++        if is_ty_conversion(parent_expr) {
++            return base_sugg;
++        }
++
++        // These expressions have precedence over casts, the suggestion therefore
++        // needs to be wrapped into parentheses
++        match parent_expr.kind {
++            ExprKind::Unary(..) | ExprKind::AddrOf(..) | ExprKind::MethodCall(..) => {
++                return format!("({base_sugg} as usize)");
++            },
++            _ => {},
++        }
++    }
++
++    format!("{base_sugg} as usize")
++}
++
++fn is_ty_conversion(expr: &Expr<'_>) -> bool {
++    if let ExprKind::Cast(..) = expr.kind {
++        true
++    } else if let ExprKind::MethodCall(path, [_], _) = expr.kind
++        && path.ident.name == rustc_span::sym::try_into
++    {
++        // This is only called for `usize` which implements `TryInto`. Therefore,
++        // we don't have to check here if `self` implements the `TryInto` trait.
++        true
++    } else {
++        false
++    }
++}
index 33d1bb2985f43fdcd4dcd525abf8d0fb16082c78,0000000000000000000000000000000000000000..b8d620d81713046ac5892d9c6239cb72157d240f
mode 100644,000000..100644
--- /dev/null
@@@ -1,186 -1,0 +1,221 @@@
- use clippy_utils::{meets_msrv, msrvs};
- use if_chain::if_chain;
- use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind};
 +use clippy_utils::attrs::is_doc_hidden;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet_opt;
- use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
++use clippy_utils::{is_lint_allowed, meets_msrv, msrvs};
++use rustc_ast::ast::{self, VisibilityKind};
++use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::Applicability;
- #[derive(Clone)]
- pub struct ManualNonExhaustive {
++use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
++use rustc_hir::{self as hir, Expr, ExprKind, QPath};
++use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
++use rustc_middle::ty::DefIdTree;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::def_id::{DefId, LocalDefId};
 +use rustc_span::{sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for manual implementations of the non-exhaustive pattern.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the #[non_exhaustive] attribute expresses better the intent
 +    /// and allows possible optimizations when applied to enums.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct S {
 +    ///     pub a: i32,
 +    ///     pub b: i32,
 +    ///     _c: (),
 +    /// }
 +    ///
 +    /// enum E {
 +    ///     A,
 +    ///     B,
 +    ///     #[doc(hidden)]
 +    ///     _C,
 +    /// }
 +    ///
 +    /// struct T(pub i32, pub i32, ());
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// #[non_exhaustive]
 +    /// struct S {
 +    ///     pub a: i32,
 +    ///     pub b: i32,
 +    /// }
 +    ///
 +    /// #[non_exhaustive]
 +    /// enum E {
 +    ///     A,
 +    ///     B,
 +    /// }
 +    ///
 +    /// #[non_exhaustive]
 +    /// struct T(pub i32, pub i32);
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub MANUAL_NON_EXHAUSTIVE,
 +    style,
 +    "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
 +}
 +
- impl ManualNonExhaustive {
++#[allow(clippy::module_name_repetitions)]
++pub struct ManualNonExhaustiveStruct {
 +    msrv: Option<RustcVersion>,
 +}
 +
- impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
++impl ManualNonExhaustiveStruct {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
- impl EarlyLintPass for ManualNonExhaustive {
-     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
-         if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
-             return;
-         }
++impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]);
 +
-         match &item.kind {
-             ItemKind::Enum(def, _) => {
-                 check_manual_non_exhaustive_enum(cx, item, &def.variants);
-             },
-             ItemKind::Struct(variant_data, _) => {
-                 if let VariantData::Unit(..) = variant_data {
-                     return;
-                 }
-                 check_manual_non_exhaustive_struct(cx, item, variant_data);
-             },
-             _ => {},
++#[allow(clippy::module_name_repetitions)]
++pub struct ManualNonExhaustiveEnum {
++    msrv: Option<RustcVersion>,
++    constructed_enum_variants: FxHashSet<(DefId, DefId)>,
++    potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>,
++}
 +
-     extract_msrv_attr!(EarlyContext);
++impl ManualNonExhaustiveEnum {
++    #[must_use]
++    pub fn new(msrv: Option<RustcVersion>) -> Self {
++        Self {
++            msrv,
++            constructed_enum_variants: FxHashSet::default(),
++            potential_enums: Vec::new(),
 +        }
 +    }
- fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) {
-     fn is_non_exhaustive_marker(variant: &Variant) -> bool {
-         matches!(variant.data, VariantData::Unit(_))
-             && variant.ident.as_str().starts_with('_')
-             && is_doc_hidden(&variant.attrs)
-     }
 +}
 +
-     let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v));
-     if_chain! {
-         if let Some(marker) = markers.next();
-         if markers.count() == 0 && variants.len() > 1;
-         then {
-             span_lint_and_then(
-                 cx,
-                 MANUAL_NON_EXHAUSTIVE,
-                 item.span,
-                 "this seems like a manual implementation of the non-exhaustive pattern",
-                 |diag| {
-                     if_chain! {
-                         if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive));
-                         let header_span = cx.sess().source_map().span_until_char(item.span, '{');
-                         if let Some(snippet) = snippet_opt(cx, header_span);
-                         then {
++impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]);
 +
-                     diag.span_help(marker.span, "remove this variant");
-                 });
++impl EarlyLintPass for ManualNonExhaustiveStruct {
++    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
++        if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
++            return;
++        }
++
++        if let ast::ItemKind::Struct(variant_data, _) = &item.kind {
++            let (fields, delimiter) = match variant_data {
++                ast::VariantData::Struct(fields, _) => (&**fields, '{'),
++                ast::VariantData::Tuple(fields, _) => (&**fields, '('),
++                ast::VariantData::Unit(_) => return,
++            };
++            if fields.len() <= 1 {
++                return;
++            }
++            let mut iter = fields.iter().filter_map(|f| match f.vis.kind {
++                VisibilityKind::Public => None,
++                VisibilityKind::Inherited => Some(Ok(f)),
++                _ => Some(Err(())),
++            });
++            if let Some(Ok(field)) = iter.next()
++                && iter.next().is_none()
++                && field.ty.kind.is_unit()
++                && field.ident.map_or(true, |name| name.as_str().starts_with('_'))
++            {
++                span_lint_and_then(
++                    cx,
++                    MANUAL_NON_EXHAUSTIVE,
++                    item.span,
++                    "this seems like a manual implementation of the non-exhaustive pattern",
++                    |diag| {
++                        if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive))
++                            && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter)
++                            && let Some(snippet) = snippet_opt(cx, header_span)
++                        {
 +                            diag.span_suggestion(
 +                                header_span,
 +                                "add the attribute",
 +                                format!("#[non_exhaustive] {}", snippet),
 +                                Applicability::Unspecified,
 +                            );
 +                        }
++                        diag.span_help(field.span, "remove this field");
 +                    }
- fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) {
-     fn is_private(field: &FieldDef) -> bool {
-         matches!(field.vis.kind, VisibilityKind::Inherited)
-     }
++                );
++            }
 +        }
 +    }
++
++    extract_msrv_attr!(EarlyContext);
 +}
 +
-     fn is_non_exhaustive_marker(field: &FieldDef) -> bool {
-         is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_'))
++impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
++    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
++        if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
++            return;
++        }
 +
-     fn find_header_span(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) -> Span {
-         let delimiter = match data {
-             VariantData::Struct(..) => '{',
-             VariantData::Tuple(..) => '(',
-             VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"),
-         };
++        if let hir::ItemKind::Enum(def, _) = &item.kind
++            && def.variants.len() > 1
++        {
++            let mut iter = def.variants.iter().filter_map(|v| {
++                let id = cx.tcx.hir().local_def_id(v.id);
++                (matches!(v.data, hir::VariantData::Unit(_))
++                    && v.ident.as_str().starts_with('_')
++                    && is_doc_hidden(cx.tcx.get_attrs(id.to_def_id())))
++                .then(|| (id, v.span))
++            });
++            if let Some((id, span)) = iter.next()
++                && iter.next().is_none()
++            {
++                self.potential_enums.push((item.def_id, id, item.span, span));
++            }
++        }
 +    }
 +
-         cx.sess().source_map().span_until_char(item.span, delimiter)
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
++        if let ExprKind::Path(QPath::Resolved(None, p)) = &e.kind
++            && let [.., name] = p.segments
++            && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res
++            && name.ident.as_str().starts_with('_')
++        {
++            let variant_id = cx.tcx.parent(id);
++            let enum_id = cx.tcx.parent(variant_id);
 +
-     let fields = data.fields();
-     let private_fields = fields.iter().filter(|f| is_private(f)).count();
-     let public_fields = fields.iter().filter(|f| f.vis.kind.is_pub()).count();
-     if_chain! {
-         if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1;
-         if let Some(marker) = fields.iter().find(|f| is_non_exhaustive_marker(f));
-         then {
++            self.constructed_enum_variants.insert((enum_id, variant_id));
++        }
 +    }
 +
-                 item.span,
++    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
++        for &(enum_id, _, enum_span, variant_span) in
++            self.potential_enums.iter().filter(|&&(enum_id, variant_id, _, _)| {
++                !self
++                    .constructed_enum_variants
++                    .contains(&(enum_id.to_def_id(), variant_id.to_def_id()))
++                    && !is_lint_allowed(cx, MANUAL_NON_EXHAUSTIVE, cx.tcx.hir().local_def_id_to_hir_id(enum_id))
++            })
++        {
 +            span_lint_and_then(
 +                cx,
 +                MANUAL_NON_EXHAUSTIVE,
-                     if_chain! {
-                         if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive));
-                         let header_span = find_header_span(cx, item, data);
-                         if let Some(snippet) = snippet_opt(cx, header_span);
-                         then {
++                enum_span,
 +                "this seems like a manual implementation of the non-exhaustive pattern",
 +                |diag| {
-                         }
++                    if !cx.tcx.adt_def(enum_id).is_variant_list_non_exhaustive()
++                        && let header_span = cx.sess().source_map().span_until_char(enum_span, '{')
++                        && let Some(snippet) = snippet_opt(cx, header_span)
++                    {
 +                            diag.span_suggestion(
 +                                header_span,
 +                                "add the attribute",
 +                                format!("#[non_exhaustive] {}", snippet),
 +                                Applicability::Unspecified,
 +                            );
-                     diag.span_help(marker.span, "remove this field");
-                 });
 +                    }
++                    diag.span_help(variant_span, "remove this variant");
++                },
++            );
 +        }
 +    }
++
++    extract_msrv_attr!(LateContext);
 +}
index e233300e26ab898e94efcdbd13b5bee37b6dbcf7,0000000000000000000000000000000000000000..ceb66947d02c6717a0ba802f61268997c00e48f0
mode 100644,000000..100644
--- /dev/null
@@@ -1,171 -1,0 +1,167 @@@
-         let message = if is_copy {
-             "you are using an explicit closure for copying elements"
-         } else {
-             "you are using an explicit closure for cloning elements"
-         };
-         let sugg_method = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) {
-             "copied"
 +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_trait_method, meets_msrv, msrvs, peel_blocks};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::mir::Mutability;
 +use rustc_middle::ty;
 +use rustc_middle::ty::adjustment::Adjust;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::Ident;
 +use rustc_span::{sym, Span};
 +
 +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"
 +}
 +
 +pub struct MapClone {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl_lint_pass!(MapClone => [MAP_CLONE]);
 +
 +impl MapClone {
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for MapClone {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
 +        if e.span.from_expansion() {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let hir::ExprKind::MethodCall(method, args, _) = e.kind;
 +            if args.len() == 2;
 +            if method.ident.name == sym::map;
 +            let ty = cx.typeck_results().expr_ty(&args[0]);
 +            if is_type_diagnostic_item(cx, ty, sym::Option) || is_trait_method(cx, e, sym::Iterator);
 +            if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind;
 +            then {
 +                let closure_body = cx.tcx.hir().body(body_id);
 +                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) {
 +                            self.lint_explicit_closure(cx, e.span, args[0].span, true);
 +                        }
 +                    },
 +                    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() {
 +                                        self.lint_explicit_closure(cx, e.span, args[0].span, true);
 +                                    }
 +                                }
 +                            },
 +                            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);
 +                                            self.lint_explicit_closure(cx, e.span, args[0].span, copy);
 +                                        }
 +                                    } else {
 +                                        lint_needless_cloning(cx, e.span, args[0].span);
 +                                    }
 +                                }
 +                            },
 +                            _ => {},
 +                        }
 +                    },
 +                    _ => {},
 +                }
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +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,
 +    );
 +}
 +
 +impl MapClone {
 +    fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) {
 +        let mut applicability = Applicability::MachineApplicable;
-             "cloned"
++
++        let (message, sugg_method) = if is_copy && meets_msrv(self.msrv.as_ref(), &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 77a4917ec58f0a80afd66d1d8e4e8841a94ad96b,0000000000000000000000000000000000000000..3349b85f1347a396519aeb5cba96c59813c93cec
mode 100644,000000..100644
--- /dev/null
@@@ -1,90 -1,0 +1,90 @@@
-     /// if let Some(valie) = iter.next().ok() {
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::method_chain_args;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, PatKind, QPath};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary `ok()` in `while let`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `ok()` in `while let` is unnecessary, instead match
 +    /// on `Ok(pat)`
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// while let Some(value) = iter.next().ok() {
 +    ///     vec.push(value)
 +    /// }
 +    ///
-             if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
++    /// if let Some(value) = iter.next().ok() {
 +    ///     vec.push(value)
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```ignore
 +    /// while let Ok(value) = iter.next() {
 +    ///     vec.push(value)
 +    /// }
 +    ///
 +    /// if let Ok(value) = iter.next() {
 +    ///        vec.push(value)
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.57.0"]
 +    pub MATCH_RESULT_OK,
 +    style,
 +    "usage of `ok()` in `let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
 +}
 +
 +declare_lint_pass!(MatchResultOk => [MATCH_RESULT_OK]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        let (let_pat, let_expr, ifwhile) =
 +            if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) {
 +                (let_pat, let_expr, "if")
 +            } else if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
 +                (let_pat, let_expr, "while")
 +            } else {
 +                return;
 +            };
 +
 +        if_chain! {
 +            if let ExprKind::MethodCall(ok_path, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
 +            if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _)  = let_pat.kind; //get operation
++            if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() method use std::marker::Sized;
 +            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result);
 +            if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
 +
 +            then {
 +
 +                let mut applicability = Applicability::MachineApplicable;
 +                let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
 +                let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_path.ident.span), "", &mut applicability);
 +                let sugg = format!(
 +                    "{} let Ok({}) = {}",
 +                    ifwhile,
 +                    some_expr_string,
 +                    trimmed_ok.trim().trim_end_matches('.'),
 +                );
 +                span_lint_and_sugg(
 +                    cx,
 +                    MATCH_RESULT_OK,
 +                    expr.span.with_hi(let_expr.span.hi()),
 +                    "matching on `Some` with `ok()` is redundant",
 +                    &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),
 +                    sugg,
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2472acb6f6e8b6f974fcf898010e45140bc021ff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs};
++use rustc_errors::Applicability;
++use rustc_hir::{ExprKind, Local, MatchSource, PatKind, QPath};
++use rustc_lint::LateContext;
++
++use super::INFALLIBLE_DESTRUCTURING_MATCH;
++
++pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool {
++    if_chain! {
++        if !local.span.from_expansion();
++        if let Some(expr) = local.init;
++        if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
++        if arms.len() == 1 && arms[0].guard.is_none();
++        if let PatKind::TupleStruct(
++            QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
++        if args.len() == 1;
++        if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
++        let body = peel_blocks(arms[0].body);
++        if path_to_local_id(body, arg);
++
++        then {
++            let mut applicability = Applicability::MachineApplicable;
++            span_lint_and_sugg(
++                cx,
++                INFALLIBLE_DESTRUCTURING_MATCH,
++                local.span,
++                "you seem to be trying to use `match` to destructure a single infallible pattern. \
++                Consider using `let`",
++                "try this",
++                format!(
++                    "let {}({}) = {};",
++                    snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
++                    snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
++                    snippet_with_applicability(cx, target.span, "..", &mut applicability),
++                ),
++                applicability,
++            );
++            return true;
++        }
++    }
++    false
++}
index b8591fe0db0ad31a3e94fc030f69310a3f137f7f,0000000000000000000000000000000000000000..9b7344fb8b0b21a93d9f5a69a9d698921e6c0771
mode 100644,000000..100644
--- /dev/null
@@@ -1,419 -1,0 +1,419 @@@
-     // The furthast forwards a pattern can move without semantic changes
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet;
 +use clippy_utils::{path_to_local, search_same, SpanlessEq, SpanlessHash};
 +use core::cmp::Ordering;
 +use core::iter;
 +use core::slice;
 +use rustc_arena::DroplessArena;
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdSet, Pat, PatKind, RangeEnd};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +use rustc_span::Symbol;
 +use std::collections::hash_map::Entry;
 +
 +use super::MATCH_SAME_ARMS;
 +
 +#[allow(clippy::too_many_lines)]
 +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
 +    let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
 +        let mut h = SpanlessHash::new(cx);
 +        h.hash_expr(arm.body);
 +        h.finish()
 +    };
 +
 +    let arena = DroplessArena::default();
 +    let normalized_pats: Vec<_> = arms
 +        .iter()
 +        .map(|a| NormalizedPat::from_pat(cx, &arena, a.pat))
 +        .collect();
 +
-     // The furthast backwards a pattern can move without semantic changes
++    // The furthest forwards a pattern can move without semantic changes
 +    let forwards_blocking_idxs: Vec<_> = normalized_pats
 +        .iter()
 +        .enumerate()
 +        .map(|(i, pat)| {
 +            normalized_pats[i + 1..]
 +                .iter()
 +                .enumerate()
 +                .find_map(|(j, other)| pat.has_overlapping_values(other).then(|| i + 1 + j))
 +                .unwrap_or(normalized_pats.len())
 +        })
 +        .collect();
 +
++    // The furthest backwards a pattern can move without semantic changes
 +    let backwards_blocking_idxs: Vec<_> = normalized_pats
 +        .iter()
 +        .enumerate()
 +        .map(|(i, pat)| {
 +            normalized_pats[..i]
 +                .iter()
 +                .enumerate()
 +                .rev()
 +                .zip(forwards_blocking_idxs[..i].iter().copied().rev())
 +                .skip_while(|&(_, forward_block)| forward_block > i)
 +                .find_map(|((j, other), forward_block)| {
 +                    (forward_block == i || pat.has_overlapping_values(other)).then(|| j)
 +                })
 +                .unwrap_or(0)
 +        })
 +        .collect();
 +
 +    let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
 +        let min_index = usize::min(lindex, rindex);
 +        let max_index = usize::max(lindex, rindex);
 +
 +        let mut local_map: HirIdMap<HirId> = HirIdMap::default();
 +        let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
 +            if_chain! {
 +                if let Some(a_id) = path_to_local(a);
 +                if let Some(b_id) = path_to_local(b);
 +                let entry = match local_map.entry(a_id) {
 +                    Entry::Vacant(entry) => entry,
 +                    // check if using the same bindings as before
 +                    Entry::Occupied(entry) => return *entry.get() == b_id,
 +                };
 +                // the names technically don't have to match; this makes the lint more conservative
 +                if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id);
 +                if cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b);
 +                if pat_contains_local(lhs.pat, a_id);
 +                if pat_contains_local(rhs.pat, b_id);
 +                then {
 +                    entry.insert(b_id);
 +                    true
 +                } else {
 +                    false
 +                }
 +            }
 +        };
 +        // Arms with a guard are ignored, those can’t always be merged together
 +        // If both arms overlap with an arm in between then these can't be merged either.
 +        !(backwards_blocking_idxs[max_index] > min_index && forwards_blocking_idxs[min_index] < max_index)
 +                && lhs.guard.is_none()
 +                && rhs.guard.is_none()
 +                && SpanlessEq::new(cx)
 +                    .expr_fallback(eq_fallback)
 +                    .eq_expr(lhs.body, rhs.body)
 +                // these checks could be removed to allow unused bindings
 +                && bindings_eq(lhs.pat, local_map.keys().copied().collect())
 +                && bindings_eq(rhs.pat, local_map.values().copied().collect())
 +    };
 +
 +    let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
 +    for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) {
 +        if matches!(arm2.pat.kind, PatKind::Wild) {
 +            span_lint_and_then(
 +                cx,
 +                MATCH_SAME_ARMS,
 +                arm1.span,
 +                "this match arm has an identical body to the `_` wildcard arm",
 +                |diag| {
 +                    diag.span_suggestion(
 +                        arm1.span,
 +                        "try removing the arm",
 +                        String::new(),
 +                        Applicability::MaybeIncorrect,
 +                    )
 +                    .help("or try changing either arm body")
 +                    .span_note(arm2.span, "`_` wildcard arm here");
 +                },
 +            );
 +        } else {
 +            let back_block = backwards_blocking_idxs[j];
 +            let (keep_arm, move_arm) = if back_block < i || (back_block == 0 && forwards_blocking_idxs[i] <= j) {
 +                (arm1, arm2)
 +            } else {
 +                (arm2, arm1)
 +            };
 +
 +            span_lint_and_then(
 +                cx,
 +                MATCH_SAME_ARMS,
 +                keep_arm.span,
 +                "this match arm has an identical body to another arm",
 +                |diag| {
 +                    let move_pat_snip = snippet(cx, move_arm.pat.span, "<pat2>");
 +                    let keep_pat_snip = snippet(cx, keep_arm.pat.span, "<pat1>");
 +
 +                    diag.span_suggestion(
 +                        keep_arm.pat.span,
 +                        "try merging the arm patterns",
 +                        format!("{} | {}", keep_pat_snip, move_pat_snip),
 +                        Applicability::MaybeIncorrect,
 +                    )
 +                    .help("or try changing either arm body")
 +                    .span_note(move_arm.span, "other arm here");
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +enum NormalizedPat<'a> {
 +    Wild,
 +    Struct(Option<DefId>, &'a [(Symbol, Self)]),
 +    Tuple(Option<DefId>, &'a [Self]),
 +    Or(&'a [Self]),
 +    Path(Option<DefId>),
 +    LitStr(Symbol),
 +    LitBytes(&'a [u8]),
 +    LitInt(u128),
 +    LitBool(bool),
 +    Range(PatRange),
 +    /// A slice pattern. If the second value is `None`, then this matches an exact size. Otherwise
 +    /// the first value contains everything before the `..` wildcard pattern, and the second value
 +    /// contains everything afterwards. Note that either side, or both sides, may contain zero
 +    /// patterns.
 +    Slice(&'a [Self], Option<&'a [Self]>),
 +}
 +
 +#[derive(Clone, Copy)]
 +struct PatRange {
 +    start: u128,
 +    end: u128,
 +    bounds: RangeEnd,
 +}
 +impl PatRange {
 +    fn contains(&self, x: u128) -> bool {
 +        x >= self.start
 +            && match self.bounds {
 +                RangeEnd::Included => x <= self.end,
 +                RangeEnd::Excluded => x < self.end,
 +            }
 +    }
 +
 +    fn overlaps(&self, other: &Self) -> bool {
 +        // Note: Empty ranges are impossible, so this is correct even though it would return true if an
 +        // empty exclusive range were to reside within an inclusive range.
 +        (match self.bounds {
 +            RangeEnd::Included => self.end >= other.start,
 +            RangeEnd::Excluded => self.end > other.start,
 +        } && match other.bounds {
 +            RangeEnd::Included => self.start <= other.end,
 +            RangeEnd::Excluded => self.start < other.end,
 +        })
 +    }
 +}
 +
 +/// Iterates over the pairs of fields with matching names.
 +fn iter_matching_struct_fields<'a>(
 +    left: &'a [(Symbol, NormalizedPat<'a>)],
 +    right: &'a [(Symbol, NormalizedPat<'a>)],
 +) -> impl Iterator<Item = (&'a NormalizedPat<'a>, &'a NormalizedPat<'a>)> + 'a {
 +    struct Iter<'a>(
 +        slice::Iter<'a, (Symbol, NormalizedPat<'a>)>,
 +        slice::Iter<'a, (Symbol, NormalizedPat<'a>)>,
 +    );
 +    impl<'a> Iterator for Iter<'a> {
 +        type Item = (&'a NormalizedPat<'a>, &'a NormalizedPat<'a>);
 +        fn next(&mut self) -> Option<Self::Item> {
 +            // Note: all the fields in each slice are sorted by symbol value.
 +            let mut left = self.0.next()?;
 +            let mut right = self.1.next()?;
 +            loop {
 +                match left.0.cmp(&right.0) {
 +                    Ordering::Equal => return Some((&left.1, &right.1)),
 +                    Ordering::Less => left = self.0.next()?,
 +                    Ordering::Greater => right = self.1.next()?,
 +                }
 +            }
 +        }
 +    }
 +    Iter(left.iter(), right.iter())
 +}
 +
 +#[allow(clippy::similar_names)]
 +impl<'a> NormalizedPat<'a> {
 +    #[allow(clippy::too_many_lines)]
 +    fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self {
 +        match pat.kind {
 +            PatKind::Wild | PatKind::Binding(.., None) => Self::Wild,
 +            PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Ref(pat, _) => {
 +                Self::from_pat(cx, arena, pat)
 +            },
 +            PatKind::Struct(ref path, fields, _) => {
 +                let fields =
 +                    arena.alloc_from_iter(fields.iter().map(|f| (f.ident.name, Self::from_pat(cx, arena, f.pat))));
 +                fields.sort_by_key(|&(name, _)| name);
 +                Self::Struct(cx.qpath_res(path, pat.hir_id).opt_def_id(), fields)
 +            },
 +            PatKind::TupleStruct(ref path, pats, wild_idx) => {
 +                let adt = match cx.typeck_results().pat_ty(pat).ty_adt_def() {
 +                    Some(x) => x,
 +                    None => return Self::Wild,
 +                };
 +                let (var_id, variant) = if adt.is_enum() {
 +                    match cx.qpath_res(path, pat.hir_id).opt_def_id() {
 +                        Some(x) => (Some(x), adt.variant_with_ctor_id(x)),
 +                        None => return Self::Wild,
 +                    }
 +                } else {
 +                    (None, adt.non_enum_variant())
 +                };
 +                let (front, back) = match wild_idx {
 +                    Some(i) => pats.split_at(i),
 +                    None => (pats, [].as_slice()),
 +                };
 +                let pats = arena.alloc_from_iter(
 +                    front
 +                        .iter()
 +                        .map(|pat| Self::from_pat(cx, arena, pat))
 +                        .chain(iter::repeat_with(|| Self::Wild).take(variant.fields.len() - pats.len()))
 +                        .chain(back.iter().map(|pat| Self::from_pat(cx, arena, pat))),
 +                );
 +                Self::Tuple(var_id, pats)
 +            },
 +            PatKind::Or(pats) => Self::Or(arena.alloc_from_iter(pats.iter().map(|pat| Self::from_pat(cx, arena, pat)))),
 +            PatKind::Path(ref path) => Self::Path(cx.qpath_res(path, pat.hir_id).opt_def_id()),
 +            PatKind::Tuple(pats, wild_idx) => {
 +                let field_count = match cx.typeck_results().pat_ty(pat).kind() {
 +                    ty::Tuple(subs) => subs.len(),
 +                    _ => return Self::Wild,
 +                };
 +                let (front, back) = match wild_idx {
 +                    Some(i) => pats.split_at(i),
 +                    None => (pats, [].as_slice()),
 +                };
 +                let pats = arena.alloc_from_iter(
 +                    front
 +                        .iter()
 +                        .map(|pat| Self::from_pat(cx, arena, pat))
 +                        .chain(iter::repeat_with(|| Self::Wild).take(field_count - pats.len()))
 +                        .chain(back.iter().map(|pat| Self::from_pat(cx, arena, pat))),
 +                );
 +                Self::Tuple(None, pats)
 +            },
 +            PatKind::Lit(e) => match &e.kind {
 +                // TODO: Handle negative integers. They're currently treated as a wild match.
 +                ExprKind::Lit(lit) => match lit.node {
 +                    LitKind::Str(sym, _) => Self::LitStr(sym),
 +                    LitKind::ByteStr(ref bytes) => Self::LitBytes(&**bytes),
 +                    LitKind::Byte(val) => Self::LitInt(val.into()),
 +                    LitKind::Char(val) => Self::LitInt(val.into()),
 +                    LitKind::Int(val, _) => Self::LitInt(val),
 +                    LitKind::Bool(val) => Self::LitBool(val),
 +                    LitKind::Float(..) | LitKind::Err(_) => Self::Wild,
 +                },
 +                _ => Self::Wild,
 +            },
 +            PatKind::Range(start, end, bounds) => {
 +                // TODO: Handle negative integers. They're currently treated as a wild match.
 +                let start = match start {
 +                    None => 0,
 +                    Some(e) => match &e.kind {
 +                        ExprKind::Lit(lit) => match lit.node {
 +                            LitKind::Int(val, _) => val,
 +                            LitKind::Char(val) => val.into(),
 +                            LitKind::Byte(val) => val.into(),
 +                            _ => return Self::Wild,
 +                        },
 +                        _ => return Self::Wild,
 +                    },
 +                };
 +                let (end, bounds) = match end {
 +                    None => (u128::MAX, RangeEnd::Included),
 +                    Some(e) => match &e.kind {
 +                        ExprKind::Lit(lit) => match lit.node {
 +                            LitKind::Int(val, _) => (val, bounds),
 +                            LitKind::Char(val) => (val.into(), bounds),
 +                            LitKind::Byte(val) => (val.into(), bounds),
 +                            _ => return Self::Wild,
 +                        },
 +                        _ => return Self::Wild,
 +                    },
 +                };
 +                Self::Range(PatRange { start, end, bounds })
 +            },
 +            PatKind::Slice(front, wild_pat, back) => Self::Slice(
 +                arena.alloc_from_iter(front.iter().map(|pat| Self::from_pat(cx, arena, pat))),
 +                wild_pat.map(|_| &*arena.alloc_from_iter(back.iter().map(|pat| Self::from_pat(cx, arena, pat)))),
 +            ),
 +        }
 +    }
 +
 +    /// Checks if two patterns overlap in the values they can match assuming they are for the same
 +    /// type.
 +    fn has_overlapping_values(&self, other: &Self) -> bool {
 +        match (*self, *other) {
 +            (Self::Wild, _) | (_, Self::Wild) => true,
 +            (Self::Or(pats), ref other) | (ref other, Self::Or(pats)) => {
 +                pats.iter().any(|pat| pat.has_overlapping_values(other))
 +            },
 +            (Self::Struct(lpath, lfields), Self::Struct(rpath, rfields)) => {
 +                if lpath != rpath {
 +                    return false;
 +                }
 +                iter_matching_struct_fields(lfields, rfields).all(|(lpat, rpat)| lpat.has_overlapping_values(rpat))
 +            },
 +            (Self::Tuple(lpath, lpats), Self::Tuple(rpath, rpats)) => {
 +                if lpath != rpath {
 +                    return false;
 +                }
 +                lpats
 +                    .iter()
 +                    .zip(rpats.iter())
 +                    .all(|(lpat, rpat)| lpat.has_overlapping_values(rpat))
 +            },
 +            (Self::Path(x), Self::Path(y)) => x == y,
 +            (Self::LitStr(x), Self::LitStr(y)) => x == y,
 +            (Self::LitBytes(x), Self::LitBytes(y)) => x == y,
 +            (Self::LitInt(x), Self::LitInt(y)) => x == y,
 +            (Self::LitBool(x), Self::LitBool(y)) => x == y,
 +            (Self::Range(ref x), Self::Range(ref y)) => x.overlaps(y),
 +            (Self::Range(ref range), Self::LitInt(x)) | (Self::LitInt(x), Self::Range(ref range)) => range.contains(x),
 +            (Self::Slice(lpats, None), Self::Slice(rpats, None)) => {
 +                lpats.len() == rpats.len() && lpats.iter().zip(rpats.iter()).all(|(x, y)| x.has_overlapping_values(y))
 +            },
 +            (Self::Slice(pats, None), Self::Slice(front, Some(back)))
 +            | (Self::Slice(front, Some(back)), Self::Slice(pats, None)) => {
 +                // Here `pats` is an exact size match. If the combined lengths of `front` and `back` are greater
 +                // then the minium length required will be greater than the length of `pats`.
 +                if pats.len() < front.len() + back.len() {
 +                    return false;
 +                }
 +                pats[..front.len()]
 +                    .iter()
 +                    .zip(front.iter())
 +                    .chain(pats[pats.len() - back.len()..].iter().zip(back.iter()))
 +                    .all(|(x, y)| x.has_overlapping_values(y))
 +            },
 +            (Self::Slice(lfront, Some(lback)), Self::Slice(rfront, Some(rback))) => lfront
 +                .iter()
 +                .zip(rfront.iter())
 +                .chain(lback.iter().rev().zip(rback.iter().rev()))
 +                .all(|(x, y)| x.has_overlapping_values(y)),
 +
 +            // Enums can mix unit variants with tuple/struct variants. These can never overlap.
 +            (Self::Path(_), Self::Tuple(..) | Self::Struct(..))
 +            | (Self::Tuple(..) | Self::Struct(..), Self::Path(_)) => false,
 +
 +            // Tuples can be matched like a struct.
 +            (Self::Tuple(x, _), Self::Struct(y, _)) | (Self::Struct(x, _), Self::Tuple(y, _)) => {
 +                // TODO: check fields here.
 +                x == y
 +            },
 +
 +            // TODO: Lit* with Path, Range with Path, LitBytes with Slice
 +            _ => true,
 +        }
 +    }
 +}
 +
 +fn pat_contains_local(pat: &Pat<'_>, id: HirId) -> bool {
 +    let mut result = false;
 +    pat.walk_short(|p| {
 +        result |= matches!(p.kind, PatKind::Binding(_, binding_id, ..) if binding_id == id);
 +        !result
 +    });
 +    result
 +}
 +
 +/// Returns true if all the bindings in the `Pat` are in `ids` and vice versa
 +fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool {
 +    let mut result = true;
 +    pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.remove(&id));
 +    result && ids.is_empty()
 +}
index e93b494653fc05923feecace84790bb7606d6122,0000000000000000000000000000000000000000..401ecef460c35cefd52f225576b5ddbd73b021ce
mode 100644,000000..100644
--- /dev/null
@@@ -1,793 -1,0 +1,796 @@@
- use clippy_utils::source::{snippet_opt, walk_span_to_context};
++use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context};
 +use clippy_utils::{meets_msrv, msrvs};
 +use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
 +use rustc_lexer::{tokenize, TokenKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{Span, SpanData, SyntaxContext};
 +
- mod infalliable_detructuring_match;
++mod infallible_destructuring_match;
 +mod match_as_ref;
 +mod match_bool;
 +mod match_like_matches;
 +mod match_ref_pats;
 +mod match_same_arms;
 +mod match_single_binding;
 +mod match_wild_enum;
 +mod match_wild_err_arm;
 +mod needless_match;
 +mod overlapping_arms;
 +mod redundant_pattern_match;
 +mod rest_pat_in_fully_bound_struct;
 +mod single_match;
 +mod wild_in_or_pats;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches with a single arm where an `if let`
 +    /// will usually suffice.
 +    ///
 +    /// ### Why is this bad?
 +    /// Just readability – `if let` nests less than a `match`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn bar(stool: &str) {}
 +    /// # let x = Some("abc");
 +    /// // Bad
 +    /// match x {
 +    ///     Some(ref foo) => bar(foo),
 +    ///     _ => (),
 +    /// }
 +    ///
 +    /// // Good
 +    /// if let Some(ref foo) = x {
 +    ///     bar(foo);
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SINGLE_MATCH,
 +    style,
 +    "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches with two arms where an `if let else` will
 +    /// usually suffice.
 +    ///
 +    /// ### Why is this bad?
 +    /// Just readability – `if let` nests less than a `match`.
 +    ///
 +    /// ### Known problems
 +    /// Personal style preferences may differ.
 +    ///
 +    /// ### Example
 +    /// Using `match`:
 +    ///
 +    /// ```rust
 +    /// # fn bar(foo: &usize) {}
 +    /// # let other_ref: usize = 1;
 +    /// # let x: Option<&usize> = Some(&1);
 +    /// match x {
 +    ///     Some(ref foo) => bar(foo),
 +    ///     _ => bar(&other_ref),
 +    /// }
 +    /// ```
 +    ///
 +    /// Using `if let` with `else`:
 +    ///
 +    /// ```rust
 +    /// # fn bar(foo: &usize) {}
 +    /// # let other_ref: usize = 1;
 +    /// # let x: Option<&usize> = Some(&1);
 +    /// if let Some(ref foo) = x {
 +    ///     bar(foo);
 +    /// } else {
 +    ///     bar(&other_ref);
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SINGLE_MATCH_ELSE,
 +    pedantic,
 +    "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches where all arms match a reference,
 +    /// suggesting to remove the reference and deref the matched expression
 +    /// instead. It also checks for `if let &foo = bar` blocks.
 +    ///
 +    /// ### Why is this bad?
 +    /// It just makes the code less readable. That reference
 +    /// destructuring adds nothing to the code.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// match x {
 +    ///     &A(ref y) => foo(y),
 +    ///     &B => bar(),
 +    ///     _ => frob(&x),
 +    /// }
 +    ///
 +    /// // Good
 +    /// match *x {
 +    ///     A(ref y) => foo(y),
 +    ///     B => bar(),
 +    ///     _ => frob(x),
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_REF_PATS,
 +    style,
 +    "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches where match expression is a `bool`. It
 +    /// suggests to replace the expression with an `if...else` block.
 +    ///
 +    /// ### Why is this bad?
 +    /// It makes the code less readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn foo() {}
 +    /// # fn bar() {}
 +    /// let condition: bool = true;
 +    /// match condition {
 +    ///     true => foo(),
 +    ///     false => bar(),
 +    /// }
 +    /// ```
 +    /// Use if/else instead:
 +    /// ```rust
 +    /// # fn foo() {}
 +    /// # fn bar() {}
 +    /// let condition: bool = true;
 +    /// if condition {
 +    ///     foo();
 +    /// } else {
 +    ///     bar();
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_BOOL,
 +    pedantic,
 +    "a `match` on a boolean expression instead of an `if..else` block"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for overlapping match arms.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is likely to be an error and if not, makes the code
 +    /// less obvious.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 5;
 +    /// match x {
 +    ///     1..=10 => println!("1 ... 10"),
 +    ///     5..=15 => println!("5 ... 15"),
 +    ///     _ => (),
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_OVERLAPPING_ARM,
 +    style,
 +    "a `match` with overlapping arms"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for arm which matches all errors with `Err(_)`
 +    /// and take drastic actions like `panic!`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is generally a bad practice, similar to
 +    /// catching all exceptions in java with `catch(Exception)`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: Result<i32, &str> = Ok(3);
 +    /// match x {
 +    ///     Ok(_) => println!("ok"),
 +    ///     Err(_) => panic!("err"),
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_WILD_ERR_ARM,
 +    pedantic,
 +    "a `match` with `Err(_)` arm and take drastic actions"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for match which is used to add a reference to an
 +    /// `Option` value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `as_ref()` or `as_mut()` instead is shorter.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: Option<()> = None;
 +    ///
 +    /// // Bad
 +    /// let r: Option<&()> = match x {
 +    ///     None => None,
 +    ///     Some(ref v) => Some(v),
 +    /// };
 +    ///
 +    /// // Good
 +    /// let r: Option<&()> = x.as_ref();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_AS_REF,
 +    complexity,
 +    "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for wildcard enum matches using `_`.
 +    ///
 +    /// ### Why is this bad?
 +    /// New enum variants added by library updates can be missed.
 +    ///
 +    /// ### Known problems
 +    /// Suggested replacements may be incorrect if guards exhaustively cover some
 +    /// variants, and also may not use correct path to enum if it's not present in the current scope.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # enum Foo { A(usize), B(usize) }
 +    /// # let x = Foo::B(1);
 +    /// // Bad
 +    /// match x {
 +    ///     Foo::A(_) => {},
 +    ///     _ => {},
 +    /// }
 +    ///
 +    /// // Good
 +    /// match x {
 +    ///     Foo::A(_) => {},
 +    ///     Foo::B(_) => {},
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.34.0"]
 +    pub WILDCARD_ENUM_MATCH_ARM,
 +    restriction,
 +    "a wildcard enum match arm using `_`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for wildcard enum matches for a single variant.
 +    ///
 +    /// ### Why is this bad?
 +    /// New enum variants added by library updates can be missed.
 +    ///
 +    /// ### Known problems
 +    /// Suggested replacements may not use correct path to enum
 +    /// if it's not present in the current scope.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # enum Foo { A, B, C }
 +    /// # let x = Foo::B;
 +    /// // Bad
 +    /// match x {
 +    ///     Foo::A => {},
 +    ///     Foo::B => {},
 +    ///     _ => {},
 +    /// }
 +    ///
 +    /// // Good
 +    /// match x {
 +    ///     Foo::A => {},
 +    ///     Foo::B => {},
 +    ///     Foo::C => {},
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +    pedantic,
 +    "a wildcard enum match for a single variant"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for wildcard pattern used with others patterns in same match arm.
 +    ///
 +    /// ### Why is this bad?
 +    /// Wildcard pattern already covers any other pattern as it will match anyway.
 +    /// It makes the code less readable, especially to spot wildcard pattern use in match arm.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// match "foo" {
 +    ///     "a" => {},
 +    ///     "bar" | _ => {},
 +    /// }
 +    ///
 +    /// // Good
 +    /// match "foo" {
 +    ///     "a" => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub WILDCARD_IN_OR_PATTERNS,
 +    complexity,
 +    "a wildcard pattern used with others patterns in same match arm"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches being used to destructure a single-variant enum
 +    /// or tuple struct where a `let` will suffice.
 +    ///
 +    /// ### Why is this bad?
 +    /// Just readability – `let` doesn't nest, whereas a `match` does.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// enum Wrapper {
 +    ///     Data(i32),
 +    /// }
 +    ///
 +    /// let wrapper = Wrapper::Data(42);
 +    ///
 +    /// let data = match wrapper {
 +    ///     Wrapper::Data(i) => i,
 +    /// };
 +    /// ```
 +    ///
 +    /// The correct use would be:
 +    /// ```rust
 +    /// enum Wrapper {
 +    ///     Data(i32),
 +    /// }
 +    ///
 +    /// let wrapper = Wrapper::Data(42);
 +    /// let Wrapper::Data(data) = wrapper;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub INFALLIBLE_DESTRUCTURING_MATCH,
 +    style,
 +    "a `match` statement with a single infallible arm instead of a `let`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for useless match that binds to only one value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability and needless complexity.
 +    ///
 +    /// ### Known problems
 +    ///  Suggested replacements may be incorrect when `match`
 +    /// is actually binding temporary value, bringing a 'dropped while borrowed' error.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let a = 1;
 +    /// # let b = 2;
 +    ///
 +    /// // Bad
 +    /// match (a, b) {
 +    ///     (c, d) => {
 +    ///         // useless match
 +    ///     }
 +    /// }
 +    ///
 +    /// // Good
 +    /// let (c, d) = (a, b);
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub MATCH_SINGLE_BINDING,
 +    complexity,
 +    "a match with a single binding instead of using `let` statement"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
 +    ///
 +    /// ### Why is this bad?
 +    /// Correctness and readability. It's like having a wildcard pattern after
 +    /// matching all enum variants explicitly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct A { a: i32 }
 +    /// let a = A { a: 5 };
 +    ///
 +    /// // Bad
 +    /// match a {
 +    ///     A { a: 5, .. } => {},
 +    ///     _ => {},
 +    /// }
 +    ///
 +    /// // Good
 +    /// match a {
 +    ///     A { a: 5 } => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +    restriction,
 +    "a match on a struct that binds all fields but still uses the wildcard pattern"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Lint for redundant pattern matching over `Result`, `Option`,
 +    /// `std::task::Poll` or `std::net::IpAddr`
 +    ///
 +    /// ### Why is this bad?
 +    /// It's more concise and clear to just use the proper
 +    /// utility function
 +    ///
 +    /// ### Known problems
 +    /// This will change the drop order for the matched type. Both `if let` and
 +    /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
 +    /// value before entering the block. For most types this change will not matter, but for a few
 +    /// types this will not be an acceptable change (e.g. locks). See the
 +    /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
 +    /// drop order.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::task::Poll;
 +    /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
 +    /// if let Ok(_) = Ok::<i32, i32>(42) {}
 +    /// if let Err(_) = Err::<i32, i32>(42) {}
 +    /// if let None = None::<()> {}
 +    /// if let Some(_) = Some(42) {}
 +    /// if let Poll::Pending = Poll::Pending::<()> {}
 +    /// if let Poll::Ready(_) = Poll::Ready(42) {}
 +    /// if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}
 +    /// if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}
 +    /// match Ok::<i32, i32>(42) {
 +    ///     Ok(_) => true,
 +    ///     Err(_) => false,
 +    /// };
 +    /// ```
 +    ///
 +    /// The more idiomatic use would be:
 +    ///
 +    /// ```rust
 +    /// # use std::task::Poll;
 +    /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
 +    /// if Ok::<i32, i32>(42).is_ok() {}
 +    /// if Err::<i32, i32>(42).is_err() {}
 +    /// if None::<()>.is_none() {}
 +    /// if Some(42).is_some() {}
 +    /// if Poll::Pending::<()>.is_pending() {}
 +    /// if Poll::Ready(42).is_ready() {}
 +    /// if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
 +    /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
 +    /// Ok::<i32, i32>(42).is_ok();
 +    /// ```
 +    #[clippy::version = "1.31.0"]
 +    pub REDUNDANT_PATTERN_MATCHING,
 +    style,
 +    "use the proper utility function avoiding an `if let`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `match`  or `if let` expressions producing a
 +    /// `bool` that could be written using `matches!`
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability and needless complexity.
 +    ///
 +    /// ### Known problems
 +    /// This lint falsely triggers, if there are arms with
 +    /// `cfg` attributes that remove an arm evaluating to `false`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = Some(5);
 +    ///
 +    /// // Bad
 +    /// let a = match x {
 +    ///     Some(0) => true,
 +    ///     _ => false,
 +    /// };
 +    ///
 +    /// let a = if let Some(0) = x {
 +    ///     true
 +    /// } else {
 +    ///     false
 +    /// };
 +    ///
 +    /// // Good
 +    /// let a = matches!(x, Some(0));
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub MATCH_LIKE_MATCHES_MACRO,
 +    style,
 +    "a match that could be written with the matches! macro"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `match` with identical arm bodies.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a copy & paste error. If arm bodies
 +    /// are the same on purpose, you can factor them
 +    /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
 +    ///
 +    /// ### Known problems
 +    /// False positive possible with order dependent `match`
 +    /// (see issue
 +    /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// match foo {
 +    ///     Bar => bar(),
 +    ///     Quz => quz(),
 +    ///     Baz => bar(), // <= oops
 +    /// }
 +    /// ```
 +    ///
 +    /// This should probably be
 +    /// ```rust,ignore
 +    /// match foo {
 +    ///     Bar => bar(),
 +    ///     Quz => quz(),
 +    ///     Baz => baz(), // <= fixed
 +    /// }
 +    /// ```
 +    ///
 +    /// or if the original code was not a typo:
 +    /// ```rust,ignore
 +    /// match foo {
 +    ///     Bar | Baz => bar(), // <= shows the intent better
 +    ///     Quz => quz(),
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_SAME_ARMS,
 +    pedantic,
 +    "`match` with identical arm bodies"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary `match` or match-like `if let` returns for `Option` and `Result`
 +    /// when function signatures are the same.
 +    ///
 +    /// ### Why is this bad?
 +    /// This `match` block does nothing and might not be what the coder intended.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// fn foo() -> Result<(), i32> {
 +    ///     match result {
 +    ///         Ok(val) => Ok(val),
 +    ///         Err(err) => Err(err),
 +    ///     }
 +    /// }
 +    ///
 +    /// fn bar() -> Option<i32> {
 +    ///     if let Some(val) = option {
 +    ///         Some(val)
 +    ///     } else {
 +    ///         None
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be replaced as
 +    ///
 +    /// ```rust,ignore
 +    /// fn foo() -> Result<(), i32> {
 +    ///     result
 +    /// }
 +    ///
 +    /// fn bar() -> Option<i32> {
 +    ///     option
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub NEEDLESS_MATCH,
 +    complexity,
 +    "`match` or match-like `if let` that are unnecessary"
 +}
 +
 +#[derive(Default)]
 +pub struct Matches {
 +    msrv: Option<RustcVersion>,
 +    infallible_destructuring_match_linted: bool,
 +}
 +
 +impl Matches {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self {
 +            msrv,
 +            ..Matches::default()
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(Matches => [
 +    SINGLE_MATCH,
 +    MATCH_REF_PATS,
 +    MATCH_BOOL,
 +    SINGLE_MATCH_ELSE,
 +    MATCH_OVERLAPPING_ARM,
 +    MATCH_WILD_ERR_ARM,
 +    MATCH_AS_REF,
 +    WILDCARD_ENUM_MATCH_ARM,
 +    MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +    WILDCARD_IN_OR_PATTERNS,
 +    MATCH_SINGLE_BINDING,
 +    INFALLIBLE_DESTRUCTURING_MATCH,
 +    REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +    REDUNDANT_PATTERN_MATCHING,
 +    MATCH_LIKE_MATCHES_MACRO,
 +    MATCH_SAME_ARMS,
 +    NEEDLESS_MATCH,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Matches {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let ExprKind::Match(ex, arms, source) = expr.kind {
++            if !span_starts_with(cx, expr.span, "match") {
++                return;
++            }
 +            if !contains_cfg_arm(cx, expr, ex, arms) {
 +                if source == MatchSource::Normal {
 +                    if !(meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO)
 +                        && match_like_matches::check_match(cx, expr, ex, arms))
 +                    {
 +                        match_same_arms::check(cx, arms);
 +                    }
 +
 +                    redundant_pattern_match::check_match(cx, expr, ex, arms);
 +                    single_match::check(cx, ex, arms, expr);
 +                    match_bool::check(cx, ex, arms, expr);
 +                    overlapping_arms::check(cx, ex, arms);
 +                    match_wild_enum::check(cx, ex, arms);
 +                    match_as_ref::check(cx, ex, arms, expr);
 +                    needless_match::check_match(cx, ex, arms, expr);
 +
 +                    if self.infallible_destructuring_match_linted {
 +                        self.infallible_destructuring_match_linted = false;
 +                    } else {
 +                        match_single_binding::check(cx, ex, arms, expr);
 +                    }
 +                }
 +                match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr);
 +            }
 +
 +            // These don't depend on a relationship between multiple arms
 +            match_wild_err_arm::check(cx, ex, arms);
 +            wild_in_or_pats::check(cx, arms);
 +        } else {
 +            if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
 +                match_like_matches::check(cx, expr);
 +            }
 +            redundant_pattern_match::check(cx, expr);
 +            needless_match::check(cx, expr);
 +        }
 +    }
 +
 +    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
-         self.infallible_destructuring_match_linted |= infalliable_detructuring_match::check(cx, local);
++        self.infallible_destructuring_match_linted |= infallible_destructuring_match::check(cx, local);
 +    }
 +
 +    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
 +        rest_pat_in_fully_bound_struct::check(cx, pat);
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +/// Checks if there are any arms with a `#[cfg(..)]` attribute.
 +fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
 +    let Some(scrutinee_span) = walk_span_to_context(scrutinee.span, SyntaxContext::root()) else {
 +        // Shouldn't happen, but treat this as though a `cfg` attribute were found
 +        return true;
 +    };
 +
 +    let start = scrutinee_span.hi();
 +    let mut arm_spans = arms.iter().map(|arm| {
 +        let data = arm.span.data();
 +        (data.ctxt == SyntaxContext::root()).then(|| (data.lo, data.hi))
 +    });
 +    let end = e.span.hi();
 +
 +    // Walk through all the non-code space before each match arm. The space trailing the final arm is
 +    // handled after the `try_fold` e.g.
 +    //
 +    // match foo {
 +    // _________^-                      everything between the scrutinee and arm1
 +    //|    arm1 => (),
 +    //|---^___________^                 everything before arm2
 +    //|    #[cfg(feature = "enabled")]
 +    //|    arm2 => some_code(),
 +    //|---^____________________^        everything before arm3
 +    //|    // some comment about arm3
 +    //|    arm3 => some_code(),
 +    //|---^____________________^        everything after arm3
 +    //|    #[cfg(feature = "disabled")]
 +    //|    arm4 = some_code(),
 +    //|};
 +    //|^
 +    let found = arm_spans.try_fold(start, |start, range| {
 +        let Some((end, next_start)) = range else {
 +            // Shouldn't happen as macros can't expand to match arms, but treat this as though a `cfg` attribute were
 +            // found.
 +            return Err(());
 +        };
 +        let span = SpanData {
 +            lo: start,
 +            hi: end,
 +            ctxt: SyntaxContext::root(),
 +            parent: None,
 +        }
 +        .span();
 +        (!span_contains_cfg(cx, span)).then(|| next_start).ok_or(())
 +    });
 +    match found {
 +        Ok(start) => {
 +            let span = SpanData {
 +                lo: start,
 +                hi: end,
 +                ctxt: SyntaxContext::root(),
 +                parent: None,
 +            }
 +            .span();
 +            span_contains_cfg(cx, span)
 +        },
 +        Err(()) => true,
 +    }
 +}
 +
 +/// Checks if the given span contains a `#[cfg(..)]` attribute
 +fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
 +    let Some(snip) = snippet_opt(cx, s) else {
 +        // Assume true. This would require either an invalid span, or one which crosses file boundaries.
 +        return true;
 +    };
 +    let mut pos = 0usize;
 +    let mut iter = tokenize(&snip).map(|t| {
 +        let start = pos;
 +        pos += t.len;
 +        (t.kind, start..pos)
 +    });
 +
 +    // Search for the token sequence [`#`, `[`, `cfg`]
 +    while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) {
 +        let mut iter = iter.by_ref().skip_while(|(t, _)| {
 +            matches!(
 +                t,
 +                TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. }
 +            )
 +        });
 +        if matches!(iter.next(), Some((TokenKind::OpenBracket, _)))
 +            && matches!(iter.next(), Some((TokenKind::Ident, range)) if &snip[range.clone()] == "cfg")
 +        {
 +            return true;
 +        }
 +    }
 +    false
 +}
index 2105a03e03a301cb8cca7aba53623766dd1fdc6e,0000000000000000000000000000000000000000..f920ad4651f9d4b8c8d304cfb55bd1e99eb1d73c
mode 100644,000000..100644
--- /dev/null
@@@ -1,209 -1,0 +1,209 @@@
-         // Recurrsively check for each `else if let` phrase,
 +use super::NEEDLESS_MATCH;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts};
 +use clippy_utils::{
 +    eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_lang_ctor, over,
 +    peel_blocks_with_stmt,
 +};
 +use rustc_errors::Applicability;
 +use rustc_hir::LangItem::OptionNone;
 +use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, QPath};
 +use rustc_lint::LateContext;
 +use rustc_span::sym;
 +use rustc_typeck::hir_ty_to_ty;
 +
 +pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
 +    if arms.len() > 1 && expr_ty_matches_p_ty(cx, ex, expr) && check_all_arms(cx, ex, arms) {
 +        let mut applicability = Applicability::MachineApplicable;
 +        span_lint_and_sugg(
 +            cx,
 +            NEEDLESS_MATCH,
 +            expr.span,
 +            "this match expression is unnecessary",
 +            "replace it with",
 +            snippet_with_applicability(cx, ex.span, "..", &mut applicability).to_string(),
 +            applicability,
 +        );
 +    }
 +}
 +
 +/// Check for nop `if let` expression that assembled as unnecessary match
 +///
 +/// ```rust,ignore
 +/// if let Some(a) = option {
 +///     Some(a)
 +/// } else {
 +///     None
 +/// }
 +/// ```
 +/// OR
 +/// ```rust,ignore
 +/// if let SomeEnum::A = some_enum {
 +///     SomeEnum::A
 +/// } else if let SomeEnum::B = some_enum {
 +///     SomeEnum::B
 +/// } else {
 +///     some_enum
 +/// }
 +/// ```
 +pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) {
 +    if let Some(ref if_let) = higher::IfLet::hir(cx, ex) {
 +        if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let(cx, if_let) {
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_sugg(
 +                cx,
 +                NEEDLESS_MATCH,
 +                ex.span,
 +                "this if-let expression is unnecessary",
 +                "replace it with",
 +                snippet_with_applicability(cx, if_let.let_expr.span, "..", &mut applicability).to_string(),
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
 +    for arm in arms {
 +        let arm_expr = peel_blocks_with_stmt(arm.body);
 +        if let PatKind::Wild = arm.pat.kind {
 +            return eq_expr_value(cx, match_expr, strip_return(arm_expr));
 +        } else if !pat_same_as_expr(arm.pat, arm_expr) {
 +            return false;
 +        }
 +    }
 +
 +    true
 +}
 +
 +fn check_if_let(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;
 +        }
 +
-                 return true;
++        // 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(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);
 +                }
- /// differs with the assigned local variable or the funtion return type.
++                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 aa3552001f469e4aa6ba2720117baa72c3e2bd32,0000000000000000000000000000000000000000..37b67647efe9e0d6b54abddce1cbec6d5390f65d
mode 100644,000000..100644
--- /dev/null
@@@ -1,432 -1,0 +1,378 @@@
- use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
 +use super::REDUNDANT_PATTERN_MATCHING;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet;
 +use clippy_utils::sugg::Sugg;
- use rustc_data_structures::fx::FxHashSet;
++use clippy_utils::ty::needs_ordered_drop;
 +use clippy_utils::{higher, match_def_path};
 +use clippy_utils::{is_lang_ctor, is_trait_method, paths};
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
-     Arm, Block, Expr, ExprKind, LangItem, Node, Pat, PatKind, QPath, UnOp,
 +use rustc_errors::Applicability;
 +use rustc_hir::LangItem::{OptionNone, PollPending};
 +use rustc_hir::{
 +    intravisit::{walk_expr, Visitor},
- /// Checks if the drop order for a type matters. Some std types implement drop solely to
- /// deallocate memory. For these types, and composites containing them, changing the drop order
- /// won't result in any observable side effects.
- fn type_needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
-     type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
- }
- fn type_needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
-     if !seen.insert(ty) {
-         return false;
-     }
-     if !ty.needs_drop(cx.tcx, cx.param_env) {
-         false
-     } else if !cx
-         .tcx
-         .lang_items()
-         .drop_trait()
-         .map_or(false, |id| implements_trait(cx, ty, id, &[]))
-     {
-         // This type doesn't implement drop, so no side effects here.
-         // Check if any component type has any.
-         match ty.kind() {
-             ty::Tuple(fields) => fields.iter().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
-             ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, *ty, seen),
-             ty::Adt(adt, subs) => adt
-                 .all_fields()
-                 .map(|f| f.ty(cx.tcx, subs))
-                 .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
-             _ => true,
-         }
-     }
-     // Check for std types which implement drop, but only for memory allocation.
-     else if is_type_diagnostic_item(cx, ty, sym::Vec)
-         || is_type_lang_item(cx, ty, LangItem::OwnedBox)
-         || is_type_diagnostic_item(cx, ty, sym::Rc)
-         || is_type_diagnostic_item(cx, ty, sym::Arc)
-         || is_type_diagnostic_item(cx, ty, sym::cstring_type)
-         || is_type_diagnostic_item(cx, ty, sym::BTreeMap)
-         || is_type_diagnostic_item(cx, ty, sym::LinkedList)
-         || match_type(cx, ty, &paths::WEAK_RC)
-         || match_type(cx, ty, &paths::WEAK_ARC)
-     {
-         // Check all of the generic arguments.
-         if let ty::Adt(_, subs) = ty.kind() {
-             subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen))
-         } else {
-             true
-         }
-     } else {
-         true
-     }
- }
++    Arm, Block, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp,
 +};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty};
 +use rustc_span::sym;
 +
 +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +    if let Some(higher::IfLet {
 +        if_else,
 +        let_pat,
 +        let_expr,
 +        ..
 +    }) = higher::IfLet::hir(cx, expr)
 +    {
 +        find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some());
 +    } else if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
 +        find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false);
 +    }
 +}
 +
-                         if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
 +// Extract the generic arguments out of a type
 +fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
 +    if_chain! {
 +        if let ty::Adt(_, subs) = ty.kind();
 +        if let Some(sub) = subs.get(index);
 +        if let GenericArgKind::Type(sub_ty) = sub.unpack();
 +        then {
 +            Some(sub_ty)
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +// Checks if there are any temporaries created in the given expression for which drop order
 +// matters.
 +fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
 +    struct V<'a, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        res: bool,
 +    }
 +    impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
 +        fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 +            match expr.kind {
 +                // Taking the reference of a value leaves a temporary
 +                // e.g. In `&String::new()` the string is a temporary value.
 +                // Remaining fields are temporary values
 +                // e.g. In `(String::new(), 0).1` the string is a temporary value.
 +                ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
 +                    if !matches!(expr.kind, ExprKind::Path(_)) {
-                         if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
++                        if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
 +                            self.res = true;
 +                        } else {
 +                            self.visit_expr(expr);
 +                        }
 +                    }
 +                },
 +                // the base type is alway taken by reference.
 +                // e.g. In `(vec![0])[0]` the vector is a temporary value.
 +                ExprKind::Index(base, index) => {
 +                    if !matches!(base.kind, ExprKind::Path(_)) {
-                         if self_by_ref && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) {
++                        if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
 +                            self.res = true;
 +                        } else {
 +                            self.visit_expr(base);
 +                        }
 +                    }
 +                    self.visit_expr(index);
 +                },
 +                // Method calls can take self by reference.
 +                // e.g. In `String::new().len()` the string is a temporary value.
 +                ExprKind::MethodCall(_, [self_arg, args @ ..], _) => {
 +                    if !matches!(self_arg.kind, ExprKind::Path(_)) {
 +                        let self_by_ref = self
 +                            .cx
 +                            .typeck_results()
 +                            .type_dependent_def_id(expr.hir_id)
 +                            .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
-     let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
++                        if self_by_ref && needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) {
 +                            self.res = true;
 +                        } else {
 +                            self.visit_expr(self_arg);
 +                        }
 +                    }
 +                    args.iter().for_each(|arg| self.visit_expr(arg));
 +                },
 +                // Either explicitly drops values, or changes control flow.
 +                ExprKind::DropTemps(_)
 +                | ExprKind::Ret(_)
 +                | ExprKind::Break(..)
 +                | ExprKind::Yield(..)
 +                | ExprKind::Block(Block { expr: None, .. }, _)
 +                | ExprKind::Loop(..) => (),
 +
 +                // Only consider the final expression.
 +                ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr),
 +
 +                _ => walk_expr(self, expr),
 +            }
 +        }
 +    }
 +
 +    let mut v = V { cx, res: false };
 +    v.visit_expr(expr);
 +    v.res
 +}
 +
 +fn find_sugg_for_if_let<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    let_pat: &Pat<'_>,
 +    let_expr: &'tcx Expr<'_>,
 +    keyword: &'static str,
 +    has_else: bool,
 +) {
 +    // also look inside refs
 +    // if we have &None for example, peel it so we can detect "if let None = x"
 +    let check_pat = match let_pat.kind {
 +        PatKind::Ref(inner, _mutability) => inner,
 +        _ => let_pat,
 +    };
 +    let op_ty = cx.typeck_results().expr_ty(let_expr);
 +    // Determine which function should be used, and the type contained by the corresponding
 +    // variant.
 +    let (good_method, inner_ty) = match check_pat.kind {
 +        PatKind::TupleStruct(ref qpath, [sub_pat], _) => {
 +            if let PatKind::Wild = sub_pat.kind {
 +                let res = cx.typeck_results().qpath_res(qpath, check_pat.hir_id);
 +                let Some(id) = res.opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) else { return };
 +                let lang_items = cx.tcx.lang_items();
 +                if Some(id) == lang_items.result_ok_variant() {
 +                    ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
 +                } else if Some(id) == lang_items.result_err_variant() {
 +                    ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))
 +                } else if Some(id) == lang_items.option_some_variant() {
 +                    ("is_some()", op_ty)
 +                } else if Some(id) == lang_items.poll_ready_variant() {
 +                    ("is_ready()", op_ty)
 +                } else if match_def_path(cx, id, &paths::IPADDR_V4) {
 +                    ("is_ipv4()", op_ty)
 +                } else if match_def_path(cx, id, &paths::IPADDR_V6) {
 +                    ("is_ipv6()", op_ty)
 +                } else {
 +                    return;
 +                }
 +            } else {
 +                return;
 +            }
 +        },
 +        PatKind::Path(ref path) => {
 +            let method = if is_lang_ctor(cx, path, OptionNone) {
 +                "is_none()"
 +            } else if is_lang_ctor(cx, path, PollPending) {
 +                "is_pending()"
 +            } else {
 +                return;
 +            };
 +            // `None` and `Pending` don't have an inner type.
 +            (method, cx.tcx.types.unit)
 +        },
 +        _ => return,
 +    };
 +
 +    // If this is the last expression in a block or there is an else clause then the whole
 +    // type needs to be considered, not just the inner type of the branch being matched on.
 +    // Note the last expression in a block is dropped after all local bindings.
 +    let check_ty = if has_else
 +        || (keyword == "if" && matches!(cx.tcx.hir().parent_iter(expr.hir_id).next(), Some((_, Node::Block(..)))))
 +    {
 +        op_ty
 +    } else {
 +        inner_ty
 +    };
 +
 +    // All temporaries created in the scrutinee expression are dropped at the same time as the
 +    // scrutinee would be, so they have to be considered as well.
 +    // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
 +    // for the duration if body.
++    let needs_drop = needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
 +
 +    // check that `while_let_on_iterator` lint does not trigger
 +    if_chain! {
 +        if keyword == "while";
 +        if let ExprKind::MethodCall(method_path, _, _) = let_expr.kind;
 +        if method_path.ident.name == sym::next;
 +        if is_trait_method(cx, let_expr, sym::Iterator);
 +        then {
 +            return;
 +        }
 +    }
 +
 +    let result_expr = match &let_expr.kind {
 +        ExprKind::AddrOf(_, _, borrowed) => borrowed,
 +        ExprKind::Unary(UnOp::Deref, deref) => deref,
 +        _ => let_expr,
 +    };
 +
 +    span_lint_and_then(
 +        cx,
 +        REDUNDANT_PATTERN_MATCHING,
 +        let_pat.span,
 +        &format!("redundant pattern matching, consider using `{}`", good_method),
 +        |diag| {
 +            // if/while let ... = ... { ... }
 +            // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +            let expr_span = expr.span;
 +
 +            // if/while let ... = ... { ... }
 +            //                 ^^^
 +            let op_span = result_expr.span.source_callsite();
 +
 +            // if/while let ... = ... { ... }
 +            // ^^^^^^^^^^^^^^^^^^^
 +            let span = expr_span.until(op_span.shrink_to_hi());
 +
 +            let app = if needs_drop {
 +                Applicability::MaybeIncorrect
 +            } else {
 +                Applicability::MachineApplicable
 +            };
 +
 +            let sugg = Sugg::hir_with_macro_callsite(cx, result_expr, "_")
 +                .maybe_par()
 +                .to_string();
 +
 +            diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app);
 +
 +            if needs_drop {
 +                diag.note("this will change drop order of the result, as well as all temporaries");
 +                diag.note("add `#[allow(clippy::redundant_pattern_matching)]` if this is important");
 +            }
 +        },
 +    );
 +}
 +
 +pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
 +    if arms.len() == 2 {
 +        let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
 +
 +        let found_good_method = match node_pair {
 +            (
 +                PatKind::TupleStruct(ref path_left, patterns_left, _),
 +                PatKind::TupleStruct(ref path_right, patterns_right, _),
 +            ) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
 +                if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
 +                    find_good_method_for_match(
 +                        cx,
 +                        arms,
 +                        path_left,
 +                        path_right,
 +                        &paths::RESULT_OK,
 +                        &paths::RESULT_ERR,
 +                        "is_ok()",
 +                        "is_err()",
 +                    )
 +                    .or_else(|| {
 +                        find_good_method_for_match(
 +                            cx,
 +                            arms,
 +                            path_left,
 +                            path_right,
 +                            &paths::IPADDR_V4,
 +                            &paths::IPADDR_V6,
 +                            "is_ipv4()",
 +                            "is_ipv6()",
 +                        )
 +                    })
 +                } else {
 +                    None
 +                }
 +            },
 +            (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right))
 +            | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _))
 +                if patterns.len() == 1 =>
 +            {
 +                if let PatKind::Wild = patterns[0].kind {
 +                    find_good_method_for_match(
 +                        cx,
 +                        arms,
 +                        path_left,
 +                        path_right,
 +                        &paths::OPTION_SOME,
 +                        &paths::OPTION_NONE,
 +                        "is_some()",
 +                        "is_none()",
 +                    )
 +                    .or_else(|| {
 +                        find_good_method_for_match(
 +                            cx,
 +                            arms,
 +                            path_left,
 +                            path_right,
 +                            &paths::POLL_READY,
 +                            &paths::POLL_PENDING,
 +                            "is_ready()",
 +                            "is_pending()",
 +                        )
 +                    })
 +                } else {
 +                    None
 +                }
 +            },
 +            _ => None,
 +        };
 +
 +        if let Some(good_method) = found_good_method {
 +            let span = expr.span.to(op.span);
 +            let result_expr = match &op.kind {
 +                ExprKind::AddrOf(_, _, borrowed) => borrowed,
 +                _ => op,
 +            };
 +            span_lint_and_then(
 +                cx,
 +                REDUNDANT_PATTERN_MATCHING,
 +                expr.span,
 +                &format!("redundant pattern matching, consider using `{}`", good_method),
 +                |diag| {
 +                    diag.span_suggestion(
 +                        span,
 +                        "try this",
 +                        format!("{}.{}", snippet(cx, result_expr.span, "_"), good_method),
 +                        Applicability::MaybeIncorrect, // snippet
 +                    );
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +#[allow(clippy::too_many_arguments)]
 +fn find_good_method_for_match<'a>(
 +    cx: &LateContext<'_>,
 +    arms: &[Arm<'_>],
 +    path_left: &QPath<'_>,
 +    path_right: &QPath<'_>,
 +    expected_left: &[&str],
 +    expected_right: &[&str],
 +    should_be_left: &'a str,
 +    should_be_right: &'a str,
 +) -> Option<&'a str> {
 +    let left_id = cx
 +        .typeck_results()
 +        .qpath_res(path_left, arms[0].pat.hir_id)
 +        .opt_def_id()?;
 +    let right_id = cx
 +        .typeck_results()
 +        .qpath_res(path_right, arms[1].pat.hir_id)
 +        .opt_def_id()?;
 +    let body_node_pair = if match_def_path(cx, left_id, expected_left) && match_def_path(cx, right_id, expected_right) {
 +        (&(*arms[0].body).kind, &(*arms[1].body).kind)
 +    } else if match_def_path(cx, right_id, expected_left) && match_def_path(cx, right_id, expected_right) {
 +        (&(*arms[1].body).kind, &(*arms[0].body).kind)
 +    } else {
 +        return None;
 +    };
 +
 +    match body_node_pair {
 +        (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
 +            (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left),
 +            (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right),
 +            _ => None,
 +        },
 +        _ => None,
 +    }
 +}
index 5076239a57c4d79f707b8aa0655d698ba126faf7,0000000000000000000000000000000000000000..0aadb482acddad443b4161732e31cc7871937146
mode 100644,000000..100644
--- /dev/null
@@@ -1,29 -1,0 +1,30 @@@
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use rustc_hir::{Pat, PatKind, QPath};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +
 +use super::REST_PAT_IN_FULLY_BOUND_STRUCTS;
 +
 +pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) {
 +    if_chain! {
 +        if !pat.span.from_expansion();
 +        if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind;
 +        if let Some(def_id) = path.res.opt_def_id();
 +        let ty = cx.tcx.type_of(def_id);
 +        if let ty::Adt(def, _) = ty.kind();
 +        if def.is_struct() || def.is_union();
 +        if fields.len() == def.non_enum_variant().fields.len();
++        if !def.non_enum_variant().is_field_list_non_exhaustive();
 +
 +        then {
 +            span_lint_and_help(
 +                cx,
 +                REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +                pat.span,
 +                "unnecessary use of `..` pattern in struct binding. All fields were already bound",
 +                None,
 +                "consider removing `..` from this binding",
 +            );
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ad333df2f2d5d7f24f33b85050a3a021a4729333
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++//! Lint for `c.is_digit(10)`
++
++use super::IS_DIGIT_ASCII_RADIX;
++use clippy_utils::{
++    consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs,
++    source::snippet_with_applicability,
++};
++use rustc_errors::Applicability;
++use rustc_hir::Expr;
++use rustc_lint::LateContext;
++use rustc_semver::RustcVersion;
++
++pub(super) fn check<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    self_arg: &'tcx Expr<'_>,
++    radix: &'tcx Expr<'_>,
++    msrv: Option<&RustcVersion>,
++) {
++    if !meets_msrv(msrv, &msrvs::IS_ASCII_DIGIT) {
++        return;
++    }
++
++    if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_char() {
++        return;
++    }
++
++    if let Some(radix_val) = constant_full_int(cx, cx.typeck_results(), radix) {
++        let (num, replacement) = match radix_val {
++            FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"),
++            FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"),
++            _ => return,
++        };
++        let mut applicability = Applicability::MachineApplicable;
++
++        span_lint_and_sugg(
++            cx,
++            IS_DIGIT_ASCII_RADIX,
++            expr.span,
++            &format!("use of `char::is_digit` with literal radix of {}", num),
++            "try",
++            format!(
++                "{}.{}()",
++                snippet_with_applicability(cx, self_arg.span, "..", &mut applicability),
++                replacement
++            ),
++            applicability,
++        );
++    }
++}
index 958c3773087b6aa70f19636f445a847e07fd99e5,0000000000000000000000000000000000000000..152072e09c77275795922332495b9622baea5335
mode 100644,000000..100644
--- /dev/null
@@@ -1,72 -1,0 +1,47 @@@
- use clippy_utils::ty::is_type_diagnostic_item;
- use clippy_utils::{
-     higher::{self, Range},
-     SpanlessEq,
- };
 +use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::higher::Range;
 +use clippy_utils::is_integer_const;
- use rustc_span::symbol::{sym, Symbol};
 +use rustc_ast::ast::RangeLimits;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, QPath};
 +use rustc_lint::LateContext;
- const DRAIN_TYPES: &[Symbol] = &[sym::Vec, sym::VecDeque];
++use rustc_span::symbol::sym;
 +use rustc_span::Span;
 +
 +use super::ITER_WITH_DRAIN;
 +
-     let ty = cx.typeck_results().expr_ty(recv).peel_refs();
-     if let Some(drained_type) = DRAIN_TYPES.iter().find(|&&sym| is_type_diagnostic_item(cx, ty, sym)) {
-         // Refuse to emit `into_iter` suggestion on draining struct fields due
-         // to the strong possibility of processing unmovable field.
-         if let ExprKind::Field(..) = recv.kind {
-             return;
-         }
 +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: &Expr<'_>) {
-         if let Some(range) = higher::Range::hir(arg) {
-             let left_full = match range {
-                 Range { start: Some(start), .. } if is_integer_const(cx, start, 0) => true,
-                 Range { start: None, .. } => true,
-                 _ => false,
-             };
-             let full = left_full
-                 && match range {
-                     Range {
-                         end: Some(end),
-                         limits: RangeLimits::HalfOpen,
-                         ..
-                     } => {
-                         // `x.drain(..x.len())` call
-                         if_chain! {
-                             if let ExprKind::MethodCall(len_path, len_args, _) = end.kind;
-                             if len_path.ident.name == sym::len && len_args.len() == 1;
-                             if let ExprKind::Path(QPath::Resolved(_, drain_path)) = recv.kind;
-                             if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;
-                             if SpanlessEq::new(cx).eq_path(drain_path, len_path);
-                             then { true }
-                             else { false }
-                         }
-                     },
-                     Range {
-                         end: None,
-                         limits: RangeLimits::HalfOpen,
-                         ..
-                     } => true,
-                     _ => false,
-                 };
-             if full {
-                 span_lint_and_sugg(
-                     cx,
-                     ITER_WITH_DRAIN,
-                     span.with_hi(expr.span.hi()),
-                     &format!("`drain(..)` used on a `{}`", drained_type),
-                     "try this",
-                     "into_iter()".to_string(),
-                     Applicability::MaybeIncorrect,
-                 );
++    if !matches!(recv.kind, ExprKind::Field(..))
++        && let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
++        && let Some(ty_name) = cx.tcx.get_diagnostic_name(adt.did())
++        && matches!(ty_name, sym::Vec | sym::VecDeque)
++        && let Some(range) = Range::hir(arg)
++        && is_full_range(cx, recv, range)
++    {
++        span_lint_and_sugg(
++            cx,
++            ITER_WITH_DRAIN,
++            span.with_hi(expr.span.hi()),
++            &format!("`drain(..)` used on a `{}`", ty_name),
++            "try this",
++            "into_iter()".to_string(),
++            Applicability::MaybeIncorrect,
++        );
++    };
++}
 +
-         }
-     }
++fn is_full_range(cx: &LateContext<'_>, container: &Expr<'_>, range: Range<'_>) -> bool {
++    range.start.map_or(true, |e| is_integer_const(cx, e, 0))
++        && range.end.map_or(true, |e| {
++            if range.limits == RangeLimits::HalfOpen
++                && let ExprKind::Path(QPath::Resolved(None, container_path)) = container.kind
++                && let ExprKind::MethodCall(name, [self_arg], _) = e.kind
++                && name.ident.name == sym::len
++                && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
++            {
++                container_path.res == path.res
++            } else {
++                false
 +            }
++        })
 +}
index 70d021a1668eb7ffb68b2cce0a79cc5c50d6195d,0000000000000000000000000000000000000000..f3be71f6b8bb8dd78d6cfc5b2825857dea528e2a
mode 100644,000000..100644
--- /dev/null
@@@ -1,2861 -1,0 +1,2926 @@@
-     /// |Prefix |Postfix     |`self` taken           | `self` type  |
-     /// |-------|------------|-----------------------|--------------|
-     /// |`as_`  | none       |`&self` or `&mut self` | any          |
-     /// |`from_`| none       | none                  | any          |
-     /// |`into_`| none       |`self`                 | any          |
-     /// |`is_`  | none       |`&self` or none        | any          |
-     /// |`to_`  | `_mut`     |`&mut self`            | any          |
-     /// |`to_`  | not `_mut` |`self`                 | `Copy`       |
-     /// |`to_`  | not `_mut` |`&self`                | not `Copy`   |
 +mod bind_instead_of_map;
 +mod bytes_nth;
 +mod chars_cmp;
 +mod chars_cmp_with_unwrap;
 +mod chars_last_cmp;
 +mod chars_last_cmp_with_unwrap;
 +mod chars_next_cmp;
 +mod chars_next_cmp_with_unwrap;
 +mod clone_on_copy;
 +mod clone_on_ref_ptr;
 +mod cloned_instead_of_copied;
 +mod err_expect;
 +mod expect_fun_call;
 +mod expect_used;
 +mod extend_with_drain;
 +mod filetype_is_file;
 +mod filter_map;
 +mod filter_map_identity;
 +mod filter_map_next;
 +mod filter_next;
 +mod flat_map_identity;
 +mod flat_map_option;
 +mod from_iter_instead_of_collect;
 +mod get_unwrap;
 +mod implicit_clone;
 +mod inefficient_to_string;
 +mod inspect_for_each;
 +mod into_iter_on_ref;
++mod is_digit_ascii_radix;
 +mod iter_cloned_collect;
 +mod iter_count;
 +mod iter_next_slice;
 +mod iter_nth;
 +mod iter_nth_zero;
 +mod iter_overeager_cloned;
 +mod iter_skip_next;
 +mod iter_with_drain;
 +mod iterator_step_by_zero;
 +mod manual_saturating_arithmetic;
 +mod manual_str_repeat;
 +mod map_collect_result_unit;
 +mod map_flatten;
 +mod map_identity;
 +mod map_unwrap_or;
 +mod needless_option_as_deref;
++mod needless_option_take;
 +mod ok_expect;
 +mod option_as_ref_deref;
 +mod option_map_or_none;
 +mod option_map_unwrap_or;
 +mod or_fun_call;
 +mod or_then_unwrap;
 +mod search_is_some;
 +mod single_char_add_str;
 +mod single_char_insert_string;
 +mod single_char_pattern;
 +mod single_char_push_string;
 +mod skip_while_next;
 +mod str_splitn;
 +mod string_extend_chars;
 +mod suspicious_map;
 +mod suspicious_splitn;
 +mod uninit_assumed_init;
 +mod unnecessary_filter_map;
 +mod unnecessary_fold;
 +mod unnecessary_iter_cloned;
 +mod unnecessary_join;
 +mod unnecessary_lazy_eval;
 +mod unnecessary_to_owned;
 +mod unwrap_or_else_default;
 +mod unwrap_used;
 +mod useless_asref;
 +mod utils;
 +mod wrong_self_convention;
 +mod zst_offset;
 +
 +use bind_instead_of_map::BindInsteadOfMap;
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 +use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
 +use clippy_utils::{contains_return, get_trait_def_id, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_hir::def::Res;
 +use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{self, TraitRef, Ty};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{sym, Span};
 +use rustc_typeck::hir_ty_to_ty;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `cloned()` on an `Iterator` or `Option` where
 +    /// `copied()` could be used instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// `copied()` is better because it guarantees that the type being cloned
 +    /// implements `Copy`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// [1, 2, 3].iter().cloned();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// [1, 2, 3].iter().copied();
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub CLONED_INSTEAD_OF_COPIED,
 +    pedantic,
 +    "used `cloned` where `copied` could be used instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's often inefficient to clone all elements of an iterator, when eventually, only some
 +    /// of them will be consumed.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let vec = vec!["string".to_string()];
 +    ///
 +    /// // Bad
 +    /// vec.iter().cloned().take(10);
 +    ///
 +    /// // Good
 +    /// vec.iter().take(10).cloned();
 +    ///
 +    /// // Bad
 +    /// vec.iter().cloned().last();
 +    ///
 +    /// // Good
 +    /// vec.iter().last().cloned();
 +    ///
 +    /// ```
 +    /// ### Known Problems
 +    /// This `lint` removes the side of effect of cloning items in the iterator.
 +    /// A code that relies on that side-effect could fail.
 +    ///
 +    #[clippy::version = "1.59.0"]
 +    pub ITER_OVEREAGER_CLONED,
 +    perf,
 +    "using `cloned()` early with `Iterator::iter()` can lead to some performance inefficiencies"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
 +    /// used instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// When applicable, `filter_map()` is more clear since it shows that
 +    /// `Option` is used to produce 0 or 1 items.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub FLAT_MAP_OPTION,
 +    pedantic,
 +    "used `flat_map` where `filter_map` could be used instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.unwrap()` calls on `Option`s and on `Result`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is better to handle the `None` or `Err` case,
 +    /// or at least call `.expect(_)` with a more helpful message. Still, for a lot of
 +    /// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is
 +    /// `Allow` by default.
 +    ///
 +    /// `result.unwrap()` will let the thread panic on `Err` values.
 +    /// Normally, you want to implement more sophisticated error handling,
 +    /// and propagate errors upwards with `?` operator.
 +    ///
 +    /// Even if you want to panic on errors, not all `Error`s implement good
 +    /// messages on display. Therefore, it may be beneficial to look at the places
 +    /// where they may get displayed. Activate this lint to do just that.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    ///
 +    /// // Bad
 +    /// opt.unwrap();
 +    ///
 +    /// // Good
 +    /// opt.expect("more helpful message");
 +    /// ```
 +    ///
 +    /// // or
 +    ///
 +    /// ```rust
 +    /// # let res: Result<usize, ()> = Ok(1);
 +    ///
 +    /// // Bad
 +    /// res.unwrap();
 +    ///
 +    /// // Good
 +    /// res.expect("more helpful message");
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub UNWRAP_USED,
 +    restriction,
 +    "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.expect()` calls on `Option`s and `Result`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// Usually it is better to handle the `None` or `Err` case.
 +    /// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why
 +    /// this lint is `Allow` by default.
 +    ///
 +    /// `result.expect()` will let the thread panic on `Err`
 +    /// values. Normally, you want to implement more sophisticated error handling,
 +    /// and propagate errors upwards with `?` operator.
 +    ///
 +    /// ### Examples
 +    /// ```rust,ignore
 +    /// # let opt = Some(1);
 +    ///
 +    /// // Bad
 +    /// opt.expect("one");
 +    ///
 +    /// // Good
 +    /// let opt = Some(1);
 +    /// opt?;
 +    /// ```
 +    ///
 +    /// // or
 +    ///
 +    /// ```rust
 +    /// # let res: Result<usize, ()> = Ok(1);
 +    ///
 +    /// // Bad
 +    /// res.expect("one");
 +    ///
 +    /// // Good
 +    /// res?;
 +    /// # Ok::<(), ()>(())
 +    /// ```
 +    #[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:
 +    ///
-     "using vec.append(&mut vec) to move the full range of a vecor to another"
++    /// |Prefix |Postfix     |`self` taken                   | `self` type  |
++    /// |-------|------------|-------------------------------|--------------|
++    /// |`as_`  | none       |`&self` or `&mut self`         | any          |
++    /// |`from_`| none       | none                          | any          |
++    /// |`into_`| none       |`self`                         | any          |
++    /// |`is_`  | none       |`&mut self` or `&self` or none | any          |
++    /// |`to_`  | `_mut`     |`&mut self`                    | any          |
++    /// |`to_`  | not `_mut` |`self`                         | `Copy`       |
++    /// |`to_`  | not `_mut` |`&self`                        | not `Copy`   |
 +    ///
 +    /// Note: Clippy doesn't trigger methods with `to_` prefix in:
 +    /// - Traits definition.
 +    /// Clippy can not tell if a type that implements a trait is `Copy` or not.
 +    /// - Traits implementation, when `&self` is taken.
 +    /// The method signature is controlled by the trait and often `&self` is required for all types that implement the trait
 +    /// (see e.g. the `std::string::ToString` trait).
 +    ///
 +    /// Clippy allows `Pin<&Self>` and `Pin<&mut Self>` if `&self` and `&mut self` is required.
 +    ///
 +    /// Please find more info here:
 +    /// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
 +    ///
 +    /// ### Why is this bad?
 +    /// Consistency breeds readability. If you follow the
 +    /// conventions, your users won't be surprised that they, e.g., need to supply a
 +    /// mutable reference to a `as_..` function.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct X;
 +    /// impl X {
 +    ///     fn as_str(self) -> &'static str {
 +    ///         // ..
 +    /// # ""
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRONG_SELF_CONVENTION,
 +    style,
 +    "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `ok().expect(..)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Because you usually call `expect()` on the `Result`
 +    /// directly to get a better error message.
 +    ///
 +    /// ### Known problems
 +    /// The error type needs to implement `Debug`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = Ok::<_, ()>(());
 +    ///
 +    /// // Bad
 +    /// x.ok().expect("why did I do this again?");
 +    ///
 +    /// // Good
 +    /// x.expect("why did I do this again?");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OK_EXPECT,
 +    style,
 +    "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.err().expect()` calls on the `Result` type.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.expect_err()` can be called directly to avoid the extra type conversion from `err()`.
 +    ///
 +    /// ### Example
 +    /// ```should_panic
 +    /// let x: Result<u32, &str> = Ok(10);
 +    /// x.err().expect("Testing err().expect()");
 +    /// ```
 +    /// Use instead:
 +    /// ```should_panic
 +    /// let x: Result<u32, &str> = Ok(10);
 +    /// x.expect_err("Testing expect_err");
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub ERR_EXPECT,
 +    style,
 +    r#"using `.err().expect("")` when `.expect_err("")` can be used"#
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and
 +    /// `Result` values.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, these can be written as `_.unwrap_or_default`, which is
 +    /// simpler and more concise.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let x = Some(1);
 +    ///
 +    /// // Bad
 +    /// x.unwrap_or_else(Default::default);
 +    /// x.unwrap_or_else(u32::default);
 +    ///
 +    /// // Good
 +    /// x.unwrap_or_default();
 +    /// ```
 +    #[clippy::version = "1.56.0"]
 +    pub UNWRAP_OR_ELSE_DEFAULT,
 +    style,
 +    "using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or
 +    /// `result.map(_).unwrap_or_else(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, these can be written more concisely (resp.) as
 +    /// `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.
 +    ///
 +    /// ### Known problems
 +    /// The order of the arguments is not in execution order
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let x = Some(1);
 +    ///
 +    /// // Bad
 +    /// x.map(|a| a + 1).unwrap_or(0);
 +    ///
 +    /// // Good
 +    /// x.map_or(0, |a| a + 1);
 +    /// ```
 +    ///
 +    /// // or
 +    ///
 +    /// ```rust
 +    /// # let x: Result<usize, ()> = Ok(1);
 +    /// # fn some_function(foo: ()) -> usize { 1 }
 +    ///
 +    /// // Bad
 +    /// x.map(|a| a + 1).unwrap_or_else(some_function);
 +    ///
 +    /// // Good
 +    /// x.map_or_else(some_function, |a| a + 1);
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub MAP_UNWRAP_OR,
 +    pedantic,
 +    "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map_or(None, _)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.and_then(_)`.
 +    ///
 +    /// ### Known problems
 +    /// The order of the arguments is not in execution order.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    ///
 +    /// // Bad
 +    /// opt.map_or(None, |a| Some(a + 1));
 +    ///
 +    /// // Good
 +    /// opt.and_then(|a| Some(a + 1));
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OPTION_MAP_OR_NONE,
 +    style,
 +    "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map_or(None, Some)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.ok()`.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust
 +    /// # let r: Result<u32, &str> = Ok(1);
 +    /// assert_eq!(Some(1), r.map_or(None, Some));
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust
 +    /// # let r: Result<u32, &str> = Ok(1);
 +    /// assert_eq!(Some(1), r.ok());
 +    /// ```
 +    #[clippy::version = "1.44.0"]
 +    pub RESULT_MAP_OR_INTO_OPTION,
 +    style,
 +    "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or
 +    /// `_.or_else(|x| Err(y))`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.map(|x| y)` or `_.map_err(|x| y)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn opt() -> Option<&'static str> { Some("42") }
 +    /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
 +    /// let _ = opt().and_then(|s| Some(s.len()));
 +    /// let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });
 +    /// let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });
 +    /// ```
 +    ///
 +    /// The correct use would be:
 +    ///
 +    /// ```rust
 +    /// # fn opt() -> Option<&'static str> { Some("42") }
 +    /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
 +    /// let _ = opt().map(|s| s.len());
 +    /// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });
 +    /// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub BIND_INSTEAD_OF_MAP,
 +    complexity,
 +    "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter(_).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().filter(|x| **x == 0).next();
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().find(|x| **x == 0);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FILTER_NEXT,
 +    complexity,
 +    "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.skip_while(condition).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find(!condition)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().skip_while(|x| **x == 0).next();
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().find(|x| **x != 0);
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub SKIP_WHILE_NEXT,
 +    complexity,
 +    "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map(_).flatten(_)` on `Iterator` and `Option`
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.flat_map(_)` for `Iterator` or `_.and_then(_)` for `Option`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec = vec![vec![1]];
 +    /// let opt = Some(5);
 +    ///
 +    /// // Bad
 +    /// vec.iter().map(|x| x.iter()).flatten();
 +    /// opt.map(|x| Some(x * 2)).flatten();
 +    ///
 +    /// // Good
 +    /// vec.iter().flat_map(|x| x.iter());
 +    /// opt.and_then(|x| Some(x * 2));
 +    /// ```
 +    #[clippy::version = "1.31.0"]
 +    pub MAP_FLATTEN,
 +    complexity,
 +    "using combinations of `flatten` and `map` which can usually be written as a single method call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter(_).map(_)` that can be written more simply
 +    /// as `filter_map(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code in the `filter` and `map` operations is poor style and
 +    /// less performant.
 +    ///
 +     /// ### Example
 +    /// Bad:
 +    /// ```rust
 +    /// (0_i32..10)
 +    ///     .filter(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust
 +    /// (0_i32..10).filter_map(|n| n.checked_add(1));
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub MANUAL_FILTER_MAP,
 +    complexity,
 +    "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.find(_).map(_)` that can be written more simply
 +    /// as `find_map(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code in the `find` and `map` operations is poor style and
 +    /// less performant.
 +    ///
 +     /// ### Example
 +    /// Bad:
 +    /// ```rust
 +    /// (0_i32..10)
 +    ///     .find(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust
 +    /// (0_i32..10).find_map(|n| n.checked_add(1));
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub MANUAL_FIND_MAP,
 +    complexity,
 +    "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter_map(_).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find_map(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    ///  (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();
 +    /// ```
 +    /// Can be written as
 +    ///
 +    /// ```rust
 +    ///  (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
 +    /// ```
 +    #[clippy::version = "1.36.0"]
 +    pub FILTER_MAP_NEXT,
 +    pedantic,
 +    "using combination of `filter_map` and `next` which can usually be written as a single method call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `flat_map(|x| x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely by using `flatten`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iter = vec![vec![0]].into_iter();
 +    /// iter.flat_map(|x| x);
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// # let iter = vec![vec![0]].into_iter();
 +    /// iter.flatten();
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub FLAT_MAP_IDENTITY,
 +    complexity,
 +    "call to `flat_map` where `flatten` is sufficient"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for an iterator or string search (such as `find()`,
 +    /// `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as:
 +    /// * `_.any(_)`, or `_.contains(_)` for `is_some()`,
 +    /// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec = vec![1];
 +    /// vec.iter().find(|x| **x == 0).is_some();
 +    ///
 +    /// let _ = "hello world".find("world").is_none();
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// let vec = vec![1];
 +    /// vec.iter().any(|x| *x == 0);
 +    ///
 +    /// let _ = !"hello world".contains("world");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SEARCH_IS_SOME,
 +    complexity,
 +    "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.chars().next()` on a `str` to check
 +    /// if it starts with a given char.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.starts_with(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let name = "foo";
 +    /// if name.chars().next() == Some('_') {};
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// let name = "foo";
 +    /// if name.starts_with('_') {};
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CHARS_NEXT_CMP,
 +    style,
 +    "using `.chars().next()` to check if a string starts with a char"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,
 +    /// etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or
 +    /// `unwrap_or_default` instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// The function will always be called and potentially
 +    /// allocate an object acting as the default.
 +    ///
 +    /// ### Known problems
 +    /// If the function has side-effects, not calling it will
 +    /// change the semantic of the program, but you shouldn't rely on that anyway.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or(String::new());
 +    /// ```
 +    /// this can instead be written:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or_else(String::new);
 +    /// ```
 +    /// or
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or_default();
 +    /// ```
 +    #[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
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # let err_msg = "I'm a teapot";
 +    /// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str());
 +    /// ```
 +    /// this can instead be written:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # let err_msg = "I'm a teapot";
 +    /// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXPECT_FUN_CALL,
 +    perf,
 +    "using any `expect` method with a function call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on a `Copy` type.
 +    ///
 +    /// ### Why is this bad?
 +    /// The only reason `Copy` types implement `Clone` is for
 +    /// generics, not for using the `clone` method on a concrete type.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// 42u64.clone();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CLONE_ON_COPY,
 +    complexity,
 +    "using `clone` on a `Copy` type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on a ref-counted pointer,
 +    /// (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified
 +    /// function syntax instead (e.g., `Rc::clone(foo)`).
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling '.clone()' on an Rc, Arc, or Weak
 +    /// can obscure the fact that only the pointer is being cloned, not the underlying
 +    /// data.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// let x = Rc::new(1);
 +    ///
 +    /// // Bad
 +    /// x.clone();
 +    ///
 +    /// // Good
 +    /// Rc::clone(&x);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CLONE_ON_REF_PTR,
 +    restriction,
 +    "using 'clone' on a ref-counted pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on an `&&T`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Cloning an `&&T` copies the inner `&T`, instead of
 +    /// cloning the underlying `T`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn main() {
 +    ///     let x = vec![1];
 +    ///     let y = &&x;
 +    ///     let z = y.clone();
 +    ///     println!("{:p} {:p}", *y, z); // prints out the same pointer
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CLONE_DOUBLE_REF,
 +    correctness,
 +    "using `clone` on `&&T`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.to_string()` on an `&&T` where
 +    /// `T` implements `ToString` directly (like `&&str` or `&&String`).
 +    ///
 +    /// ### Why is this bad?
 +    /// This bypasses the specialized implementation of
 +    /// `ToString` and instead goes through the more expensive string formatting
 +    /// facilities.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Generic implementation for `T: Display` is used (slow)
 +    /// ["foo", "bar"].iter().map(|s| s.to_string());
 +    ///
 +    /// // OK, the specialized impl is used
 +    /// ["foo", "bar"].iter().map(|&s| s.to_string());
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub INEFFICIENT_TO_STRING,
 +    pedantic,
 +    "using `to_string` on `&&T` where `T: ToString`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `new` not returning a type that contains `Self`.
 +    ///
 +    /// ### Why is this bad?
 +    /// As a convention, `new` methods are used to make a new
 +    /// instance of a type.
 +    ///
 +    /// ### Example
 +    /// In an impl block:
 +    /// ```rust
 +    /// # struct Foo;
 +    /// # struct NotAFoo;
 +    /// impl Foo {
 +    ///     fn new() -> NotAFoo {
 +    /// # NotAFoo
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # struct Foo;
 +    /// struct Bar(Foo);
 +    /// impl Foo {
 +    ///     // Bad. The type name must contain `Self`
 +    ///     fn new() -> Bar {
 +    /// # Bar(Foo)
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # struct Foo;
 +    /// # struct FooError;
 +    /// impl Foo {
 +    ///     // Good. Return type contains `Self`
 +    ///     fn new() -> Result<Foo, FooError> {
 +    /// # Ok(Foo)
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Or in a trait definition:
 +    /// ```rust
 +    /// pub trait Trait {
 +    ///     // Bad. The type name must contain `Self`
 +    ///     fn new();
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// pub trait Trait {
 +    ///     // Good. Return type contains `Self`
 +    ///     fn new() -> Self;
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEW_RET_NO_SELF,
 +    style,
 +    "not returning type containing `Self` in a `new` method"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for string methods that receive a single-character
 +    /// `str` as an argument, e.g., `_.split("x")`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Performing these methods using a `char` is faster than
 +    /// using a `str`.
 +    ///
 +    /// ### Known problems
 +    /// Does not catch multi-byte unicode characters.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// _.split("x");
 +    ///
 +    /// // Good
 +    /// _.split('x');
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SINGLE_CHAR_PATTERN,
 +    perf,
 +    "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calling `.step_by(0)` on iterators which panics.
 +    ///
 +    /// ### Why is this bad?
 +    /// This very much looks like an oversight. Use `panic!()` instead if you
 +    /// actually intend to panic.
 +    ///
 +    /// ### Example
 +    /// ```rust,should_panic
 +    /// for x in (0..100).step_by(0) {
 +    ///     //..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITERATOR_STEP_BY_ZERO,
 +    correctness,
 +    "using `Iterator::step_by(0)`, which will panic at runtime"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for indirect collection of populated `Option`
 +    ///
 +    /// ### Why is this bad?
 +    /// `Option` is like a collection of 0-1 things, so `flatten`
 +    /// automatically does this without suspicious-looking `unwrap` calls.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = std::iter::empty::<Option<i32>>().filter(Option::is_some).map(Option::unwrap);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let _ = std::iter::empty::<Option<i32>>().flatten();
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub OPTION_FILTER_MAP,
 +    complexity,
 +    "filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `iter.nth(0)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `iter.next()` is equivalent to
 +    /// `iter.nth(0)`, as they both consume the next element,
 +    ///  but is more readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// // Bad
 +    /// # let mut s = HashSet::new();
 +    /// # s.insert(1);
 +    /// let x = s.iter().nth(0);
 +    ///
 +    /// // Good
 +    /// # let mut s = HashSet::new();
 +    /// # s.insert(1);
 +    /// let x = s.iter().next();
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub ITER_NTH_ZERO,
 +    style,
 +    "replace `iter.nth(0)` with `iter.next()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.iter().nth()` (and the related
 +    /// `.iter_mut().nth()`) on standard library types with *O*(1) element access.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.get()` and `.get_mut()` are more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().nth(3);
 +    /// let bad_slice = &some_vec[..].iter().nth(3);
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.get(3);
 +    /// let bad_slice = &some_vec[..].get(3);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITER_NTH,
 +    perf,
 +    "using `.iter().nth()` on a standard library type with O(1) element access"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.skip(x).next()` on iterators.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.nth(x)` is cleaner
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().skip(3).next();
 +    /// let bad_slice = &some_vec[..].iter().skip(3).next();
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().nth(3);
 +    /// let bad_slice = &some_vec[..].iter().nth(3);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITER_SKIP_NEXT,
 +    style,
 +    "using `.skip(x).next()` on an iterator"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.drain(..)` on `Vec` and `VecDeque` for iteration.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.into_iter()` is simpler with better performance.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// let mut foo = vec![0, 1, 2, 3];
 +    /// let bar: HashSet<usize> = foo.drain(..).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// let foo = vec![0, 1, 2, 3];
 +    /// let bar: HashSet<usize> = foo.into_iter().collect();
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub ITER_WITH_DRAIN,
 +    nursery,
 +    "replace `.drain(..)` with `.into_iter()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.get().unwrap()` (or
 +    /// `.get_mut().unwrap`) on a standard library type which implements `Index`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the Index trait (`[]`) is more clear and more
 +    /// concise.
 +    ///
 +    /// ### Known problems
 +    /// Not a replacement for error handling: Using either
 +    /// `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`
 +    /// if the value being accessed is `None`. If the use of `.get().unwrap()` is a
 +    /// temporary placeholder for dealing with the `Option` type, then this does
 +    /// not mitigate the need for error handling. If there is a chance that `.get()`
 +    /// will be `None` in your program, then it is advisable that the `None` case
 +    /// is handled in a future refactor instead of using `.unwrap()` or the Index
 +    /// trait.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut some_vec = vec![0, 1, 2, 3];
 +    /// let last = some_vec.get(3).unwrap();
 +    /// *some_vec.get_mut(0).unwrap() = 1;
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let mut some_vec = vec![0, 1, 2, 3];
 +    /// let last = some_vec[3];
 +    /// some_vec[0] = 1;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub GET_UNWRAP,
 +    restriction,
 +    "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for occurrences where one vector gets extended instead of append
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `append` instead of `extend` is more concise and faster
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut a = vec![1, 2, 3];
 +    /// let mut b = vec![4, 5, 6];
 +    ///
 +    /// // Bad
 +    /// a.extend(b.drain(..));
 +    ///
 +    /// // Good
 +    /// a.append(&mut b);
 +    /// ```
 +    #[clippy::version = "1.55.0"]
 +    pub EXTEND_WITH_DRAIN,
 +    perf,
-     ///  let (key, value) = _.splitn(2, '=').next_tuple()?;
-     ///  let value = _.splitn(2, '=').nth(1)?;
++    "using vec.append(&mut vec) to move the full range of a vector to another"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.extend(s.chars())` where s is a
 +    /// `&str` or `String`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.push_str(s)` is clearer
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let abc = "abc";
 +    /// let def = String::from("def");
 +    /// let mut s = String::new();
 +    /// s.extend(abc.chars());
 +    /// s.extend(def.chars());
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let abc = "abc";
 +    /// let def = String::from("def");
 +    /// let mut s = String::new();
 +    /// s.push_str(abc);
 +    /// s.push_str(&def);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub STRING_EXTEND_CHARS,
 +    style,
 +    "using `x.extend(s.chars())` where s is a `&str` or `String`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.cloned().collect()` on slice to
 +    /// create a `Vec`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.to_vec()` is clearer
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let s = [1, 2, 3, 4, 5];
 +    /// let s2: Vec<isize> = s[..].iter().cloned().collect();
 +    /// ```
 +    /// The better use would be:
 +    /// ```rust
 +    /// let s = [1, 2, 3, 4, 5];
 +    /// let s2: Vec<isize> = s.to_vec();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITER_CLONED_COLLECT,
 +    style,
 +    "using `.cloned().collect()` on slice to create a `Vec`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.chars().last()` or
 +    /// `_.chars().next_back()` on a `str` to check if it ends with a given char.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.ends_with(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let name = "_";
 +    ///
 +    /// // Bad
 +    /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-');
 +    ///
 +    /// // Good
 +    /// name.ends_with('_') || name.ends_with('-');
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CHARS_LAST_CMP,
 +    style,
 +    "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.as_ref()` or `.as_mut()` where the
 +    /// types before and after the call are the same.
 +    ///
 +    /// ### Why is this bad?
 +    /// The call is unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn do_stuff(x: &[i32]) {}
 +    /// let x: &[i32] = &[1, 2, 3, 4, 5];
 +    /// do_stuff(x.as_ref());
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// # fn do_stuff(x: &[i32]) {}
 +    /// let x: &[i32] = &[1, 2, 3, 4, 5];
 +    /// do_stuff(x);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USELESS_ASREF,
 +    complexity,
 +    "using `as_ref` where the types before and after the call are the same"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for using `fold` when a more succinct alternative exists.
 +    /// Specifically, this checks for `fold`s which could be replaced by `any`, `all`,
 +    /// `sum` or `product`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).fold(false, |acc, x| acc || x > 2);
 +    /// ```
 +    /// This could be written as:
 +    /// ```rust
 +    /// let _ = (0..3).any(|x| x > 2);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNNECESSARY_FOLD,
 +    style,
 +    "using `fold` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `filter_map` calls that could be replaced by `filter` or `map`.
 +    /// More specifically it checks if the closure provided is only performing one of the
 +    /// filter or map operations and suggests the appropriate option.
 +    ///
 +    /// ### Why is this bad?
 +    /// Complexity. The intent is also clearer if only a single
 +    /// operation is being performed.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });
 +    ///
 +    /// // As there is no transformation of the argument this could be written as:
 +    /// let _ = (0..3).filter(|&x| x > 2);
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// let _ = (0..4).filter_map(|x| Some(x + 1));
 +    ///
 +    /// // As there is no conditional check on the argument this could be written as:
 +    /// let _ = (0..4).map(|x| x + 1);
 +    /// ```
 +    #[clippy::version = "1.31.0"]
 +    pub UNNECESSARY_FILTER_MAP,
 +    complexity,
 +    "using `filter_map` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `find_map` calls that could be replaced by `find` or `map`. More
 +    /// specifically it checks if the closure provided is only performing one of the
 +    /// find or map operations and suggests the appropriate option.
 +    ///
 +    /// ### Why is this bad?
 +    /// Complexity. The intent is also clearer if only a single
 +    /// operation is being performed.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).find_map(|x| if x > 2 { Some(x) } else { None });
 +    ///
 +    /// // As there is no transformation of the argument this could be written as:
 +    /// let _ = (0..3).find(|&x| x > 2);
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// let _ = (0..4).find_map(|x| Some(x + 1));
 +    ///
 +    /// // As there is no conditional check on the argument this could be written as:
 +    /// let _ = (0..4).map(|x| x + 1).next();
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub UNNECESSARY_FIND_MAP,
 +    complexity,
 +    "using `find_map` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `into_iter` calls on references which should be replaced by `iter`
 +    /// or `iter_mut`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. Calling `into_iter` on a reference will not move out its
 +    /// content into the resulting iterator, which is confusing. It is better just call `iter` or
 +    /// `iter_mut` directly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let _ = (&vec![3, 4, 5]).into_iter();
 +    ///
 +    /// // Good
 +    /// let _ = (&vec![3, 4, 5]).iter();
 +    /// ```
 +    #[clippy::version = "1.32.0"]
 +    pub INTO_ITER_ON_REF,
 +    style,
 +    "using `.into_iter()` on a reference"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `map` followed by a `count`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It looks suspicious. Maybe `map` was confused with `filter`.
 +    /// If the `map` call is intentional, this should be rewritten
 +    /// using `inspect`. Or, if you intend to drive the iterator to
 +    /// completion, you can just use `for_each` instead.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).map(|x| x + 2).count();
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub SUSPICIOUS_MAP,
 +    suspicious,
 +    "suspicious usage of map"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `MaybeUninit::uninit().assume_init()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// For most types, this is undefined behavior.
 +    ///
 +    /// ### Known problems
 +    /// For now, we accept empty tuples and tuples / arrays
 +    /// of `MaybeUninit`. There may be other types that allow uninitialized
 +    /// data, but those are not yet rigorously defined.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Beware the UB
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
 +    /// ```
 +    ///
 +    /// Note that the following is OK:
 +    ///
 +    /// ```rust
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: [MaybeUninit<bool>; 5] = unsafe {
 +    ///     MaybeUninit::uninit().assume_init()
 +    /// };
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub UNINIT_ASSUMED_INIT,
 +    correctness,
 +    "`MaybeUninit::uninit().assume_init()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be written simply with `saturating_add/sub` methods.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.checked_add(y).unwrap_or(u32::MAX);
 +    /// let sub = x.checked_sub(y).unwrap_or(u32::MIN);
 +    /// ```
 +    ///
 +    /// can be written using dedicated methods for saturating addition/subtraction as:
 +    ///
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.saturating_add(y);
 +    /// let sub = x.saturating_sub(y);
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub MANUAL_SATURATING_ARITHMETIC,
 +    style,
 +    "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
 +    /// zero-sized types
 +    ///
 +    /// ### Why is this bad?
 +    /// This is a no-op, and likely unintended
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe { (&() as *const ()).offset(1) };
 +    /// ```
 +    #[clippy::version = "1.41.0"]
 +    pub ZST_OFFSET,
 +    correctness,
 +    "Check for offset calculations on raw pointers to zero-sized types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `FileType::is_file()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// When people testing a file type with `FileType::is_file`
 +    /// they are testing whether a path is something they can get bytes from. But
 +    /// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover
 +    /// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if filetype.is_file() {
 +    ///     // read file
 +    /// }
 +    /// # Ok::<_, std::io::Error>(())
 +    /// # };
 +    /// ```
 +    ///
 +    /// should be written as:
 +    ///
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if !filetype.is_dir() {
 +    ///     // read file
 +    /// }
 +    /// # Ok::<_, std::io::Error>(())
 +    /// # };
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub FILETYPE_IS_FILE,
 +    restriction,
 +    "`FileType::is_file` is not recommended to test for readable file type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.as_deref()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some("".to_string());
 +    /// opt.as_ref().map(String::as_str)
 +    /// # ;
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// # let opt = Some("".to_string());
 +    /// opt.as_deref()
 +    /// # ;
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub OPTION_AS_REF_DEREF,
 +    complexity,
 +    "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `iter().next()` on a Slice or an Array
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be shortened into `.get()`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let a = [1, 2, 3];
 +    /// # let b = vec![1, 2, 3];
 +    /// a[2..].iter().next();
 +    /// b.iter().next();
 +    /// ```
 +    /// should be written as:
 +    /// ```rust
 +    /// # let a = [1, 2, 3];
 +    /// # let b = vec![1, 2, 3];
 +    /// a.get(2);
 +    /// b.get(0);
 +    /// ```
 +    #[clippy::version = "1.46.0"]
 +    pub ITER_NEXT_SLICE,
 +    style,
 +    "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns when using `push_str`/`insert_str` with a single-character string literal
 +    /// where `push`/`insert` with a `char` would work fine.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's less clear that we are pushing a single character.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut string = String::new();
 +    /// string.insert_str(0, "R");
 +    /// string.push_str("R");
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// let mut string = String::new();
 +    /// string.insert(0, 'R');
 +    /// string.push('R');
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub SINGLE_CHAR_ADD_STR,
 +    style,
 +    "`push_str()` or `insert_str()` used with a single-character string literal as parameter"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// As the counterpart to `or_fun_call`, this lint looks for unnecessary
 +    /// lazily evaluated closures on `Option` and `Result`.
 +    ///
 +    /// This lint suggests changing the following functions, when eager evaluation results in
 +    /// simpler code:
 +    ///  - `unwrap_or_else` to `unwrap_or`
 +    ///  - `and_then` to `and`
 +    ///  - `or_else` to `or`
 +    ///  - `get_or_insert_with` to `get_or_insert`
 +    ///  - `ok_or_else` to `ok_or`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using eager evaluation is shorter and simpler in some cases.
 +    ///
 +    /// ### Known problems
 +    /// It is possible, but not recommended for `Deref` and `Index` to have
 +    /// side effects. Eagerly evaluating them can change the semantics of the program.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // example code where clippy issues a warning
 +    /// let opt: Option<u32> = None;
 +    ///
 +    /// opt.unwrap_or_else(|| 42);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let opt: Option<u32> = None;
 +    ///
 +    /// opt.unwrap_or(42);
 +    /// ```
 +    #[clippy::version = "1.48.0"]
 +    pub UNNECESSARY_LAZY_EVALUATIONS,
 +    style,
 +    "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map(_).collect::<Result<(), _>()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `try_for_each` instead is more readable and idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// (0..3).try_for_each(|t| Err(t));
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub MAP_COLLECT_RESULT_UNIT,
 +    style,
 +    "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `from_iter()` function calls on types that implement the `FromIterator`
 +    /// trait.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is recommended style to use collect. See
 +    /// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::iter::FromIterator;
 +    ///
 +    /// let five_fives = std::iter::repeat(5).take(5);
 +    ///
 +    /// let v = Vec::from_iter(five_fives);
 +    ///
 +    /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let five_fives = std::iter::repeat(5).take(5);
 +    ///
 +    /// let v: Vec<i32> = five_fives.collect();
 +    ///
 +    /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub FROM_ITER_INSTEAD_OF_COLLECT,
 +    pedantic,
 +    "use `.collect()` instead of `::from_iter()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `inspect().for_each()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is the same as performing the computation
 +    /// inside `inspect` at the beginning of the closure in `for_each`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// [1,2,3,4,5].iter()
 +    /// .inspect(|&x| println!("inspect the number: {}", x))
 +    /// .for_each(|&x| {
 +    ///     assert!(x >= 0);
 +    /// });
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// [1,2,3,4,5].iter()
 +    /// .for_each(|&x| {
 +    ///     println!("inspect the number: {}", x);
 +    ///     assert!(x >= 0);
 +    /// });
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub INSPECT_FOR_EACH,
 +    complexity,
 +    "using `.inspect().for_each()`, which can be replaced with `.for_each()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `filter_map(|x| x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely by using `flatten`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iter = vec![Some(1)].into_iter();
 +    /// iter.filter_map(|x| x);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let iter = vec![Some(1)].into_iter();
 +    /// iter.flatten();
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub FILTER_MAP_IDENTITY,
 +    complexity,
 +    "call to `filter_map` where `flatten` is sufficient"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for instances of `map(f)` where `f` is the identity function.
 +    ///
 +    /// ### Why is this bad?
 +    /// It can be written more concisely without the call to `map`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = [1, 2, 3];
 +    /// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = [1, 2, 3];
 +    /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub MAP_IDENTITY,
 +    complexity,
 +    "using iterator.map(|x| x)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.bytes().nth()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.as_bytes().get()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let _ = "Hello".bytes().nth(3);
 +    ///
 +    /// // Good
 +    /// let _ = "Hello".as_bytes().get(3);
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub BYTES_NTH,
 +    style,
 +    "replace `.bytes().nth()` with `.as_bytes().get()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.
 +    ///
 +    /// ### Why is this bad?
 +    /// These methods do the same thing as `_.clone()` but may be confusing as
 +    /// to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = vec![1, 2, 3];
 +    /// let b = a.to_vec();
 +    /// let c = a.to_owned();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = vec![1, 2, 3];
 +    /// let b = a.clone();
 +    /// let c = a.clone();
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub IMPLICIT_CLONE,
 +    pedantic,
 +    "implicitly cloning a value by invoking a function on its dereferenced type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.iter().count()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.len()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let _ = some_vec.iter().count();
 +    /// let _ = &some_vec[..].iter().count();
 +    ///
 +    /// // Good
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let _ = some_vec.len();
 +    /// let _ = &some_vec[..].len();
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub ITER_COUNT,
 +    complexity,
 +    "replace `.iter().count()` with `.len()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to [`splitn`]
 +    /// (https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and
 +    /// related functions with either zero or one splits.
 +    ///
 +    /// ### Why is this bad?
 +    /// These calls don't actually split the value and are
 +    /// likely to be intended as a different number.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let s = "";
 +    /// for x in s.splitn(1, ":") {
 +    ///     // use x
 +    /// }
 +    ///
 +    /// // Good
 +    /// let s = "";
 +    /// for x in s.splitn(2, ":") {
 +    ///     // use x
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.54.0"]
 +    pub SUSPICIOUS_SPLITN,
 +    correctness,
 +    "checks for `.splitn(0, ..)` and `.splitn(1, ..)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for manual implementations of `str::repeat`
 +    ///
 +    /// ### Why is this bad?
 +    /// These are both harder to read, as well as less performant.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let x: String = std::iter::repeat('x').take(10).collect();
 +    ///
 +    /// // Good
 +    /// let x: String = "x".repeat(10);
 +    /// ```
 +    #[clippy::version = "1.54.0"]
 +    pub MANUAL_STR_REPEAT,
 +    perf,
 +    "manual implementation of `str::repeat`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `str::splitn(2, _)`
 +    ///
 +    /// ### Why is this bad?
 +    /// `split_once` is both clearer in intent and slightly more efficient.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // Bad
-     /// let (key, value) = _.split_once('=')?;
-     /// let value = _.split_once('=')?.1;
++    /// let s = "key=value=add";
++    /// let (key, value) = s.splitn(2, '=').next_tuple()?;
++    /// let value = s.splitn(2, '=').nth(1)?;
 +    ///
++    /// let mut parts = s.splitn(2, '=');
++    /// let key = parts.next()?;
++    /// let value = parts.next()?;
++    /// ```
++    /// Use instead:
++    /// ```rust,ignore
 +    /// // Good
-     /// Additionlly, differences have been observed between aarch64 and x86_64 assembly output,
++    /// let s = "key=value=add";
++    /// let (key, value) = s.split_once('=')?;
++    /// let value = s.split_once('=')?.1;
++    ///
++    /// let (key, value) = s.split_once('=')?;
 +    /// ```
++    ///
++    /// ### Limitations
++    /// The multiple statement variant currently only detects `iter.next()?`/`iter.next().unwrap()`
++    /// in two separate `let` statements that immediately follow the `splitn()`
 +    #[clippy::version = "1.57.0"]
 +    pub MANUAL_SPLIT_ONCE,
 +    complexity,
 +    "replace `.splitn(2, pat)` with `.split_once(pat)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `str::splitn` (or `str::rsplitn`) where using `str::split` would be the same.
 +    /// ### Why is this bad?
 +    /// The function `split` is simpler and there is no performance difference in these cases, considering
 +    /// that both functions return a lazy iterator.
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let str = "key=value=add";
 +    /// let _ = str.splitn(3, '=').next().unwrap();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// // Good
 +    /// let str = "key=value=add";
 +    /// let _ = str.split('=').next().unwrap();
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub NEEDLESS_SPLITN,
 +    complexity,
 +    "usages of `str::splitn` that can be replaced with `str::split`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary calls to [`ToOwned::to_owned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned)
 +    /// and other `to_owned`-like functions.
 +    ///
 +    /// ### Why is this bad?
 +    /// The unnecessary calls result in useless allocations.
 +    ///
 +    /// ### Known problems
 +    /// `unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an
 +    /// owned copy of a resource and the resource is later used mutably. See
 +    /// [#8148](https://github.com/rust-lang/rust-clippy/issues/8148).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let path = std::path::Path::new("x");
 +    /// foo(&path.to_string_lossy().to_string());
 +    /// fn foo(s: &str) {}
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let path = std::path::Path::new("x");
 +    /// foo(&path.to_string_lossy());
 +    /// fn foo(s: &str) {}
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub UNNECESSARY_TO_OWNED,
 +    perf,
 +    "unnecessary calls to `to_owned`-like functions"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.collect::<Vec<String>>().join("")` on iterators.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.collect::<String>()` is more concise and might be more performant
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vector = vec!["hello",  "world"];
 +    /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
 +    /// println!("{}", output);
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let vector = vec!["hello",  "world"];
 +    /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
 +    /// println!("{}", output);
 +    /// ```
 +    /// ### Known problems
 +    /// While `.collect::<String>()` is sometimes more performant, there are cases where
 +    /// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")`
 +    /// will prevent loop unrolling and will result in a negative performance impact.
 +    ///
-                 unnecessary_to_owned::check(cx, expr, method_call.ident.name, args);
++    /// Additionally, differences have been observed between aarch64 and x86_64 assembly output,
 +    /// with aarch64 tending to producing faster assembly in more cases when using `.collect::<String>()`
 +    #[clippy::version = "1.61.0"]
 +    pub UNNECESSARY_JOIN,
 +    pedantic,
 +    "using `.collect::<Vec<String>>().join(\"\")` on an iterator"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for no-op uses of `Option::{as_deref, as_deref_mut}`,
 +    /// for example, `Option<&T>::as_deref()` returns the same type.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code and improving readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = Some(&1);
 +    /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
 +    /// ```
 +    /// Could be written as:
 +    /// ```rust
 +    /// let a = Some(&1);
 +    /// let b = a;
 +    /// ```
 +    #[clippy::version = "1.57.0"]
 +    pub NEEDLESS_OPTION_AS_DEREF,
 +    complexity,
 +    "no-op use of `deref` or `deref_mut` method to `Option`."
 +}
 +
++declare_clippy_lint! {
++    /// ### What it does
++    /// Finds usages of [`char::is_digit`]
++    /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that
++    /// can be replaced with [`is_ascii_digit`]
++    /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or
++    /// [`is_ascii_hexdigit`]
++    /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit).
++    ///
++    /// ### Why is this bad?
++    /// `is_digit(..)` is slower and requires specifying the radix.
++    ///
++    /// ### Example
++    /// ```rust
++    /// let c: char = '6';
++    /// c.is_digit(10);
++    /// c.is_digit(16);
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// let c: char = '6';
++    /// c.is_ascii_digit();
++    /// c.is_ascii_hexdigit();
++    /// ```
++    #[clippy::version = "1.61.0"]
++    pub IS_DIGIT_ASCII_RADIX,
++    style,
++    "use of `char::is_digit(..)` with literal radix of 10 or 16"
++}
++
++declare_clippy_lint! {
++    ///
++    /// ### Why is this bad?
++    ///
++    /// ### Example
++    /// ```rust
++    /// let x = Some(3);
++    /// x.as_ref().take();
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// let x = Some(3);
++    /// x.as_ref();
++    /// ```
++    #[clippy::version = "1.61.0"]
++    pub NEEDLESS_OPTION_TAKE,
++    complexity,
++    "using `.as_ref().take()` on a temporary value"
++}
++
 +pub struct Methods {
 +    avoid_breaking_exported_api: bool,
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl Methods {
 +    #[must_use]
 +    pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Self {
 +        Self {
 +            avoid_breaking_exported_api,
 +            msrv,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(Methods => [
 +    UNWRAP_USED,
 +    EXPECT_USED,
 +    SHOULD_IMPLEMENT_TRAIT,
 +    WRONG_SELF_CONVENTION,
 +    OK_EXPECT,
 +    UNWRAP_OR_ELSE_DEFAULT,
 +    MAP_UNWRAP_OR,
 +    RESULT_MAP_OR_INTO_OPTION,
 +    OPTION_MAP_OR_NONE,
 +    BIND_INSTEAD_OF_MAP,
 +    OR_FUN_CALL,
 +    OR_THEN_UNWRAP,
 +    EXPECT_FUN_CALL,
 +    CHARS_NEXT_CMP,
 +    CHARS_LAST_CMP,
 +    CLONE_ON_COPY,
 +    CLONE_ON_REF_PTR,
 +    CLONE_DOUBLE_REF,
 +    ITER_OVEREAGER_CLONED,
 +    CLONED_INSTEAD_OF_COPIED,
 +    FLAT_MAP_OPTION,
 +    INEFFICIENT_TO_STRING,
 +    NEW_RET_NO_SELF,
 +    SINGLE_CHAR_PATTERN,
 +    SINGLE_CHAR_ADD_STR,
 +    SEARCH_IS_SOME,
 +    FILTER_NEXT,
 +    SKIP_WHILE_NEXT,
 +    FILTER_MAP_IDENTITY,
 +    MAP_IDENTITY,
 +    MANUAL_FILTER_MAP,
 +    MANUAL_FIND_MAP,
 +    OPTION_FILTER_MAP,
 +    FILTER_MAP_NEXT,
 +    FLAT_MAP_IDENTITY,
 +    MAP_FLATTEN,
 +    ITERATOR_STEP_BY_ZERO,
 +    ITER_NEXT_SLICE,
 +    ITER_COUNT,
 +    ITER_NTH,
 +    ITER_NTH_ZERO,
 +    BYTES_NTH,
 +    ITER_SKIP_NEXT,
 +    GET_UNWRAP,
 +    STRING_EXTEND_CHARS,
 +    ITER_CLONED_COLLECT,
 +    ITER_WITH_DRAIN,
 +    USELESS_ASREF,
 +    UNNECESSARY_FOLD,
 +    UNNECESSARY_FILTER_MAP,
 +    UNNECESSARY_FIND_MAP,
 +    INTO_ITER_ON_REF,
 +    SUSPICIOUS_MAP,
 +    UNINIT_ASSUMED_INIT,
 +    MANUAL_SATURATING_ARITHMETIC,
 +    ZST_OFFSET,
 +    FILETYPE_IS_FILE,
 +    OPTION_AS_REF_DEREF,
 +    UNNECESSARY_LAZY_EVALUATIONS,
 +    MAP_COLLECT_RESULT_UNIT,
 +    FROM_ITER_INSTEAD_OF_COLLECT,
 +    INSPECT_FOR_EACH,
 +    IMPLICIT_CLONE,
 +    SUSPICIOUS_SPLITN,
 +    MANUAL_STR_REPEAT,
 +    EXTEND_WITH_DRAIN,
 +    MANUAL_SPLIT_ONCE,
 +    NEEDLESS_SPLITN,
 +    UNNECESSARY_TO_OWNED,
 +    UNNECESSARY_JOIN,
 +    ERR_EXPECT,
 +    NEEDLESS_OPTION_AS_DEREF,
++    IS_DIGIT_ASCII_RADIX,
++    NEEDLESS_OPTION_TAKE,
 +]);
 +
 +/// 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;
 +        }
 +
 +        check_methods(cx, expr, self.msrv.as_ref());
 +
 +        match expr.kind {
 +            hir::ExprKind::Call(func, args) => {
 +                from_iter_instead_of_collect::check(cx, expr, args, func);
 +            },
 +            hir::ExprKind::MethodCall(method_call, 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);
-                     if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) {
-                         str_splitn::check_manual_split_once(cx, name, expr, recv, pat_arg);
-                     }
-                     if count >= 2 {
-                         str_splitn::check_needless_splitn(cx, name, expr, recv, pat_arg, count);
-                     }
++                unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv.as_ref());
 +            },
 +            hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
 +                let mut info = BinaryExprInfo {
 +                    expr,
 +                    chain: lhs,
 +                    other: rhs,
 +                    eq: op.node == hir::BinOpKind::Eq,
 +                };
 +                lint_binary_expr_with_method_call(cx, &mut info);
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    #[allow(clippy::too_many_lines)]
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
 +        if in_external_macro(cx.sess(), impl_item.span) {
 +            return;
 +        }
 +        let name = impl_item.ident.name.as_str();
 +        let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id());
 +        let item = cx.tcx.hir().expect_item(parent);
 +        let self_ty = cx.tcx.type_of(item.def_id);
 +
 +        let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }));
 +        if_chain! {
 +            if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind;
 +            if let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next();
 +
 +            let method_sig = cx.tcx.fn_sig(impl_item.def_id);
 +            let method_sig = cx.tcx.erase_late_bound_regions(method_sig);
 +
 +            let first_arg_ty = method_sig.inputs().iter().next();
 +
 +            // check conventions w.r.t. conversion method names and predicates
 +            if let Some(first_arg_ty) = first_arg_ty;
 +
 +            then {
 +                // if this impl block implements a trait, lint in trait definition instead
 +                if !implements_trait && cx.access_levels.is_exported(impl_item.def_id) {
 +                    // check missing trait implementations
 +                    for method_config in &TRAIT_METHODS {
 +                        if name == method_config.method_name &&
 +                            sig.decl.inputs.len() == method_config.param_count &&
 +                            method_config.output_type.matches(&sig.decl.output) &&
 +                            method_config.self_kind.matches(cx, self_ty, *first_arg_ty) &&
 +                            fn_header_equals(method_config.fn_header, sig.header) &&
 +                            method_config.lifetime_param_cond(impl_item)
 +                        {
 +                            span_lint_and_help(
 +                                cx,
 +                                SHOULD_IMPLEMENT_TRAIT,
 +                                impl_item.span,
 +                                &format!(
 +                                    "method `{}` can be confused for the standard trait method `{}::{}`",
 +                                    method_config.method_name,
 +                                    method_config.trait_name,
 +                                    method_config.method_name
 +                                ),
 +                                None,
 +                                &format!(
 +                                    "consider implementing the trait `{}` or choosing a less ambiguous method name",
 +                                    method_config.trait_name
 +                                )
 +                            );
 +                        }
 +                    }
 +                }
 +
 +                if sig.decl.implicit_self.has_implicit_self()
 +                    && !(self.avoid_breaking_exported_api
 +                        && cx.access_levels.is_exported(impl_item.def_id))
 +                {
 +                    wrong_self_convention::check(
 +                        cx,
 +                        name,
 +                        self_ty,
 +                        *first_arg_ty,
 +                        first_arg.pat.span,
 +                        implements_trait,
 +                        false
 +                    );
 +                }
 +            }
 +        }
 +
 +        // if this impl block implements a trait, lint in trait definition instead
 +        if implements_trait {
 +            return;
 +        }
 +
 +        if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
 +            let ret_ty = return_ty(cx, impl_item.hir_id());
 +
 +            // walk the return type and check for Self (this does not check associated types)
 +            if let Some(self_adt) = self_ty.ty_adt_def() {
 +                if contains_adt_constructor(ret_ty, self_adt) {
 +                    return;
 +                }
 +            } else if contains_ty(ret_ty, self_ty) {
 +                return;
 +            }
 +
 +            // if return type is impl trait, check the associated types
 +            if let ty::Opaque(def_id, _) = *ret_ty.kind() {
 +                // one of the associated types must be Self
 +                for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
 +                    if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
 +                        let assoc_ty = match projection_predicate.term {
 +                            ty::Term::Ty(ty) => ty,
 +                            ty::Term::Const(_c) => continue,
 +                        };
 +                        // walk the associated type and check for Self
 +                        if let Some(self_adt) = self_ty.ty_adt_def() {
 +                            if contains_adt_constructor(assoc_ty, self_adt) {
 +                                return;
 +                            }
 +                        } else if contains_ty(assoc_ty, self_ty) {
 +                            return;
 +                        }
 +                    }
 +                }
 +            }
 +
 +            if name == "new" && ret_ty != self_ty {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    impl_item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if in_external_macro(cx.tcx.sess, item.span) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let TraitItemKind::Fn(ref sig, _) = item.kind;
 +            if sig.decl.implicit_self.has_implicit_self();
 +            if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
 +
 +            then {
 +                let first_arg_span = first_arg_ty.span;
 +                let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
 +                let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
 +                wrong_self_convention::check(
 +                    cx,
 +                    item.ident.name.as_str(),
 +                    self_ty,
 +                    first_arg_ty,
 +                    first_arg_span,
 +                    false,
 +                    true
 +                );
 +            }
 +        }
 +
 +        if_chain! {
 +            if item.ident.name == sym::new;
 +            if let TraitItemKind::Fn(_, _) = item.kind;
 +            let ret_ty = return_ty(cx, item.hir_id());
 +            let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
 +            if !contains_ty(ret_ty, self_ty);
 +
 +            then {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) {
 +    if let Some((name, [recv, args @ ..], span)) = method_call(expr) {
 +        match (name, args) {
 +            ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
 +                zst_offset::check(cx, expr, recv);
 +            },
 +            ("and_then", [arg]) => {
 +                let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg);
 +                let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg);
 +                if !biom_option_linted && !biom_result_linted {
 +                    unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
 +                }
 +            },
 +            ("as_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, 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(msrv, &msrvs::STR_REPEAT) {
 +                        manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
 +                    }
 +                },
 +                _ => {},
 +            },
 +            (name @ "count", args @ []) => match method_call(recv) {
 +                Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
 +                Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
 +                    iter_count::check(cx, expr, recv2, name2);
 +                },
 +                Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
 +                _ => {},
 +            },
 +            ("drain", [arg]) => {
 +                iter_with_drain::check(cx, expr, recv, span, arg);
 +            },
 +            ("expect", [_]) => match method_call(recv) {
 +                Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
 +                Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, msrv, span, err_span),
 +                _ => expect_used::check(cx, expr, recv),
 +            },
 +            ("extend", [arg]) => {
 +                string_extend_chars::check(cx, expr, recv, arg);
 +                extend_with_drain::check(cx, expr, recv, arg);
 +            },
 +            ("filter_map", [arg]) => {
 +                unnecessary_filter_map::check(cx, expr, arg, name);
 +                filter_map_identity::check(cx, expr, arg, span);
 +            },
 +            ("find_map", [arg]) => {
 +                unnecessary_filter_map::check(cx, expr, arg, name);
 +            },
 +            ("flat_map", [arg]) => {
 +                flat_map_identity::check(cx, expr, arg, span);
 +                flat_map_option::check(cx, expr, arg, span);
 +            },
 +            (name @ "flatten", args @ []) => match method_call(recv) {
 +                Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
 +                Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
 +                _ => {},
 +            },
 +            ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
 +            ("for_each", [_]) => {
 +                if let Some(("inspect", [_, _], span2)) = method_call(recv) {
 +                    inspect_for_each::check(cx, expr, span2);
 +                }
 +            },
 +            ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
 +            ("is_file", []) => filetype_is_file::check(cx, expr, recv),
++            ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, msrv),
 +            ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
 +            ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
 +            ("join", [join_arg]) => {
 +                if let Some(("collect", _, span)) = method_call(recv) {
 +                    unnecessary_join::check(cx, expr, recv, join_arg, span);
 +                }
 +            },
 +            ("last", args @ []) | ("skip", args @ [_]) => {
 +                if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
 +                    if let ("cloned", []) = (name2, args2) {
 +                        iter_overeager_cloned::check(cx, expr, recv2, name, args);
 +                    }
 +                }
 +            },
 +            (name @ ("map" | "map_err"), [m_arg]) => {
 +                if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
 +                    match (name, args) {
 +                        ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv),
 +                        ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv),
 +                        ("filter", [f_arg]) => {
 +                            filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
 +                        },
 +                        ("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true),
 +                        _ => {},
 +                    }
 +                }
 +                map_identity::check(cx, expr, recv, m_arg, name, span);
 +            },
 +            ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
 +            (name @ "next", args @ []) => {
 +                if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) {
 +                    match (name2, args2) {
 +                        ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
 +                        ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
 +                        ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, msrv),
 +                        ("iter", []) => iter_next_slice::check(cx, expr, recv2),
 +                        ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
 +                        ("skip_while", [_]) => skip_while_next::check(cx, expr),
 +                        _ => {},
 +                    }
 +                }
 +            },
 +            ("nth", args @ [n_arg]) => match method_call(recv) {
 +                Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
 +                Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
 +                Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
 +                Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
 +                _ => iter_nth_zero::check(cx, expr, recv, n_arg),
 +            },
 +            ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
 +            ("or_else", [arg]) => {
 +                if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
 +                    unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
 +                }
 +            },
 +            ("splitn" | "rsplitn", [count_arg, pat_arg]) => {
 +                if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
 +                    suspicious_splitn::check(cx, name, expr, recv, count);
++                    str_splitn::check(cx, name, expr, recv, pat_arg, count, msrv);
 +                }
 +            },
 +            ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
 +                if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
 +                    suspicious_splitn::check(cx, name, expr, recv, count);
 +                }
 +            },
 +            ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
 +            ("take", args @ [_arg]) => {
 +                if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
 +                    if let ("cloned", []) = (name2, args2) {
 +                        iter_overeager_cloned::check(cx, expr, recv2, name, args);
 +                    }
 +                }
 +            },
++            ("take", []) => needless_option_take::check(cx, expr, recv),
 +            ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
 +                implicit_clone::check(cx, name, expr, recv);
 +            },
 +            ("unwrap", []) => {
 +                match method_call(recv) {
 +                    Some(("get", [recv, get_arg], _)) => {
 +                        get_unwrap::check(cx, expr, recv, get_arg, false);
 +                    },
 +                    Some(("get_mut", [recv, get_arg], _)) => {
 +                        get_unwrap::check(cx, expr, recv, get_arg, true);
 +                    },
 +                    Some(("or", [recv, or_arg], or_span)) => {
 +                        or_then_unwrap::check(cx, expr, recv, or_arg, or_span);
 +                    },
 +                    _ => {},
 +                }
 +                unwrap_used::check(cx, expr, recv);
 +            },
 +            ("unwrap_or", [u_arg]) => match method_call(recv) {
 +                Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
 +                    manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
 +                },
 +                Some(("map", [m_recv, m_arg], span)) => {
 +                    option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
 +                },
 +                _ => {},
 +            },
 +            ("unwrap_or_else", [u_arg]) => match method_call(recv) {
 +                Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {},
 +                _ => {
 +                    unwrap_or_else_default::check(cx, expr, recv, u_arg);
 +                    unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
 +                },
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
 +    if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call(recv) {
 +        search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
 +    }
 +}
 +
 +/// Used for `lint_binary_expr_with_method_call`.
 +#[derive(Copy, Clone)]
 +struct BinaryExprInfo<'a> {
 +    expr: &'a hir::Expr<'a>,
 +    chain: &'a hir::Expr<'a>,
 +    other: &'a hir::Expr<'a>,
 +    eq: bool,
 +}
 +
 +/// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
 +fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) {
 +    macro_rules! lint_with_both_lhs_and_rhs {
 +        ($func:expr, $cx:expr, $info:ident) => {
 +            if !$func($cx, $info) {
 +                ::std::mem::swap(&mut $info.chain, &mut $info.other);
 +                if $func($cx, $info) {
 +                    return;
 +                }
 +            }
 +        };
 +    }
 +
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info);
 +}
 +
 +const FN_HEADER: hir::FnHeader = hir::FnHeader {
 +    unsafety: hir::Unsafety::Normal,
 +    constness: hir::Constness::NotConst,
 +    asyncness: hir::IsAsync::NotAsync,
 +    abi: rustc_target::spec::abi::Abi::Rust,
 +};
 +
 +struct ShouldImplTraitCase {
 +    trait_name: &'static str,
 +    method_name: &'static str,
 +    param_count: usize,
 +    fn_header: hir::FnHeader,
 +    // implicit self kind expected (none, self, &self, ...)
 +    self_kind: SelfKind,
 +    // checks against the output type
 +    output_type: OutType,
 +    // certain methods with explicit lifetimes can't implement the equivalent trait method
 +    lint_explicit_lifetime: bool,
 +}
 +impl ShouldImplTraitCase {
 +    const fn new(
 +        trait_name: &'static str,
 +        method_name: &'static str,
 +        param_count: usize,
 +        fn_header: hir::FnHeader,
 +        self_kind: SelfKind,
 +        output_type: OutType,
 +        lint_explicit_lifetime: bool,
 +    ) -> ShouldImplTraitCase {
 +        ShouldImplTraitCase {
 +            trait_name,
 +            method_name,
 +            param_count,
 +            fn_header,
 +            self_kind,
 +            output_type,
 +            lint_explicit_lifetime,
 +        }
 +    }
 +
 +    fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool {
 +        self.lint_explicit_lifetime
 +            || !impl_item.generics.params.iter().any(|p| {
 +                matches!(
 +                    p.kind,
 +                    hir::GenericParamKind::Lifetime {
 +                        kind: hir::LifetimeParamKind::Explicit
 +                    }
 +                )
 +            })
 +    }
 +}
 +
 +#[rustfmt::skip]
 +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
 +    ShouldImplTraitCase::new("std::ops::Add", "add",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::convert::AsMut", "as_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::convert::AsRef", "as_ref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::BitAnd", "bitand",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitOr", "bitor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitXor", "bitxor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::borrow::Borrow", "borrow",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::clone::Clone", "clone",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::cmp::Ord", "cmp",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    // FIXME: default doesn't work
 +    ShouldImplTraitCase::new("std::default::Default", "default",  0,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Deref", "deref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::Div", "div",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Drop", "drop",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Unit, true),
 +    ShouldImplTraitCase::new("std::cmp::PartialEq", "eq",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Bool, true),
 +    ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::str::FromStr", "from_str",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::hash::Hash", "hash",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Unit, true),
 +    ShouldImplTraitCase::new("std::ops::Index", "index",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut",  2,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Mul", "mul",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Neg", "neg",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::iter::Iterator", "next",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Any, false),
 +    ShouldImplTraitCase::new("std::ops::Not", "not",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Rem", "rem",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Shl", "shl",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Shr", "shr",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Sub", "sub",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +];
 +
 +#[derive(Clone, Copy, PartialEq, Debug)]
 +enum SelfKind {
 +    Value,
 +    Ref,
 +    RefMut,
 +    No,
 +}
 +
 +impl SelfKind {
 +    fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +        fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'_>, ty: Ty<'_>) -> bool {
 +            if ty == parent_ty {
 +                true
 +            } else if ty.is_box() {
 +                ty.boxed_ty() == parent_ty
 +            } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
 +                if let ty::Adt(_, substs) = ty.kind() {
 +                    substs.types().next().map_or(false, |t| t == parent_ty)
 +                } else {
 +                    false
 +                }
 +            } else {
 +                false
 +            }
 +        }
 +
 +        fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +            if let ty::Ref(_, t, m) = *ty.kind() {
 +                return m == mutability && t == parent_ty;
 +            }
 +
 +            let trait_path = match mutability {
 +                hir::Mutability::Not => &paths::ASREF_TRAIT,
 +                hir::Mutability::Mut => &paths::ASMUT_TRAIT,
 +            };
 +
 +            let trait_def_id = match get_trait_def_id(cx, trait_path) {
 +                Some(did) => did,
 +                None => return false,
 +            };
 +            implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
 +        }
 +
 +        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..829c118d2916386974ab7835e8be9fe72a2bf5c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::match_def_path;
++use clippy_utils::source::snippet_with_applicability;
++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::NEEDLESS_OPTION_TAKE;
++
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
++    // Checks if expression type is equal to sym::Option and if the expr is not a syntactic place
++    if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) && has_expr_as_ref_path(cx, recv) {
++        let mut applicability = Applicability::MachineApplicable;
++        span_lint_and_sugg(
++            cx,
++            NEEDLESS_OPTION_TAKE,
++            expr.span,
++            "called `Option::take()` on a temporary value",
++            "try",
++            format!(
++                "{}",
++                snippet_with_applicability(cx, recv.span, "..", &mut applicability)
++            ),
++            applicability,
++        );
++    }
++}
++
++fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
++    let expr_type = cx.typeck_results().expr_ty(expr);
++    is_type_diagnostic_item(cx, expr_type, sym::Option)
++}
++
++fn has_expr_as_ref_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
++    if let Some(ref_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
++        return match_def_path(cx, ref_id, &["core", "option", "Option", "as_ref"]);
++    }
++    false
++}
index 8125930b3046144053a7ab4bf8e4063e2ef42675,0000000000000000000000000000000000000000..52891eeed0696db2031bcb2fcb67943972c31036
mode 100644,000000..100644
--- /dev/null
@@@ -1,323 -1,0 +1,390 @@@
- use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::consts::{constant, Constant};
- use clippy_utils::{is_diag_item_method, match_def_path, paths};
++use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet_with_context;
- use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath};
++use clippy_utils::usage::local_used_after_expr;
++use clippy_utils::visitors::expr_visitor;
++use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
- use rustc_middle::ty::{self, adjustment::Adjust};
- use rustc_span::{symbol::sym, Span, SyntaxContext};
++use rustc_hir::intravisit::Visitor;
++use rustc_hir::{
++    BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind,
++};
 +use rustc_lint::LateContext;
- use super::MANUAL_SPLIT_ONCE;
++use rustc_middle::ty;
++use rustc_semver::RustcVersion;
++use rustc_span::{sym, Span, Symbol, SyntaxContext};
 +
- pub(super) fn check_manual_split_once(
++use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN};
 +
-     if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
++pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    method_name: &str,
 +    expr: &Expr<'_>,
 +    self_arg: &Expr<'_>,
 +    pat_arg: &Expr<'_>,
++    count: u128,
++    msrv: Option<&RustcVersion>,
 +) {
-     let (method_name, msg, reverse) = if method_name == "splitn" {
-         ("split_once", "manual implementation of `split_once`", false)
++    if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
 +        return;
 +    }
 +
++    let needless = |usage_kind| match usage_kind {
++        IterUsageKind::Nth(n) => count > n + 1,
++        IterUsageKind::NextTuple => count > 2,
++    };
++    let manual = count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE);
++
++    match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) {
++        Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg),
++        Some(usage) if manual => check_manual_split_once(cx, method_name, expr, self_arg, pat_arg, &usage),
++        None if manual => {
++            check_manual_split_once_indirect(cx, method_name, expr, self_arg, pat_arg);
++        },
++        _ => {},
++    }
++}
++
++fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) {
++    let mut app = Applicability::MachineApplicable;
++    let r = if method_name == "splitn" { "" } else { "r" };
++
++    span_lint_and_sugg(
++        cx,
++        NEEDLESS_SPLITN,
++        expr.span,
++        &format!("unnecessary use of `{r}splitn`"),
++        "try this",
++        format!(
++            "{}.{r}split({})",
++            snippet_with_context(cx, self_arg.span, expr.span.ctxt(), "..", &mut app).0,
++            snippet_with_context(cx, pat_arg.span, expr.span.ctxt(), "..", &mut app).0,
++        ),
++        app,
++    );
++}
++
++fn check_manual_split_once(
++    cx: &LateContext<'_>,
++    method_name: &str,
++    expr: &Expr<'_>,
++    self_arg: &Expr<'_>,
++    pat_arg: &Expr<'_>,
++    usage: &IterUsage,
++) {
 +    let ctxt = expr.span.ctxt();
-         ("rsplit_once", "manual implementation of `rsplit_once`", true)
-     };
-     let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) {
-         Some(x) => x,
-         None => return,
++    let (msg, reverse) = if method_name == "splitn" {
++        ("manual implementation of `split_once`", false)
 +    } else {
-             format!("{}.{}({})", self_snip, method_name, pat_snip)
-         },
-         IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip),
-         IterUsageKind::Next | IterUsageKind::Second => {
-             let self_deref = {
-                 let adjust = cx.typeck_results().expr_adjustments(self_arg);
-                 if adjust.len() < 2 {
-                     String::new()
-                 } else if cx.typeck_results().expr_ty(self_arg).is_box()
-                     || adjust
-                         .iter()
-                         .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box())
-                 {
-                     format!("&{}", "*".repeat(adjust.len().saturating_sub(1)))
-                 } else {
-                     "*".repeat(adjust.len().saturating_sub(2))
-                 }
-             };
-             if matches!(usage.kind, IterUsageKind::Next) {
-                 match usage.unwrap_kind {
-                     Some(UnwrapKind::Unwrap) => {
-                         if reverse {
-                             format!("{}.{}({}).unwrap().0", self_snip, method_name, pat_snip)
-                         } else {
-                             format!(
-                                 "{}.{}({}).map_or({}{}, |x| x.0)",
-                                 self_snip, method_name, pat_snip, self_deref, &self_snip
-                             )
-                         }
-                     },
-                     Some(UnwrapKind::QuestionMark) => {
-                         format!(
-                             "{}.{}({}).map_or({}{}, |x| x.0)",
-                             self_snip, method_name, pat_snip, self_deref, &self_snip
-                         )
-                     },
-                     None => {
-                         format!(
-                             "Some({}.{}({}).map_or({}{}, |x| x.0))",
-                             &self_snip, method_name, pat_snip, self_deref, &self_snip
-                         )
-                     },
-                 }
++        ("manual implementation of `rsplit_once`", true)
 +    };
 +
 +    let mut app = Applicability::MachineApplicable;
 +    let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
 +    let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
 +
 +    let sugg = match usage.kind {
 +        IterUsageKind::NextTuple => {
-                 match usage.unwrap_kind {
-                     Some(UnwrapKind::Unwrap) => {
-                         if reverse {
-                             // In this case, no better suggestion is offered.
-                             return;
-                         }
-                         format!("{}.{}({}).unwrap().1", self_snip, method_name, pat_snip)
-                     },
-                     Some(UnwrapKind::QuestionMark) => {
-                         format!("{}.{}({})?.1", self_snip, method_name, pat_snip)
-                     },
-                     None => {
-                         format!("{}.{}({}).map(|x| x.1)", self_snip, method_name, pat_snip)
-                     },
-                 }
++            if reverse {
++                format!("{self_snip}.rsplit_once({pat_snip}).map(|(x, y)| (y, x))")
 +            } else {
-     Next,
-     Second,
++                format!("{self_snip}.split_once({pat_snip})")
++            }
++        },
++        IterUsageKind::Nth(1) => {
++            let (r, field) = if reverse { ("r", 0) } else { ("", 1) };
++
++            match usage.unwrap_kind {
++                Some(UnwrapKind::Unwrap) => {
++                    format!("{self_snip}.{r}split_once({pat_snip}).unwrap().{field}")
++                },
++                Some(UnwrapKind::QuestionMark) => {
++                    format!("{self_snip}.{r}split_once({pat_snip})?.{field}")
++                },
++                None => {
++                    format!("{self_snip}.{r}split_once({pat_snip}).map(|x| x.{field})")
++                },
 +            }
 +        },
++        IterUsageKind::Nth(_) => return,
 +    };
 +
 +    span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
 +}
 +
++/// checks for
++///
++/// ```
++/// let mut iter = "a.b.c".splitn(2, '.');
++/// let a = iter.next();
++/// let b = iter.next();
++/// ```
++fn check_manual_split_once_indirect(
++    cx: &LateContext<'_>,
++    method_name: &str,
++    expr: &Expr<'_>,
++    self_arg: &Expr<'_>,
++    pat_arg: &Expr<'_>,
++) -> Option<()> {
++    let ctxt = expr.span.ctxt();
++    let mut parents = cx.tcx.hir().parent_iter(expr.hir_id);
++    if let (_, Node::Local(local)) = parents.next()?
++        && let PatKind::Binding(BindingAnnotation::Mutable, iter_binding_id, iter_ident, None) = local.pat.kind
++        && let (iter_stmt_id, Node::Stmt(_)) = parents.next()?
++        && let (_, Node::Block(enclosing_block)) = parents.next()?
++
++        && let mut stmts = enclosing_block
++            .stmts
++            .iter()
++            .skip_while(|stmt| stmt.hir_id != iter_stmt_id)
++            .skip(1)
++
++        && let first = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)?
++        && let second = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)?
++        && first.unwrap_kind == second.unwrap_kind
++        && first.name != second.name
++        && !local_used_after_expr(cx, iter_binding_id, second.init_expr)
++    {
++        let (r, lhs, rhs) = if method_name == "splitn" {
++            ("", first.name, second.name)
++        } else {
++            ("r", second.name, first.name)
++        };
++        let msg = format!("manual implementation of `{r}split_once`");
++
++        let mut app = Applicability::MachineApplicable;
++        let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
++        let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
++
++        span_lint_and_then(cx, MANUAL_SPLIT_ONCE, local.span, &msg, |diag| {
++            diag.span_label(first.span, "first usage here");
++            diag.span_label(second.span, "second usage here");
++
++            let unwrap = match first.unwrap_kind {
++                UnwrapKind::Unwrap => ".unwrap()",
++                UnwrapKind::QuestionMark => "?",
++            };
++            diag.span_suggestion_verbose(
++                local.span,
++                &format!("try `{r}split_once`"),
++                format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"),
++                app,
++            );
++
++            let remove_msg = format!("remove the `{iter_ident}` usages");
++            diag.span_suggestion(
++                first.span,
++                &remove_msg,
++                String::new(),
++                app,
++            );
++            diag.span_suggestion(
++                second.span,
++                &remove_msg,
++                String::new(),
++                app,
++            );
++        });
++    }
++
++    Some(())
++}
++
++#[derive(Debug)]
++struct IndirectUsage<'a> {
++    name: Symbol,
++    span: Span,
++    init_expr: &'a Expr<'a>,
++    unwrap_kind: UnwrapKind,
++}
++
++/// returns `Some(IndirectUsage)` for e.g.
++///
++/// ```ignore
++/// let name = binding.next()?;
++/// let name = binding.next().unwrap();
++/// ```
++fn indirect_usage<'tcx>(
++    cx: &LateContext<'tcx>,
++    stmt: &Stmt<'tcx>,
++    binding: HirId,
++    ctxt: SyntaxContext,
++) -> Option<IndirectUsage<'tcx>> {
++    if let StmtKind::Local(Local {
++        pat:
++            Pat {
++                kind: PatKind::Binding(BindingAnnotation::Unannotated, _, ident, None),
++                ..
++            },
++        init: Some(init_expr),
++        hir_id: local_hir_id,
++        ..
++    }) = stmt.kind
++    {
++        let mut path_to_binding = None;
++        expr_visitor(cx, |expr| {
++            if path_to_local_id(expr, binding) {
++                path_to_binding = Some(expr);
++            }
++
++            path_to_binding.is_none()
++        })
++        .visit_expr(init_expr);
++
++        let mut parents = cx.tcx.hir().parent_iter(path_to_binding?.hir_id);
++        let iter_usage = parse_iter_usage(cx, ctxt, &mut parents)?;
++
++        let (parent_id, _) = parents.find(|(_, node)| {
++            !matches!(
++                node,
++                Node::Expr(Expr {
++                    kind: ExprKind::Match(.., MatchSource::TryDesugar),
++                    ..
++                })
++            )
++        })?;
++
++        if let IterUsage {
++            kind: IterUsageKind::Nth(0),
++            unwrap_kind: Some(unwrap_kind),
++            ..
++        } = iter_usage
++        {
++            if parent_id == *local_hir_id {
++                return Some(IndirectUsage {
++                    name: ident.name,
++                    span: stmt.span,
++                    init_expr,
++                    unwrap_kind,
++                });
++            }
++        }
++    }
++
++    None
++}
++
++#[derive(Debug, Clone, Copy)]
 +enum IterUsageKind {
-     RNextTuple,
++    Nth(u128),
 +    NextTuple,
-     reverse: bool,
 +}
 +
++#[derive(Debug, PartialEq)]
 +enum UnwrapKind {
 +    Unwrap,
 +    QuestionMark,
 +}
 +
++#[derive(Debug)]
 +struct IterUsage {
 +    kind: IterUsageKind,
 +    unwrap_kind: Option<UnwrapKind>,
 +    span: Span,
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn parse_iter_usage<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ctxt: SyntaxContext,
 +    mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
-                 ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
-                     if reverse {
-                         (IterUsageKind::Second, e.span)
-                     } else {
-                         (IterUsageKind::Next, e.span)
-                     }
-                 },
 +) -> Option<IterUsage> {
 +    let (kind, span) = match iter.next() {
 +        Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
 +            let (name, args) = if let ExprKind::MethodCall(name, [_, args @ ..], _) = e.kind {
 +                (name, args)
 +            } else {
 +                return None;
 +            };
 +            let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
 +            let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
 +
 +            match (name.ident.as_str(), args) {
-                                 kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple },
++                ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span),
 +                ("next_tuple", []) => {
 +                    return if_chain! {
 +                        if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
 +                        if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind();
 +                        if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did());
 +                        if let ty::Tuple(subs) = subs.type_at(0).kind();
 +                        if subs.len() == 2;
 +                        then {
 +                            Some(IterUsage {
-                         match if reverse { idx ^ 1 } else { idx } {
-                             0 => (IterUsageKind::Next, span),
-                             1 => (IterUsageKind::Second, span),
-                             _ => return None,
-                         }
++                                kind: IterUsageKind::NextTuple,
 +                                span: e.span,
 +                                unwrap_kind: None
 +                            })
 +                        } else {
 +                            None
 +                        }
 +                    };
 +                },
 +                ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
 +                    if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
 +                        let span = if name.ident.as_str() == "nth" {
 +                            e.span
 +                        } else {
 +                            if_chain! {
 +                                if let Some((_, Node::Expr(next_expr))) = iter.next();
 +                                if let ExprKind::MethodCall(next_name, [_], _) = next_expr.kind;
 +                                if next_name.ident.name == sym::next;
 +                                if next_expr.span.ctxt() == ctxt;
 +                                if let Some(next_id) = cx.typeck_results().type_dependent_def_id(next_expr.hir_id);
 +                                if cx.tcx.trait_of_item(next_id) == Some(iter_id);
 +                                then {
 +                                    next_expr.span
 +                                } else {
 +                                    return None;
 +                                }
 +                            }
 +                        };
- use super::NEEDLESS_SPLITN;
- pub(super) fn check_needless_splitn(
-     cx: &LateContext<'_>,
-     method_name: &str,
-     expr: &Expr<'_>,
-     self_arg: &Expr<'_>,
-     pat_arg: &Expr<'_>,
-     count: u128,
- ) {
-     if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
-         return;
-     }
-     let ctxt = expr.span.ctxt();
-     let mut app = Applicability::MachineApplicable;
-     let (reverse, message) = if method_name == "splitn" {
-         (false, "unnecessary use of `splitn`")
-     } else {
-         (true, "unnecessary use of `rsplitn`")
-     };
-     if_chain! {
-         if count >= 2;
-         if check_iter(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), count);
-         then {
-             span_lint_and_sugg(
-                 cx,
-                 NEEDLESS_SPLITN,
-                 expr.span,
-                 message,
-                 "try this",
-                 format!(
-                     "{}.{}({})",
-                     snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0,
-                     if reverse {"rsplit"} else {"split"},
-                     snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0
-                 ),
-                 app,
-             );
-         }
-     }
- }
- fn check_iter<'tcx>(
-     cx: &LateContext<'tcx>,
-     ctxt: SyntaxContext,
-     mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
-     count: u128,
- ) -> bool {
-     match iter.next() {
-         Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
-             let (name, args) = if let ExprKind::MethodCall(name, [_, args @ ..], _) = e.kind {
-                 (name, args)
-             } else {
-                 return false;
-             };
-             if_chain! {
-                 if let Some(did) = cx.typeck_results().type_dependent_def_id(e.hir_id);
-                 if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
-                 then {
-                     match (name.ident.as_str(), args) {
-                         ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
-                             return true;
-                         },
-                         ("next_tuple", []) if count > 2 => {
-                             return true;
-                         },
-                         ("nth", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
-                             if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
-                                 if count > idx + 1 {
-                                     return true;
-                                 }
-                             }
-                         },
-                         _ =>  return false,
-                     }
-                 }
-             }
-         },
-         _ => return false,
-     };
-     false
- }
++                        (IterUsageKind::Nth(idx), span)
 +                    } else {
 +                        return None;
 +                    }
 +                },
 +                _ => return None,
 +            }
 +        },
 +        _ => return None,
 +    };
 +
 +    let (unwrap_kind, span) = if let Some((_, Node::Expr(e))) = iter.next() {
 +        match e.kind {
 +            ExprKind::Call(
 +                Expr {
 +                    kind: ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)),
 +                    ..
 +                },
 +                _,
 +            ) => {
 +                let parent_span = e.span.parent_callsite().unwrap();
 +                if parent_span.ctxt() == ctxt {
 +                    (Some(UnwrapKind::QuestionMark), parent_span)
 +                } else {
 +                    (None, span)
 +                }
 +            },
 +            _ if e.span.ctxt() != ctxt => (None, span),
 +            ExprKind::MethodCall(name, [_], _)
 +                if name.ident.name == sym::unwrap
 +                    && cx
 +                        .typeck_results()
 +                        .type_dependent_def_id(e.hir_id)
 +                        .map_or(false, |id| is_diag_item_method(cx, id, sym::Option)) =>
 +            {
 +                (Some(UnwrapKind::Unwrap), e.span)
 +            },
 +            _ => (None, span),
 +        }
 +    } else {
 +        (None, span)
 +    };
 +
 +    Some(IterUsage {
 +        kind,
 +        unwrap_kind,
 +        span,
 +    })
 +}
index 1555758fc4ad825b6b013a7bb1014544123f6804,0000000000000000000000000000000000000000..02b882e8b55e041bc14f7afc810a6451b6f24927
mode 100644,000000..100644
--- /dev/null
@@@ -1,406 -1,0 +1,421 @@@
- pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) {
 +use super::implicit_clone::is_clone_like;
 +use super::unnecessary_iter_cloned::{self, is_into_iter};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::{
 +    contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs,
 +};
++use clippy_utils::{meets_msrv, msrvs};
++
 +use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
 +use rustc_errors::Applicability;
 +use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::mir::Mutability;
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
 +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
 +use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
++use rustc_semver::RustcVersion;
 +use rustc_span::{sym, Symbol};
 +use std::cmp::max;
 +
 +use super::UNNECESSARY_TO_OWNED;
 +
-                 if check_into_iter_call_arg(cx, expr, method_name, receiver) {
++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;
 +                }
- fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool {
++                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 Some(target_ty) = match adjustments[..]
 +        {
 +            // For matching uses of `Cow::from`
 +            [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    target: target_ty,
 +                },
 +            ]
 +            // For matching uses of arrays
 +            | [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Pointer(_),
 +                    target: target_ty,
 +                },
 +            ]
 +            // For matching everything else
 +            | [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Deref(Some(OverloadedDeref { .. })),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    target: target_ty,
 +                },
 +            ] => Some(target_ty),
 +            _ => None,
 +        };
 +        let receiver_ty = cx.typeck_results().expr_ty(receiver);
 +        // Only flag cases where the receiver is copyable or the method is `Cow::into_owned`. This
 +        // restriction is to ensure there is not overlap between `redundant_clone` and this lint.
 +        if is_copy(cx, receiver_ty) || is_cow_into_owned(cx, method_name, method_def_id);
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty);
 +            let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty);
 +            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.
-             let cloned_or_copied = if is_copy(cx, item_ty) { "copied" } else { "cloned" };
++fn check_into_iter_call_arg(
++    cx: &LateContext<'_>,
++    expr: &Expr<'_>,
++    method_name: Symbol,
++    receiver: &Expr<'_>,
++    msrv: Option<&RustcVersion>,
++) -> bool {
 +    if_chain! {
 +        if let Some(parent) = get_parent_expr(cx, expr);
 +        if let Some(callee_def_id) = fn_def_id(cx, parent);
 +        if is_into_iter(cx, callee_def_id);
 +        if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
 +        let parent_ty = cx.typeck_results().expr_ty(parent);
 +        if implements_trait(cx, parent_ty, iterator_trait_id, &[]);
 +        if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty);
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
 +                return true;
 +            }
++            let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) { "copied" } else { "cloned" };
 +            // The next suggestion may be incorrect because the removal of the `to_owned`-like
 +            // function could cause the iterator to hold a reference to a resource that is used
 +            // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
 +            span_lint_and_sugg(
 +                cx,
 +                UNNECESSARY_TO_OWNED,
 +                parent.span,
 +                &format!("unnecessary use of `{}`", method_name),
 +                "use",
 +                format!("{}.iter().{}()", receiver_snippet, cloned_or_copied),
 +                Applicability::MaybeIncorrect,
 +            );
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks whether `expr` is an argument in a function call and, if so, determines whether its call
 +/// of a `to_owned`-like function is unnecessary.
 +fn check_other_call_arg<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    method_name: Symbol,
 +    receiver: &'tcx Expr<'tcx>,
 +) -> bool {
 +    if_chain! {
 +        if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr);
 +        if let Some((callee_def_id, call_substs, call_args)) = get_callee_substs_and_args(cx, maybe_call);
 +        let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
 +        if let Some(i) = call_args.iter().position(|arg| arg.hir_id == maybe_arg.hir_id);
 +        if let Some(input) = fn_sig.inputs().get(i);
 +        let (input, n_refs) = peel_mid_ty_refs(*input);
 +        if let (trait_predicates, projection_predicates) = get_input_traits_and_projections(cx, callee_def_id, input);
 +        if let Some(sized_def_id) = cx.tcx.lang_items().sized_trait();
 +        if let [trait_predicate] = trait_predicates
 +            .iter()
 +            .filter(|trait_predicate| trait_predicate.def_id() != sized_def_id)
 +            .collect::<Vec<_>>()[..];
 +        if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
 +        if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
 +        let receiver_ty = cx.typeck_results().expr_ty(receiver);
 +        // If the callee has type parameters, they could appear in `projection_predicate.ty` or the
 +        // types of `trait_predicate.trait_ref.substs`.
 +        if if trait_predicate.def_id() == deref_trait_id {
 +            if let [projection_predicate] = projection_predicates[..] {
 +                let normalized_ty =
 +                    cx.tcx
 +                        .subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.term);
 +                implements_trait(cx, receiver_ty, deref_trait_id, &[])
 +                    && get_associated_type(cx, receiver_ty, deref_trait_id, "Target")
 +                        .map_or(false, |ty| ty::Term::Ty(ty) == normalized_ty)
 +            } else {
 +                false
 +            }
 +        } else if trait_predicate.def_id() == as_ref_trait_id {
 +            let composed_substs = compose_substs(
 +                cx,
 +                &trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..],
 +                call_substs,
 +            );
 +            implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
 +        } else {
 +            false
 +        };
 +        // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match
 +        // `Target = T`.
 +        if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id;
 +        let n_refs = max(n_refs, if is_copy(cx, receiver_ty) { 0 } else { 1 });
 +        // If the trait is `AsRef` and the input type variable `T` occurs in the output type, then
 +        // `T` must not be instantiated with a reference
 +        // (https://github.com/rust-lang/rust-clippy/issues/8507).
 +        if (n_refs == 0 && !receiver_ty.is_ref())
 +            || trait_predicate.def_id() != as_ref_trait_id
 +            || !contains_ty(fn_sig.output(), input);
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                UNNECESSARY_TO_OWNED,
 +                maybe_arg.span,
 +                &format!("unnecessary use of `{}`", method_name),
 +                "use",
 +                format!("{:&>width$}{}", "", receiver_snippet, width = n_refs),
 +                Applicability::MachineApplicable,
 +            );
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +/// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such
 +/// expression found (if any) along with the immediately prior expression.
 +fn skip_addr_of_ancestors<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    mut expr: &'tcx Expr<'tcx>,
 +) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
 +    while let Some(parent) = get_parent_expr(cx, expr) {
 +        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind {
 +            expr = parent;
 +        } else {
 +            return Some((parent, expr));
 +        }
 +    }
 +    None
 +}
 +
 +/// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
 +/// `Substs`, and arguments.
 +fn get_callee_substs_and_args<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +) -> Option<(DefId, SubstsRef<'tcx>, &'tcx [Expr<'tcx>])> {
 +    if_chain! {
 +        if let ExprKind::Call(callee, args) = expr.kind;
 +        let callee_ty = cx.typeck_results().expr_ty(callee);
 +        if let ty::FnDef(callee_def_id, _) = callee_ty.kind();
 +        then {
 +            let substs = cx.typeck_results().node_substs(callee.hir_id);
 +            return Some((*callee_def_id, substs, args));
 +        }
 +    }
 +    if_chain! {
 +        if let ExprKind::MethodCall(_, args, _) = expr.kind;
 +        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
 +        then {
 +            let substs = cx.typeck_results().node_substs(expr.hir_id);
 +            return Some((method_def_id, substs, args));
 +        }
 +    }
 +    None
 +}
 +
 +/// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type.
 +fn get_input_traits_and_projections<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    callee_def_id: DefId,
 +    input: Ty<'tcx>,
 +) -> (Vec<TraitPredicate<'tcx>>, Vec<ProjectionPredicate<'tcx>>) {
 +    let mut trait_predicates = Vec::new();
 +    let mut projection_predicates = Vec::new();
 +    for (predicate, _) in cx.tcx.predicates_of(callee_def_id).predicates.iter() {
 +        // `substs` should have 1 + n elements. The first is the type on the left hand side of an
 +        // `as`. The remaining n are trait parameters.
 +        let is_input_substs = |substs: SubstsRef<'tcx>| {
 +            if_chain! {
 +                if let Some(arg) = substs.iter().next();
 +                if let GenericArgKind::Type(arg_ty) = arg.unpack();
 +                if arg_ty == input;
 +                then { true } else { false }
 +            }
 +        };
 +        match predicate.kind().skip_binder() {
 +            PredicateKind::Trait(trait_predicate) => {
 +                if is_input_substs(trait_predicate.trait_ref.substs) {
 +                    trait_predicates.push(trait_predicate);
 +                }
 +            },
 +            PredicateKind::Projection(projection_predicate) => {
 +                if is_input_substs(projection_predicate.projection_ty.substs) {
 +                    projection_predicates.push(projection_predicate);
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +    (trait_predicates, projection_predicates)
 +}
 +
 +/// Composes two substitutions by applying the latter to the types of the former.
 +fn compose_substs<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    left: &[GenericArg<'tcx>],
 +    right: SubstsRef<'tcx>,
 +) -> Vec<GenericArg<'tcx>> {
 +    left.iter()
 +        .map(|arg| {
 +            if let GenericArgKind::Type(arg_ty) = arg.unpack() {
 +                let normalized_ty = cx.tcx.subst_and_normalize_erasing_regions(right, cx.param_env, arg_ty);
 +                GenericArg::from(normalized_ty)
 +            } else {
 +                *arg
 +            }
 +        })
 +        .collect()
 +}
 +
 +/// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`.
 +fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
 +    (method_name.as_str() == "cloned" || method_name.as_str() == "copied")
 +        && is_diag_trait_item(cx, method_def_id, sym::Iterator)
 +}
 +
 +/// Returns true if the named method can be used to convert the receiver to its "owned"
 +/// representation.
 +fn is_to_owned_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
 +    is_clone_like(cx, &*method_name.as_str(), method_def_id)
 +        || is_cow_into_owned(cx, method_name, method_def_id)
 +        || is_to_string(cx, method_name, method_def_id)
 +}
 +
 +/// Returns true if the named method is `Cow::into_owned`.
 +fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
 +    method_name.as_str() == "into_owned" && is_diag_item_method(cx, method_def_id, sym::Cow)
 +}
 +
 +/// Returns true if the named method is `ToString::to_string`.
 +fn is_to_string(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
 +    method_name.as_str() == "to_string" && is_diag_trait_item(cx, method_def_id, sym::ToString)
 +}
index aecfea9c141cfad801b75fedc45472f76d0be272,0000000000000000000000000000000000000000..4b368d3ffae254abd04ba4727b7b30faed484ef3
mode 100644,000000..100644
--- /dev/null
@@@ -1,154 -1,0 +1,154 @@@
-     (&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]),
 +use crate::methods::SelfKind;
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::ty::is_copy;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::Ty;
 +use rustc_span::source_map::Span;
 +use std::fmt;
 +
 +use super::WRONG_SELF_CONVENTION;
 +
 +#[rustfmt::skip]
 +const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [
 +    (&[Convention::Eq("new")], &[SelfKind::No]),
 +    (&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]),
 +    (&[Convention::StartsWith("from_")], &[SelfKind::No]),
 +    (&[Convention::StartsWith("into_")], &[SelfKind::Value]),
++    (&[Convention::StartsWith("is_")], &[SelfKind::RefMut, SelfKind::Ref, SelfKind::No]),
 +    (&[Convention::Eq("to_mut")], &[SelfKind::RefMut]),
 +    (&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]),
 +
 +    // Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types).
 +    // Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
 +    (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false),
 +    Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Ref]),
 +    (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true),
 +    Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Value]),
 +];
 +
 +enum Convention {
 +    Eq(&'static str),
 +    StartsWith(&'static str),
 +    EndsWith(&'static str),
 +    NotEndsWith(&'static str),
 +    IsSelfTypeCopy(bool),
 +    ImplementsTrait(bool),
 +    IsTraitItem(bool),
 +}
 +
 +impl Convention {
 +    #[must_use]
 +    fn check<'tcx>(
 +        &self,
 +        cx: &LateContext<'tcx>,
 +        self_ty: Ty<'tcx>,
 +        other: &str,
 +        implements_trait: bool,
 +        is_trait_item: bool,
 +    ) -> bool {
 +        match *self {
 +            Self::Eq(this) => this == other,
 +            Self::StartsWith(this) => other.starts_with(this) && this != other,
 +            Self::EndsWith(this) => other.ends_with(this) && this != other,
 +            Self::NotEndsWith(this) => !Self::EndsWith(this).check(cx, self_ty, other, implements_trait, is_trait_item),
 +            Self::IsSelfTypeCopy(is_true) => is_true == is_copy(cx, self_ty),
 +            Self::ImplementsTrait(is_true) => is_true == implements_trait,
 +            Self::IsTraitItem(is_true) => is_true == is_trait_item,
 +        }
 +    }
 +}
 +
 +impl fmt::Display for Convention {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
 +        match *self {
 +            Self::Eq(this) => format!("`{}`", this).fmt(f),
 +            Self::StartsWith(this) => format!("`{}*`", this).fmt(f),
 +            Self::EndsWith(this) => format!("`*{}`", this).fmt(f),
 +            Self::NotEndsWith(this) => format!("`~{}`", this).fmt(f),
 +            Self::IsSelfTypeCopy(is_true) => {
 +                format!("`self` type is{} `Copy`", if is_true { "" } else { " not" }).fmt(f)
 +            },
 +            Self::ImplementsTrait(is_true) => {
 +                let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") };
 +                format!("method{} implement{} a trait", negation, s_suffix).fmt(f)
 +            },
 +            Self::IsTraitItem(is_true) => {
 +                let suffix = if is_true { " is" } else { " is not" };
 +                format!("method{} a trait item", suffix).fmt(f)
 +            },
 +        }
 +    }
 +}
 +
 +#[allow(clippy::too_many_arguments)]
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    item_name: &str,
 +    self_ty: Ty<'tcx>,
 +    first_arg_ty: Ty<'tcx>,
 +    first_arg_span: Span,
 +    implements_trait: bool,
 +    is_trait_item: bool,
 +) {
 +    if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| {
 +        convs
 +            .iter()
 +            .all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item))
 +    }) {
 +        // don't lint if it implements a trait but not willing to check `Copy` types conventions (see #7032)
 +        if implements_trait
 +            && !conventions
 +                .iter()
 +                .any(|conv| matches!(conv, Convention::IsSelfTypeCopy(_)))
 +        {
 +            return;
 +        }
 +        if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
 +            let suggestion = {
 +                if conventions.len() > 1 {
 +                    // Don't mention `NotEndsWith` when there is also `StartsWith` convention present
 +                    let cut_ends_with_conv = conventions.iter().any(|conv| matches!(conv, Convention::StartsWith(_)))
 +                        && conventions
 +                            .iter()
 +                            .any(|conv| matches!(conv, Convention::NotEndsWith(_)));
 +
 +                    let s = conventions
 +                        .iter()
 +                        .filter_map(|conv| {
 +                            if (cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_)))
 +                                || matches!(conv, Convention::ImplementsTrait(_))
 +                                || matches!(conv, Convention::IsTraitItem(_))
 +                            {
 +                                None
 +                            } else {
 +                                Some(conv.to_string())
 +                            }
 +                        })
 +                        .collect::<Vec<_>>()
 +                        .join(" and ");
 +
 +                    format!("methods with the following characteristics: ({})", &s)
 +                } else {
 +                    format!("methods called {}", &conventions[0])
 +                }
 +            };
 +
 +            span_lint_and_help(
 +                cx,
 +                WRONG_SELF_CONVENTION,
 +                first_arg_span,
 +                &format!(
 +                    "{} usually take {}",
 +                    suggestion,
 +                    &self_kinds
 +                        .iter()
 +                        .map(|k| k.description())
 +                        .collect::<Vec<_>>()
 +                        .join(" or ")
 +                ),
 +                None,
 +                "consider choosing a less ambiguous name",
 +            );
 +        }
 +    }
 +}
index d955fad7d41a2fb12295dc352f901dfa19c883bf,0000000000000000000000000000000000000000..6860b60acbdb4612e144604db5ddd53b146e5b46
mode 100644,000000..100644
--- /dev/null
@@@ -1,387 -1,0 +1,387 @@@
-             Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip,
 +mod builtin_type_shadow;
 +mod double_neg;
 +mod literal_suffix;
 +mod mixed_case_hex_literals;
 +mod redundant_pattern;
 +mod unneeded_field_pattern;
 +mod unneeded_wildcard_pattern;
 +mod zero_prefixed_literal;
 +
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::source::snippet_opt;
 +use rustc_ast::ast::{Expr, ExprKind, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind};
 +use rustc_ast::visit::FnKind;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for structure field patterns bound to wildcards.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `..` instead is shorter and leaves the focus on
 +    /// the fields that are actually bound.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct Foo {
 +    /// #     a: i32,
 +    /// #     b: i32,
 +    /// #     c: i32,
 +    /// # }
 +    /// let f = Foo { a: 0, b: 0, c: 0 };
 +    ///
 +    /// // Bad
 +    /// match f {
 +    ///     Foo { a: _, b: 0, .. } => {},
 +    ///     Foo { a: _, b: _, c: _ } => {},
 +    /// }
 +    ///
 +    /// // Good
 +    /// match f {
 +    ///     Foo { b: 0, .. } => {},
 +    ///     Foo { .. } => {},
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNNEEDED_FIELD_PATTERN,
 +    restriction,
 +    "struct fields bound to a wildcard instead of using `..`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for function arguments having the similar names
 +    /// differing by an underscore.
 +    ///
 +    /// ### Why is this bad?
 +    /// It affects code readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// fn foo(a: i32, _a: i32) {}
 +    ///
 +    /// // Good
 +    /// fn bar(a: i32, _b: i32) {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DUPLICATE_UNDERSCORE_ARGUMENT,
 +    style,
 +    "function arguments having names which only differ by an underscore"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects expressions of the form `--x`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It can mislead C/C++ programmers to think `x` was
 +    /// decremented.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut x = 3;
 +    /// --x;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DOUBLE_NEG,
 +    style,
 +    "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns on hexadecimal literals with mixed-case letter
 +    /// digits.
 +    ///
 +    /// ### Why is this bad?
 +    /// It looks confusing.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let y = 0x1a9BAcD;
 +    ///
 +    /// // Good
 +    /// let y = 0x1A9BACD;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MIXED_CASE_HEX_LITERALS,
 +    style,
 +    "hex literals whose letter digits are not consistently upper- or lowercased"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns if literal suffixes are not separated by an
 +    /// underscore.
 +    /// To enforce unseparated literal suffix style,
 +    /// see the `separated_literal_suffix` lint.
 +    ///
 +    /// ### Why is this bad?
 +    /// Suffix style should be consistent.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let y = 123832i32;
 +    ///
 +    /// // Good
 +    /// let y = 123832_i32;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNSEPARATED_LITERAL_SUFFIX,
 +    restriction,
 +    "literals whose suffix is not separated by an underscore"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns if literal suffixes are separated by an underscore.
 +    /// To enforce separated literal suffix style,
 +    /// see the `unseparated_literal_suffix` lint.
 +    ///
 +    /// ### Why is this bad?
 +    /// Suffix style should be consistent.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let y = 123832_i32;
 +    ///
 +    /// // Good
 +    /// let y = 123832i32;
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub SEPARATED_LITERAL_SUFFIX,
 +    restriction,
 +    "literals whose suffix is separated by an underscore"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns if an integral constant literal starts with `0`.
 +    ///
 +    /// ### Why is this bad?
 +    /// In some languages (including the infamous C language
 +    /// and most of its
 +    /// family), this marks an octal constant. In Rust however, this is a decimal
 +    /// constant. This could
 +    /// be confusing for both the writer and a reader of the constant.
 +    ///
 +    /// ### Example
 +    ///
 +    /// In Rust:
 +    /// ```rust
 +    /// fn main() {
 +    ///     let a = 0123;
 +    ///     println!("{}", a);
 +    /// }
 +    /// ```
 +    ///
 +    /// prints `123`, while in C:
 +    ///
 +    /// ```c
 +    /// #include <stdio.h>
 +    ///
 +    /// int main() {
 +    ///     int a = 0123;
 +    ///     printf("%d\n", a);
 +    /// }
 +    /// ```
 +    ///
 +    /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ZERO_PREFIXED_LITERAL,
 +    complexity,
 +    "integer literals starting with `0`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns if a generic shadows a built-in type.
 +    ///
 +    /// ### Why is this bad?
 +    /// This gives surprising type errors.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```ignore
 +    /// impl<u32> Foo<u32> {
 +    ///     fn impl_func(&self) -> u32 {
 +    ///         42
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub BUILTIN_TYPE_SHADOW,
 +    style,
 +    "shadowing a builtin type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for patterns in the form `name @ _`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's almost always more readable to just use direct
 +    /// bindings.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let v = Some("abc");
 +    ///
 +    /// // Bad
 +    /// match v {
 +    ///     Some(x) => (),
 +    ///     y @ _ => (),
 +    /// }
 +    ///
 +    /// // Good
 +    /// match v {
 +    ///     Some(x) => (),
 +    ///     y => (),
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub REDUNDANT_PATTERN,
 +    style,
 +    "using `name @ _` in a pattern"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for tuple patterns with a wildcard
 +    /// pattern (`_`) is next to a rest pattern (`..`).
 +    ///
 +    /// _NOTE_: While `_, ..` means there is at least one element left, `..`
 +    /// means there are 0 or more elements left. This can make a difference
 +    /// when refactoring, but shouldn't result in errors in the refactored code,
 +    /// since the wildcard pattern isn't used anyway.
 +    /// ### Why is this bad?
 +    /// The wildcard pattern is unneeded as the rest pattern
 +    /// can match that element as well.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct TupleStruct(u32, u32, u32);
 +    /// # let t = TupleStruct(1, 2, 3);
 +    /// // Bad
 +    /// match t {
 +    ///     TupleStruct(0, .., _) => (),
 +    ///     _ => (),
 +    /// }
 +    ///
 +    /// // Good
 +    /// match t {
 +    ///     TupleStruct(0, ..) => (),
 +    ///     _ => (),
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub UNNEEDED_WILDCARD_PATTERN,
 +    complexity,
 +    "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
 +}
 +
 +declare_lint_pass!(MiscEarlyLints => [
 +    UNNEEDED_FIELD_PATTERN,
 +    DUPLICATE_UNDERSCORE_ARGUMENT,
 +    DOUBLE_NEG,
 +    MIXED_CASE_HEX_LITERALS,
 +    UNSEPARATED_LITERAL_SUFFIX,
 +    SEPARATED_LITERAL_SUFFIX,
 +    ZERO_PREFIXED_LITERAL,
 +    BUILTIN_TYPE_SHADOW,
 +    REDUNDANT_PATTERN,
 +    UNNEEDED_WILDCARD_PATTERN,
 +]);
 +
 +impl EarlyLintPass for MiscEarlyLints {
 +    fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
 +        for param in &gen.params {
 +            builtin_type_shadow::check(cx, param);
 +        }
 +    }
 +
 +    fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
 +        unneeded_field_pattern::check(cx, pat);
 +        redundant_pattern::check(cx, pat);
 +        unneeded_wildcard_pattern::check(cx, pat);
 +    }
 +
 +    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
 +        let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();
 +
 +        for arg in &fn_kind.decl().inputs {
 +            if let PatKind::Ident(_, ident, None) = arg.pat.kind {
 +                let arg_name = ident.to_string();
 +
 +                if let Some(arg_name) = arg_name.strip_prefix('_') {
 +                    if let Some(correspondence) = registered_names.get(arg_name) {
 +                        span_lint(
 +                            cx,
 +                            DUPLICATE_UNDERSCORE_ARGUMENT,
 +                            *correspondence,
 +                            &format!(
 +                                "`{}` already exists, having another argument having almost the same \
 +                                 name makes code comprehension and documentation more difficult",
 +                                arg_name
 +                            ),
 +                        );
 +                    }
 +                } else {
 +                    registered_names.insert(arg_name, arg.pat.span);
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
 +        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
 +
 +        if let ExprKind::Lit(ref lit) = expr.kind {
 +            MiscEarlyLints::check_lit(cx, lit);
 +        }
 +        double_neg::check(cx, expr);
 +    }
 +}
 +
 +impl MiscEarlyLints {
 +    fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
 +        // We test if first character in snippet is a number, because the snippet could be an expansion
 +        // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`.
 +        // Note that this check also covers special case that `line!()` is eagerly expanded by compiler.
 +        // See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
 +        // FIXME: Find a better way to detect those cases.
 +        let lit_snip = match snippet_opt(cx, lit.span) {
++            Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip,
 +            _ => return,
 +        };
 +
 +        if let LitKind::Int(value, lit_int_type) = lit.kind {
 +            let suffix = match lit_int_type {
 +                LitIntType::Signed(ty) => ty.name_str(),
 +                LitIntType::Unsigned(ty) => ty.name_str(),
 +                LitIntType::Unsuffixed => "",
 +            };
 +            literal_suffix::check(cx, lit, &lit_snip, suffix, "integer");
 +            if lit_snip.starts_with("0x") {
 +                mixed_case_hex_literals::check(cx, lit, suffix, &lit_snip);
 +            } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") {
 +                // nothing to do
 +            } else if value != 0 && lit_snip.starts_with('0') {
 +                zero_prefixed_literal::check(cx, lit, &lit_snip);
 +            }
 +        } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind {
 +            let suffix = float_ty.name_str();
 +            literal_suffix::check(cx, lit, &lit_snip, suffix, "float");
 +        }
 +    }
 +}
index ac2f16b49e3f1cd3fc3bc560b8aac36d268735da,0000000000000000000000000000000000000000..0d95329918984c68a74d541042995ff1a94ec28b
mode 100644,000000..100644
--- /dev/null
@@@ -1,170 -1,0 +1,170 @@@
-     ///    pub fn not_ptrivate() {} // missing #[inline]
 +use clippy_utils::diagnostics::span_lint;
 +use rustc_ast::ast;
 +use rustc_hir as hir;
 +use rustc_lint::{self, LateContext, LateLintPass, LintContext};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// It lints if an exported function, method, trait method with default impl,
 +    /// or trait method impl is not `#[inline]`.
 +    ///
 +    /// ### Why is this bad?
 +    /// In general, it is not. Functions can be inlined across
 +    /// crates when that's profitable as long as any form of LTO is used. When LTO is disabled,
 +    /// functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates
 +    /// might intend for most of the methods in their public API to be able to be inlined across
 +    /// crates even when LTO is disabled. For these types of crates, enabling this lint might make
 +    /// sense. It allows the crate to require all exported methods to be `#[inline]` by default, and
 +    /// then opt out for specific methods where this might not make sense.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// pub fn foo() {} // missing #[inline]
 +    /// fn ok() {} // ok
 +    /// #[inline] pub fn bar() {} // ok
 +    /// #[inline(always)] pub fn baz() {} // ok
 +    ///
 +    /// pub trait Bar {
 +    ///   fn bar(); // ok
 +    ///   fn def_bar() {} // missing #[inline]
 +    /// }
 +    ///
 +    /// struct Baz;
 +    /// impl Baz {
 +    ///    fn private() {} // ok
 +    /// }
 +    ///
 +    /// impl Bar for Baz {
 +    ///   fn bar() {} // ok - Baz is not exported
 +    /// }
 +    ///
 +    /// pub struct PubBaz;
 +    /// impl PubBaz {
 +    ///    fn private() {} // ok
-             hir::ItemKind::Trait(ref _is_auto, ref _unsafe, ref _generics, _bounds, trait_items) => {
++    ///    pub fn not_private() {} // missing #[inline]
 +    /// }
 +    ///
 +    /// impl Bar for PubBaz {
 +    ///    fn bar() {} // missing #[inline]
 +    ///    fn def_bar() {} // missing #[inline]
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MISSING_INLINE_IN_PUBLIC_ITEMS,
 +    restriction,
 +    "detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)"
 +}
 +
 +fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) {
 +    let has_inline = attrs.iter().any(|a| a.has_name(sym::inline));
 +    if !has_inline {
 +        span_lint(
 +            cx,
 +            MISSING_INLINE_IN_PUBLIC_ITEMS,
 +            sp,
 +            &format!("missing `#[inline]` for {}", desc),
 +        );
 +    }
 +}
 +
 +fn is_executable_or_proc_macro(cx: &LateContext<'_>) -> bool {
 +    use rustc_session::config::CrateType;
 +
 +    cx.tcx
 +        .sess
 +        .crate_types()
 +        .iter()
 +        .any(|t: &CrateType| matches!(t, CrateType::Executable | CrateType::ProcMacro))
 +}
 +
 +declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MissingInline {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) {
 +        if rustc_middle::lint::in_external_macro(cx.sess(), it.span) || is_executable_or_proc_macro(cx) {
 +            return;
 +        }
 +
 +        if !cx.access_levels.is_exported(it.def_id) {
 +            return;
 +        }
 +        match it.kind {
 +            hir::ItemKind::Fn(..) => {
 +                let desc = "a function";
 +                let attrs = cx.tcx.hir().attrs(it.hir_id());
 +                check_missing_inline_attrs(cx, attrs, it.span, desc);
 +            },
++            hir::ItemKind::Trait(ref _is_auto, ref _unsafe, _generics, _bounds, trait_items) => {
 +                // note: we need to check if the trait is exported so we can't use
 +                // `LateLintPass::check_trait_item` here.
 +                for tit in trait_items {
 +                    let tit_ = cx.tcx.hir().trait_item(tit.id);
 +                    match tit_.kind {
 +                        hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => {},
 +                        hir::TraitItemKind::Fn(..) => {
 +                            if tit.defaultness.has_value() {
 +                                // trait method with default body needs inline in case
 +                                // an impl is not provided
 +                                let desc = "a default trait method";
 +                                let item = cx.tcx.hir().trait_item(tit.id);
 +                                let attrs = cx.tcx.hir().attrs(item.hir_id());
 +                                check_missing_inline_attrs(cx, attrs, item.span, desc);
 +                            }
 +                        },
 +                    }
 +                }
 +            },
 +            hir::ItemKind::Const(..)
 +            | hir::ItemKind::Enum(..)
 +            | hir::ItemKind::Macro(..)
 +            | hir::ItemKind::Mod(..)
 +            | hir::ItemKind::Static(..)
 +            | hir::ItemKind::Struct(..)
 +            | hir::ItemKind::TraitAlias(..)
 +            | hir::ItemKind::GlobalAsm(..)
 +            | hir::ItemKind::TyAlias(..)
 +            | hir::ItemKind::Union(..)
 +            | hir::ItemKind::OpaqueTy(..)
 +            | hir::ItemKind::ExternCrate(..)
 +            | hir::ItemKind::ForeignMod { .. }
 +            | hir::ItemKind::Impl { .. }
 +            | hir::ItemKind::Use(..) => {},
 +        };
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
 +        use rustc_middle::ty::{ImplContainer, TraitContainer};
 +        if rustc_middle::lint::in_external_macro(cx.sess(), impl_item.span) || is_executable_or_proc_macro(cx) {
 +            return;
 +        }
 +
 +        // If the item being implemented is not exported, then we don't need #[inline]
 +        if !cx.access_levels.is_exported(impl_item.def_id) {
 +            return;
 +        }
 +
 +        let desc = match impl_item.kind {
 +            hir::ImplItemKind::Fn(..) => "a method",
 +            hir::ImplItemKind::Const(..) | hir::ImplItemKind::TyAlias(_) => return,
 +        };
 +
 +        let trait_def_id = match cx.tcx.associated_item(impl_item.def_id).container {
 +            TraitContainer(cid) => Some(cid),
 +            ImplContainer(cid) => cx.tcx.impl_trait_ref(cid).map(|t| t.def_id),
 +        };
 +
 +        if let Some(trait_def_id) = trait_def_id {
 +            if trait_def_id.is_local() && !cx.access_levels.is_exported(impl_item.def_id) {
 +                // If a trait is being implemented for an item, and the
 +                // trait is not exported, we don't need #[inline]
 +                return;
 +            }
 +        }
 +
 +        let attrs = cx.tcx.hir().attrs(impl_item.hir_id());
 +        check_missing_inline_attrs(cx, attrs, impl_item.span, desc);
 +    }
 +}
index a8a8d174a823e309f088517808464b9a6d8ad4d8,0000000000000000000000000000000000000000..95395e2e136d9f18024a356d0ea7f3dc1f4f6bd9
mode 100644,000000..100644
--- /dev/null
@@@ -1,85 -1,0 +1,85 @@@
- fn suggession_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet_opt;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using
 +    /// a lazy and.
 +    ///
 +    /// ### Why is this bad?
 +    /// The bitwise operators do not support short-circuiting, so it may hinder code performance.
 +    /// Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold`
 +    ///
 +    /// ### Known problems
 +    /// This lint evaluates only when the right side is determined to have no side effects. At this time, that
 +    /// determination is quite conservative.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let (x,y) = (true, false);
 +    /// if x & !y {} // where both x and y are booleans
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let (x,y) = (true, false);
 +    /// if x && !y {}
 +    /// ```
 +    #[clippy::version = "1.54.0"]
 +    pub NEEDLESS_BITWISE_BOOL,
 +    pedantic,
 +    "Boolean expressions that use bitwise rather than lazy operators"
 +}
 +
 +declare_lint_pass!(NeedlessBitwiseBool => [NEEDLESS_BITWISE_BOOL]);
 +
 +fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    let ty = cx.typeck_results().expr_ty(expr);
 +    if_chain! {
 +        if !expr.span.from_expansion();
 +        if let (&ExprKind::Binary(ref op, _, right), &ty::Bool) = (&expr.kind, &ty.kind());
 +        if op.node == BinOpKind::BitAnd || op.node == BinOpKind::BitOr;
 +        if let ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..) = right.kind;
 +        if !right.can_have_side_effects();
 +        then {
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
-                     if let Some(sugg) = suggession_snippet(cx, expr) {
++fn suggesstion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
 +    if let ExprKind::Binary(ref op, left, right) = expr.kind {
 +        if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) {
 +            let op_snippet = match op.node {
 +                BinOpKind::BitAnd => "&&",
 +                _ => "||",
 +            };
 +            return Some(format!("{} {} {}", l_snippet, op_snippet, r_snippet));
 +        }
 +    }
 +    None
 +}
 +
 +impl LateLintPass<'_> for NeedlessBitwiseBool {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        if is_bitwise_operation(cx, expr) {
 +            span_lint_and_then(
 +                cx,
 +                NEEDLESS_BITWISE_BOOL,
 +                expr.span,
 +                "use of bitwise operator instead of lazy operator between booleans",
 +                |diag| {
++                    if let Some(sugg) = suggesstion_snippet(cx, expr) {
 +                        diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable);
 +                    }
 +                },
 +            );
 +        }
 +    }
 +}
index 9957afcbf04aaee1d7daaa305f1a89e797d335e9,0000000000000000000000000000000000000000..b70871b38beab178f63735330657ab8388e2f355
mode 100644,000000..100644
--- /dev/null
@@@ -1,355 -1,0 +1,389 @@@
- use clippy_utils::visitors::{expr_visitor, is_local_used};
- use rustc_errors::Applicability;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::path_to_local;
 +use clippy_utils::source::snippet_opt;
- use rustc_hir::{Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind};
++use clippy_utils::ty::needs_ordered_drop;
++use clippy_utils::visitors::{expr_visitor, expr_visitor_no_bodies, is_local_used};
++use rustc_errors::{Applicability, MultiSpan};
 +use rustc_hir::intravisit::Visitor;
-                 local_stmt.span,
-                 "unneeded late initalization",
++use rustc_hir::{
++    BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt,
++    StmtKind,
++};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for late initializations that can be replaced by a `let` statement
 +    /// with an initializer.
 +    ///
 +    /// ### Why is this bad?
 +    /// Assigning in the `let` statement is less repetitive.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a;
 +    /// a = 1;
 +    ///
 +    /// let b;
 +    /// match 3 {
 +    ///     0 => b = "zero",
 +    ///     1 => b = "one",
 +    ///     _ => b = "many",
 +    /// }
 +    ///
 +    /// let c;
 +    /// if true {
 +    ///     c = 1;
 +    /// } else {
 +    ///     c = -1;
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = 1;
 +    ///
 +    /// let b = match 3 {
 +    ///     0 => "zero",
 +    ///     1 => "one",
 +    ///     _ => "many",
 +    /// };
 +    ///
 +    /// let c = if true {
 +    ///     1
 +    /// } else {
 +    ///     -1
 +    /// };
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub NEEDLESS_LATE_INIT,
 +    style,
 +    "late initializations that can be replaced by a `let` statement with an initializer"
 +}
 +declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]);
 +
 +fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool {
 +    let mut seen = false;
 +    expr_visitor(cx, |expr| {
 +        if let ExprKind::Assign(..) = expr.kind {
 +            seen = true;
 +        }
 +
 +        !seen
 +    })
 +    .visit_stmt(stmt);
 +
 +    seen
 +}
 +
++fn contains_let(cond: &Expr<'_>) -> bool {
++    let mut seen = false;
++    expr_visitor_no_bodies(|expr| {
++        if let ExprKind::Let(_) = expr.kind {
++            seen = true;
++        }
++
++        !seen
++    })
++    .visit_expr(cond);
++
++    seen
++}
++
++fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
++    let StmtKind::Local(local) = stmt.kind else { return false };
++    !local.pat.walk_short(|pat| {
++        if let PatKind::Binding(.., None) = pat.kind {
++            !needs_ordered_drop(cx, cx.typeck_results().pat_ty(pat))
++        } else {
++            true
++        }
++    })
++}
++
 +#[derive(Debug)]
 +struct LocalAssign {
 +    lhs_id: HirId,
 +    lhs_span: Span,
 +    rhs_span: Span,
 +    span: Span,
 +}
 +
 +impl LocalAssign {
 +    fn from_expr(expr: &Expr<'_>, span: Span) -> Option<Self> {
 +        if let ExprKind::Assign(lhs, rhs, _) = expr.kind {
 +            if lhs.span.from_expansion() {
 +                return None;
 +            }
 +
 +            Some(Self {
 +                lhs_id: path_to_local(lhs)?,
 +                lhs_span: lhs.span,
 +                rhs_span: rhs.span.source_callsite(),
 +                span,
 +            })
 +        } else {
 +            None
 +        }
 +    }
 +
 +    fn new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, binding_id: HirId) -> Option<LocalAssign> {
 +        let assign = match expr.kind {
 +            ExprKind::Block(Block { expr: Some(expr), .. }, _) => Self::from_expr(expr, expr.span),
 +            ExprKind::Block(block, _) => {
 +                if_chain! {
 +                    if let Some((last, other_stmts)) = block.stmts.split_last();
 +                    if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = last.kind;
 +
 +                    let assign = Self::from_expr(expr, last.span)?;
 +
 +                    // avoid visiting if not needed
 +                    if assign.lhs_id == binding_id;
 +                    if other_stmts.iter().all(|stmt| !contains_assign_expr(cx, stmt));
 +
 +                    then {
 +                        Some(assign)
 +                    } else {
 +                        None
 +                    }
 +                }
 +            },
 +            ExprKind::Assign(..) => Self::from_expr(expr, expr.span),
 +            _ => None,
 +        }?;
 +
 +        if assign.lhs_id == binding_id {
 +            Some(assign)
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +fn assignment_suggestions<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    binding_id: HirId,
 +    exprs: impl IntoIterator<Item = &'tcx Expr<'tcx>>,
 +) -> Option<(Applicability, Vec<(Span, String)>)> {
 +    let mut assignments = Vec::new();
 +
 +    for expr in exprs {
 +        let ty = cx.typeck_results().expr_ty(expr);
 +
 +        if ty.is_never() {
 +            continue;
 +        }
 +        if !ty.is_unit() {
 +            return None;
 +        }
 +
 +        let assign = LocalAssign::new(cx, expr, binding_id)?;
 +
 +        assignments.push(assign);
 +    }
 +
 +    let suggestions = assignments
 +        .iter()
 +        .map(|assignment| Some((assignment.span.until(assignment.rhs_span), String::new())))
 +        .chain(assignments.iter().map(|assignment| {
 +            Some((
 +                assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi()),
 +                String::new(),
 +            ))
 +        }))
 +        .collect::<Option<Vec<(Span, String)>>>()?;
 +
 +    let applicability = if suggestions.len() > 1 {
 +        // multiple suggestions don't work with rustfix in multipart_suggest
 +        // https://github.com/rust-lang/rustfix/issues/141
 +        Applicability::Unspecified
 +    } else {
 +        Applicability::MachineApplicable
 +    };
 +    Some((applicability, suggestions))
 +}
 +
 +struct Usage<'tcx> {
 +    stmt: &'tcx Stmt<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    needs_semi: bool,
 +}
 +
 +fn first_usage<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    binding_id: HirId,
 +    local_stmt_id: HirId,
 +    block: &'tcx Block<'tcx>,
 +) -> Option<Usage<'tcx>> {
++    let significant_drop = needs_ordered_drop(cx, cx.typeck_results().node_type(binding_id));
++
 +    block
 +        .stmts
 +        .iter()
 +        .skip_while(|stmt| stmt.hir_id != local_stmt_id)
 +        .skip(1)
++        .take_while(|stmt| !significant_drop || !stmt_needs_ordered_drop(cx, stmt))
 +        .find(|&stmt| is_local_used(cx, stmt, binding_id))
 +        .and_then(|stmt| match stmt.kind {
 +            StmtKind::Expr(expr) => Some(Usage {
 +                stmt,
 +                expr,
 +                needs_semi: true,
 +            }),
 +            StmtKind::Semi(expr) => Some(Usage {
 +                stmt,
 +                expr,
 +                needs_semi: false,
 +            }),
 +            _ => None,
 +        })
 +}
 +
 +fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &Local<'_>) -> Option<String> {
 +    let span = local.span.with_hi(match local.ty {
 +        // let <pat>: <ty>;
 +        // ~~~~~~~~~~~~~~~
 +        Some(ty) => ty.span.hi(),
 +        // let <pat>;
 +        // ~~~~~~~~~
 +        None => local.pat.span.hi(),
 +    });
 +
 +    snippet_opt(cx, span)
 +}
 +
 +fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    local: &'tcx Local<'tcx>,
 +    local_stmt: &'tcx Stmt<'tcx>,
 +    block: &'tcx Block<'tcx>,
 +    binding_id: HirId,
 +) -> Option<()> {
 +    let usage = first_usage(cx, binding_id, local_stmt.hir_id, block)?;
 +    let binding_name = cx.tcx.hir().opt_name(binding_id)?;
 +    let let_snippet = local_snippet_without_semicolon(cx, local)?;
 +
 +    match usage.expr.kind {
 +        ExprKind::Assign(..) => {
 +            let assign = LocalAssign::new(cx, usage.expr, binding_id)?;
++            let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]);
++            msg_span.push_span_label(local_stmt.span, "created here");
++            msg_span.push_span_label(assign.span, "initialised here");
 +
 +            span_lint_and_then(
 +                cx,
 +                NEEDLESS_LATE_INIT,
-         ExprKind::If(_, then_expr, Some(else_expr)) => {
++                msg_span,
++                "unneeded late initialization",
 +                |diag| {
 +                    diag.tool_only_span_suggestion(
 +                        local_stmt.span,
 +                        "remove the local",
 +                        String::new(),
 +                        Applicability::MachineApplicable,
 +                    );
 +
 +                    diag.span_suggestion(
 +                        assign.lhs_span,
 +                        &format!("declare `{}` here", binding_name),
 +                        let_snippet,
 +                        Applicability::MachineApplicable,
 +                    );
 +                },
 +            );
 +        },
-                 "unneeded late initalization",
++        ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => {
 +            let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?;
 +
 +            span_lint_and_then(
 +                cx,
 +                NEEDLESS_LATE_INIT,
 +                local_stmt.span,
-                 "unneeded late initalization",
++                "unneeded late initialization",
 +                |diag| {
 +                    diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
 +
 +                    diag.span_suggestion_verbose(
 +                        usage.stmt.span.shrink_to_lo(),
 +                        &format!("declare `{}` here", binding_name),
 +                        format!("{} = ", let_snippet),
 +                        applicability,
 +                    );
 +
 +                    diag.multipart_suggestion("remove the assignments from the branches", suggestions, applicability);
 +
 +                    if usage.needs_semi {
 +                        diag.span_suggestion(
 +                            usage.stmt.span.shrink_to_hi(),
 +                            "add a semicolon after the `if` expression",
 +                            ";".to_string(),
 +                            applicability,
 +                        );
 +                    }
 +                },
 +            );
 +        },
 +        ExprKind::Match(_, arms, MatchSource::Normal) => {
 +            let (applicability, suggestions) = assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?;
 +
 +            span_lint_and_then(
 +                cx,
 +                NEEDLESS_LATE_INIT,
 +                local_stmt.span,
++                "unneeded late initialization",
 +                |diag| {
 +                    diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
 +
 +                    diag.span_suggestion_verbose(
 +                        usage.stmt.span.shrink_to_lo(),
 +                        &format!("declare `{}` here", binding_name),
 +                        format!("{} = ", let_snippet),
 +                        applicability,
 +                    );
 +
 +                    diag.multipart_suggestion(
 +                        "remove the assignments from the `match` arms",
 +                        suggestions,
 +                        applicability,
 +                    );
 +
 +                    if usage.needs_semi {
 +                        diag.span_suggestion(
 +                            usage.stmt.span.shrink_to_hi(),
 +                            "add a semicolon after the `match` expression",
 +                            ";".to_string(),
 +                            applicability,
 +                        );
 +                    }
 +                },
 +            );
 +        },
 +        _ => {},
 +    };
 +
 +    Some(())
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit {
 +    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
 +        let mut parents = cx.tcx.hir().parent_iter(local.hir_id);
-                     kind: PatKind::Binding(_, binding_id, _, None),
 +        if_chain! {
 +            if let Local {
 +                init: None,
 +                pat: &Pat {
++                    kind: PatKind::Binding(BindingAnnotation::Unannotated, binding_id, _, None),
 +                    ..
 +                },
 +                source: LocalSource::Normal,
 +                ..
 +            } = local;
 +            if let Some((_, Node::Stmt(local_stmt))) = parents.next();
 +            if let Some((_, Node::Block(block))) = parents.next();
 +
 +            then {
 +                check(cx, local, local_stmt, block, binding_id);
 +            }
 +        }
 +    }
 +}
index 96c00c205ff2e9401e0589e1a9786fb47c2c2356,0000000000000000000000000000000000000000..2f733f221d572250866fd51150826a871b83a504
mode 100644,000000..100644
--- /dev/null
@@@ -1,176 -1,0 +1,176 @@@
-             ref generics,
 +use clippy_utils::diagnostics::span_lint_hir_and_then;
 +use clippy_utils::return_ty;
 +use clippy_utils::source::snippet;
 +use clippy_utils::sugg::DiagnosticExt;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::HirIdSet;
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for public types with a `pub fn new() -> Self` method and no
 +    /// implementation of
 +    /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html).
 +    ///
 +    /// ### Why is this bad?
 +    /// The user might expect to be able to use
 +    /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the
 +    /// type can be constructed without arguments.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// pub struct Foo(Bar);
 +    ///
 +    /// impl Foo {
 +    ///     pub fn new() -> Self {
 +    ///         Foo(Bar::new())
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// To fix the lint, add a `Default` implementation that delegates to `new`:
 +    ///
 +    /// ```ignore
 +    /// pub struct Foo(Bar);
 +    ///
 +    /// impl Default for Foo {
 +    ///     fn default() -> Self {
 +    ///         Foo::new()
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEW_WITHOUT_DEFAULT,
 +    style,
 +    "`pub fn new() -> Self` method without `Default` implementation"
 +}
 +
 +#[derive(Clone, Default)]
 +pub struct NewWithoutDefault {
 +    impling_types: Option<HirIdSet>,
 +}
 +
 +impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
 +    #[allow(clippy::too_many_lines)]
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        if let hir::ItemKind::Impl(hir::Impl {
 +            of_trait: None,
++            generics,
 +            self_ty: impl_self_ty,
 +            items,
 +            ..
 +        }) = item.kind
 +        {
 +            for assoc_item in *items {
 +                if assoc_item.kind == (hir::AssocItemKind::Fn { has_self: false }) {
 +                    let impl_item = cx.tcx.hir().impl_item(assoc_item.id);
 +                    if in_external_macro(cx.sess(), impl_item.span) {
 +                        return;
 +                    }
 +                    if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind {
 +                        let name = impl_item.ident.name;
 +                        let id = impl_item.hir_id();
 +                        if sig.header.constness == hir::Constness::Const {
 +                            // can't be implemented by default
 +                            return;
 +                        }
 +                        if sig.header.unsafety == hir::Unsafety::Unsafe {
 +                            // can't be implemented for unsafe new
 +                            return;
 +                        }
 +                        if clippy_utils::is_doc_hidden(cx.tcx.hir().attrs(id)) {
 +                            // shouldn't be implemented when it is hidden in docs
 +                            return;
 +                        }
 +                        if impl_item
 +                            .generics
 +                            .params
 +                            .iter()
 +                            .any(|gen| matches!(gen.kind, hir::GenericParamKind::Type { .. }))
 +                        {
 +                            // when the result of `new()` depends on a type parameter we should not require
 +                            // an
 +                            // impl of `Default`
 +                            return;
 +                        }
 +                        if_chain! {
 +                            if sig.decl.inputs.is_empty();
 +                            if name == sym::new;
 +                            if cx.access_levels.is_reachable(impl_item.def_id);
 +                            let self_def_id = cx.tcx.hir().get_parent_item(id);
 +                            let self_ty = cx.tcx.type_of(self_def_id);
 +                            if self_ty == return_ty(cx, id);
 +                            if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default);
 +                            then {
 +                                if self.impling_types.is_none() {
 +                                    let mut impls = HirIdSet::default();
 +                                    cx.tcx.for_each_impl(default_trait_id, |d| {
 +                                        if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() {
 +                                            if let Some(local_def_id) = ty_def.did().as_local() {
 +                                                impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id));
 +                                            }
 +                                        }
 +                                    });
 +                                    self.impling_types = Some(impls);
 +                                }
 +
 +                                // Check if a Default implementation exists for the Self type, regardless of
 +                                // generics
 +                                if_chain! {
 +                                    if let Some(ref impling_types) = self.impling_types;
 +                                    if let Some(self_def) = cx.tcx.type_of(self_def_id).ty_adt_def();
 +                                    if let Some(self_local_did) = self_def.did().as_local();
 +                                    let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did);
 +                                    if impling_types.contains(&self_id);
 +                                    then {
 +                                        return;
 +                                    }
 +                                }
 +
 +                                let generics_sugg = snippet(cx, generics.span, "");
 +                                let self_ty_fmt = self_ty.to_string();
 +                                let self_type_snip = snippet(cx, impl_self_ty.span, &self_ty_fmt);
 +                                span_lint_hir_and_then(
 +                                    cx,
 +                                    NEW_WITHOUT_DEFAULT,
 +                                    id,
 +                                    impl_item.span,
 +                                    &format!(
 +                                        "you should consider adding a `Default` implementation for `{}`",
 +                                        self_type_snip
 +                                    ),
 +                                    |diag| {
 +                                        diag.suggest_prepend_item(
 +                                            cx,
 +                                            item.span,
 +                                            "try adding this",
 +                                            &create_new_without_default_suggest_msg(&self_type_snip, &generics_sugg),
 +                                            Applicability::MaybeIncorrect,
 +                                        );
 +                                    },
 +                                );
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn create_new_without_default_suggest_msg(self_type_snip: &str, generics_sugg: &str) -> String {
 +    #[rustfmt::skip]
 +    format!(
 +"impl{} Default for {} {{
 +    fn default() -> Self {{
 +        Self::new()
 +    }}
 +}}", generics_sugg, self_type_snip)
 +}
index 0d0c88b02c78b6d5adabe7ed4a6b8bc41b599507,0000000000000000000000000000000000000000..e3bc40c4b49148962fe3cf94d316c42de3e7220f
mode 100644,000000..100644
--- /dev/null
@@@ -1,429 -1,0 +1,429 @@@
-         if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 +use rustc_ast::ast::{
 +    self, Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, Pat, PatKind,
 +};
 +use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +use rustc_span::sym;
 +use rustc_span::symbol::{Ident, Symbol};
 +use std::cmp::Ordering;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for names that are very similar and thus confusing.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's hard to distinguish between names that differ only
 +    /// by a single character.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// let checked_exp = something;
 +    /// let checked_expr = something_else;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SIMILAR_NAMES,
 +    pedantic,
 +    "similarly named items and bindings"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for too many variables whose name consists of a
 +    /// single character.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's hard to memorize what a variable means without a
 +    /// descriptive name.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// let (a, b, c, d, e, f, g) = (...);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MANY_SINGLE_CHAR_NAMES,
 +    pedantic,
 +    "too many single character bindings"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks if you have variables whose name consists of just
 +    /// underscores and digits.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's hard to memorize what a variable means without a
 +    /// descriptive name.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _1 = 1;
 +    /// let ___1 = 1;
 +    /// let __1___2 = 11;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub JUST_UNDERSCORES_AND_DIGITS,
 +    style,
 +    "unclear name"
 +}
 +
 +#[derive(Copy, Clone)]
 +pub struct NonExpressiveNames {
 +    pub single_char_binding_names_threshold: u64,
 +}
 +
 +impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]);
 +
 +struct ExistingName {
 +    interned: Symbol,
 +    span: Span,
 +    len: usize,
 +    exemptions: &'static [&'static str],
 +}
 +
 +struct SimilarNamesLocalVisitor<'a, 'tcx> {
 +    names: Vec<ExistingName>,
 +    cx: &'a EarlyContext<'tcx>,
 +    lint: &'a NonExpressiveNames,
 +
 +    /// A stack of scopes containing the single-character bindings in each scope.
 +    single_char_names: Vec<Vec<Ident>>,
 +}
 +
 +impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
 +    fn check_single_char_names(&self) {
 +        let num_single_char_names = self.single_char_names.iter().flatten().count();
 +        let threshold = self.lint.single_char_binding_names_threshold;
 +        if num_single_char_names as u64 > threshold {
 +            let span = self
 +                .single_char_names
 +                .iter()
 +                .flatten()
 +                .map(|ident| ident.span)
 +                .collect::<Vec<_>>();
 +            span_lint(
 +                self.cx,
 +                MANY_SINGLE_CHAR_NAMES,
 +                span,
 +                &format!(
 +                    "{} bindings with single-character names in scope",
 +                    num_single_char_names
 +                ),
 +            );
 +        }
 +    }
 +}
 +
 +// this list contains lists of names that are allowed to be similar
 +// the assumption is that no name is ever contained in multiple lists.
 +#[rustfmt::skip]
 +const ALLOWED_TO_BE_SIMILAR: &[&[&str]] = &[
 +    &["parsed", "parser"],
 +    &["lhs", "rhs"],
 +    &["tx", "rx"],
 +    &["set", "get"],
 +    &["args", "arms"],
 +    &["qpath", "path"],
 +    &["lit", "lint"],
 +    &["wparam", "lparam"],
 +    &["iter", "item"],
 +];
 +
 +struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);
 +
 +impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
 +    fn visit_pat(&mut self, pat: &'tcx Pat) {
 +        match pat.kind {
 +            PatKind::Ident(_, ident, _) => {
 +                if !pat.span.from_expansion() {
 +                    self.check_ident(ident);
 +                }
 +            },
 +            PatKind::Struct(_, _, ref fields, _) => {
 +                for field in fields {
 +                    if !field.is_shorthand {
 +                        self.visit_pat(&field.pat);
 +                    }
 +                }
 +            },
 +            // just go through the first pattern, as either all patterns
 +            // bind the same bindings or rustc would have errored much earlier
 +            PatKind::Or(ref pats) => self.visit_pat(&pats[0]),
 +            _ => walk_pat(self, pat),
 +        }
 +    }
 +}
 +
 +#[must_use]
 +fn get_exemptions(interned_name: &str) -> Option<&'static [&'static str]> {
 +    for &list in ALLOWED_TO_BE_SIMILAR {
 +        if allowed_to_be_similar(interned_name, list) {
 +            return Some(list);
 +        }
 +    }
 +    None
 +}
 +
 +#[must_use]
 +fn allowed_to_be_similar(interned_name: &str, list: &[&str]) -> bool {
 +    list.iter()
 +        .any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name))
 +}
 +
 +impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
 +    fn check_short_ident(&mut self, ident: Ident) {
 +        // Ignore shadowing
 +        if self
 +            .0
 +            .single_char_names
 +            .iter()
 +            .flatten()
 +            .any(|id| id.name == ident.name)
 +        {
 +            return;
 +        }
 +
 +        if let Some(scope) = &mut self.0.single_char_names.last_mut() {
 +            scope.push(ident);
 +        }
 +    }
 +
 +    #[allow(clippy::too_many_lines)]
 +    fn check_ident(&mut self, ident: Ident) {
 +        let interned_name = ident.name.as_str();
 +        if interned_name.chars().any(char::is_uppercase) {
 +            return;
 +        }
++        if interned_name.chars().all(|c| c.is_ascii_digit() || c == '_') {
 +            span_lint(
 +                self.0.cx,
 +                JUST_UNDERSCORES_AND_DIGITS,
 +                ident.span,
 +                "consider choosing a more descriptive name",
 +            );
 +            return;
 +        }
 +        if interned_name.starts_with('_') {
 +            // these bindings are typically unused or represent an ignored portion of a destructuring pattern
 +            return;
 +        }
 +        let count = interned_name.chars().count();
 +        if count < 3 {
 +            if count == 1 {
 +                self.check_short_ident(ident);
 +            }
 +            return;
 +        }
 +        for existing_name in &self.0.names {
 +            if allowed_to_be_similar(interned_name, existing_name.exemptions) {
 +                continue;
 +            }
 +            match existing_name.len.cmp(&count) {
 +                Ordering::Greater => {
 +                    if existing_name.len - count != 1
 +                        || levenstein_not_1(interned_name, existing_name.interned.as_str())
 +                    {
 +                        continue;
 +                    }
 +                },
 +                Ordering::Less => {
 +                    if count - existing_name.len != 1
 +                        || levenstein_not_1(existing_name.interned.as_str(), interned_name)
 +                    {
 +                        continue;
 +                    }
 +                },
 +                Ordering::Equal => {
 +                    let mut interned_chars = interned_name.chars();
 +                    let interned_str = existing_name.interned.as_str();
 +                    let mut existing_chars = interned_str.chars();
 +                    let first_i = interned_chars.next().expect("we know we have at least one char");
 +                    let first_e = existing_chars.next().expect("we know we have at least one char");
 +                    let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();
 +
 +                    if eq_or_numeric((first_i, first_e)) {
 +                        let last_i = interned_chars.next_back().expect("we know we have at least two chars");
 +                        let last_e = existing_chars.next_back().expect("we know we have at least two chars");
 +                        if eq_or_numeric((last_i, last_e)) {
 +                            if interned_chars
 +                                .zip(existing_chars)
 +                                .filter(|&ie| !eq_or_numeric(ie))
 +                                .count()
 +                                != 1
 +                            {
 +                                continue;
 +                            }
 +                        } else {
 +                            let second_last_i = interned_chars
 +                                .next_back()
 +                                .expect("we know we have at least three chars");
 +                            let second_last_e = existing_chars
 +                                .next_back()
 +                                .expect("we know we have at least three chars");
 +                            if !eq_or_numeric((second_last_i, second_last_e))
 +                                || second_last_i == '_'
 +                                || !interned_chars.zip(existing_chars).all(eq_or_numeric)
 +                            {
 +                                // allowed similarity foo_x, foo_y
 +                                // or too many chars differ (foo_x, boo_y) or (foox, booy)
 +                                continue;
 +                            }
 +                        }
 +                    } else {
 +                        let second_i = interned_chars.next().expect("we know we have at least two chars");
 +                        let second_e = existing_chars.next().expect("we know we have at least two chars");
 +                        if !eq_or_numeric((second_i, second_e))
 +                            || second_i == '_'
 +                            || !interned_chars.zip(existing_chars).all(eq_or_numeric)
 +                        {
 +                            // allowed similarity x_foo, y_foo
 +                            // or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
 +                            continue;
 +                        }
 +                    }
 +                },
 +            }
 +            span_lint_and_then(
 +                self.0.cx,
 +                SIMILAR_NAMES,
 +                ident.span,
 +                "binding's name is too similar to existing binding",
 +                |diag| {
 +                    diag.span_note(existing_name.span, "existing binding defined here");
 +                },
 +            );
 +            return;
 +        }
 +        self.0.names.push(ExistingName {
 +            exemptions: get_exemptions(interned_name).unwrap_or(&[]),
 +            interned: ident.name,
 +            span: ident.span,
 +            len: count,
 +        });
 +    }
 +}
 +
 +impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
 +    /// ensure scoping rules work
 +    fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) {
 +        let n = self.names.len();
 +        let single_char_count = self.single_char_names.len();
 +        f(self);
 +        self.names.truncate(n);
 +        self.single_char_names.truncate(single_char_count);
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
 +    fn visit_local(&mut self, local: &'tcx Local) {
 +        if let Some((init, els)) = &local.kind.init_else_opt() {
 +            self.apply(|this| walk_expr(this, init));
 +            if let Some(els) = els {
 +                self.apply(|this| walk_block(this, els));
 +            }
 +        }
 +        // add the pattern after the expression because the bindings aren't available
 +        // yet in the init
 +        // expression
 +        SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
 +    }
 +    fn visit_block(&mut self, blk: &'tcx Block) {
 +        self.single_char_names.push(vec![]);
 +
 +        self.apply(|this| walk_block(this, blk));
 +
 +        self.check_single_char_names();
 +        self.single_char_names.pop();
 +    }
 +    fn visit_arm(&mut self, arm: &'tcx Arm) {
 +        self.single_char_names.push(vec![]);
 +
 +        self.apply(|this| {
 +            SimilarNamesNameVisitor(this).visit_pat(&arm.pat);
 +            this.apply(|this| walk_expr(this, &arm.body));
 +        });
 +
 +        self.check_single_char_names();
 +        self.single_char_names.pop();
 +    }
 +    fn visit_item(&mut self, _: &Item) {
 +        // do not recurse into inner items
 +    }
 +}
 +
 +impl EarlyLintPass for NonExpressiveNames {
 +    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
 +        if in_external_macro(cx.sess(), item.span) {
 +            return;
 +        }
 +
 +        if let ItemKind::Fn(box ast::Fn {
 +            ref sig,
 +            body: Some(ref blk),
 +            ..
 +        }) = item.kind
 +        {
 +            do_check(self, cx, &item.attrs, &sig.decl, blk);
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) {
 +        if in_external_macro(cx.sess(), item.span) {
 +            return;
 +        }
 +
 +        if let AssocItemKind::Fn(box ast::Fn {
 +            ref sig,
 +            body: Some(ref blk),
 +            ..
 +        }) = item.kind
 +        {
 +            do_check(self, cx, &item.attrs, &sig.decl, blk);
 +        }
 +    }
 +}
 +
 +fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
 +    if !attrs.iter().any(|attr| attr.has_name(sym::test)) {
 +        let mut visitor = SimilarNamesLocalVisitor {
 +            names: Vec::new(),
 +            cx,
 +            lint,
 +            single_char_names: vec![vec![]],
 +        };
 +
 +        // initialize with function arguments
 +        for arg in &decl.inputs {
 +            SimilarNamesNameVisitor(&mut visitor).visit_pat(&arg.pat);
 +        }
 +        // walk all other bindings
 +        walk_block(&mut visitor, blk);
 +
 +        visitor.check_single_char_names();
 +    }
 +}
 +
 +/// Precondition: `a_name.chars().count() < b_name.chars().count()`.
 +#[must_use]
 +fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
 +    debug_assert!(a_name.chars().count() < b_name.chars().count());
 +    let mut a_chars = a_name.chars();
 +    let mut b_chars = b_name.chars();
 +    while let (Some(a), Some(b)) = (a_chars.next(), b_chars.next()) {
 +        if a == b {
 +            continue;
 +        }
 +        if let Some(b2) = b_chars.next() {
 +            // check if there's just one character inserted
 +            return a != b2 || a_chars.ne(b_chars);
 +        }
 +        // tuple
 +        // ntuple
 +        return true;
 +    }
 +    // for item in items
 +    true
 +}
index c19cea661042d4c81a34572ec178f25aed6839d3,0000000000000000000000000000000000000000..e8532db4f711dbc658d9862f8ff6b4851253b5c8
mode 100644,000000..100644
--- /dev/null
@@@ -1,150 -1,0 +1,150 @@@
-     /// cases to be unambigious.
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use rustc_ast::ast::{Expr, ExprKind};
 +use rustc_ast::token::{Lit, LitKind};
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::Span;
 +use std::fmt::Write;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `\0` escapes in string and byte literals that look like octal
 +    /// character escapes in C.
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// C and other languages support octal character escapes in strings, where
 +    /// a backslash is followed by up to three octal digits. For example, `\033`
 +    /// stands for the ASCII character 27 (ESC). Rust does not support this
 +    /// notation, but has the escape code `\0` which stands for a null
 +    /// byte/character, and any following digits do not form part of the escape
 +    /// sequence. Therefore, `\033` is not a compiler error but the result may
 +    /// be surprising.
 +    ///
 +    /// ### Known problems
 +    /// The actual meaning can be the intended one. `\x00` can be used in these
++    /// cases to be unambiguous.
 +    ///
 +    /// The lint does not trigger for format strings in `print!()`, `write!()`
 +    /// and friends since the string is already preprocessed when Clippy lints
 +    /// can see it.
 +    ///
 +    /// # Example
 +    /// ```rust
 +    /// // Bad
 +    /// let one = "\033[1m Bold? \033[0m";  // \033 intended as escape
 +    /// let two = "\033\0";                 // \033 intended as null-3-3
 +    ///
 +    /// // Good
 +    /// let one = "\x1b[1mWill this be bold?\x1b[0m";
 +    /// let two = "\x0033\x00";
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub OCTAL_ESCAPES,
 +    suspicious,
 +    "string escape sequences looking like octal characters"
 +}
 +
 +declare_lint_pass!(OctalEscapes => [OCTAL_ESCAPES]);
 +
 +impl EarlyLintPass for OctalEscapes {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
 +        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
 +
 +        if let ExprKind::Lit(lit) = &expr.kind {
 +            if matches!(lit.token.kind, LitKind::Str) {
 +                check_lit(cx, &lit.token, lit.span, true);
 +            } else if matches!(lit.token.kind, LitKind::ByteStr) {
 +                check_lit(cx, &lit.token, lit.span, false);
 +            }
 +        }
 +    }
 +}
 +
 +fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) {
 +    let contents = lit.symbol.as_str();
 +    let mut iter = contents.char_indices().peekable();
 +    let mut found = vec![];
 +
 +    // go through the string, looking for \0[0-7][0-7]?
 +    while let Some((from, ch)) = iter.next() {
 +        if ch == '\\' {
 +            if let Some((_, '0')) = iter.next() {
 +                // collect up to two further octal digits
 +                if let Some((mut to, '0'..='7')) = iter.next() {
 +                    if let Some((_, '0'..='7')) = iter.peek() {
 +                        to += 1;
 +                    }
 +                    found.push((from, to + 1));
 +                }
 +            }
 +        }
 +    }
 +
 +    if found.is_empty() {
 +        return;
 +    }
 +
 +    // construct two suggestion strings, one with \x escapes with octal meaning
 +    // as in C, and one with \x00 for null bytes.
 +    let mut suggest_1 = if is_string { "\"" } else { "b\"" }.to_string();
 +    let mut suggest_2 = suggest_1.clone();
 +    let mut index = 0;
 +    for (from, to) in found {
 +        suggest_1.push_str(&contents[index..from]);
 +        suggest_2.push_str(&contents[index..from]);
 +
 +        // construct a replacement escape
 +        // the maximum value is \077, or \x3f, so u8 is sufficient here
 +        if let Ok(n) = u8::from_str_radix(&contents[from + 1..to], 8) {
 +            write!(suggest_1, "\\x{:02x}", n).unwrap();
 +        }
 +
 +        // append the null byte as \x00 and the following digits literally
 +        suggest_2.push_str("\\x00");
 +        suggest_2.push_str(&contents[from + 2..to]);
 +
 +        index = to;
 +    }
 +    suggest_1.push_str(&contents[index..]);
 +    suggest_1.push('"');
 +    suggest_2.push_str(&contents[index..]);
 +    suggest_2.push('"');
 +
 +    span_lint_and_then(
 +        cx,
 +        OCTAL_ESCAPES,
 +        span,
 +        &format!(
 +            "octal-looking escape in {} literal",
 +            if is_string { "string" } else { "byte string" }
 +        ),
 +        |diag| {
 +            diag.help(&format!(
 +                "octal escapes are not supported, `\\0` is always a null {}",
 +                if is_string { "character" } else { "byte" }
 +            ));
 +            // suggestion 1: equivalent hex escape
 +            diag.span_suggestion(
 +                span,
 +                "if an octal escape was intended, use the hexadecimal representation instead",
 +                suggest_1,
 +                Applicability::MaybeIncorrect,
 +            );
 +            // suggestion 2: unambiguous null byte
 +            diag.span_suggestion(
 +                span,
 +                &format!(
 +                    "if the null {} is intended, disambiguate using",
 +                    if is_string { "character" } else { "byte" }
 +                ),
 +                suggest_2,
 +                Applicability::MaybeIncorrect,
 +            );
 +        },
 +    );
 +}
index 8e61f2347767dfa85bfaa47754b4002f69dec360,0000000000000000000000000000000000000000..beb812793f81ccfe5d40a44ef3c6840051d3bc6a
mode 100644,000000..100644
--- /dev/null
@@@ -1,668 -1,0 +1,660 @@@
- use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 +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 rustc_errors::Applicability;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
-     complexity,
++use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor};
 +use rustc_hir::{
 +    Arm, Block, Body, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path, PathSegment,
 +    QPath, Stmt, StmtKind, TyKind, UnOp,
 +};
 +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_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.60.0"]
 +    pub ONLY_USED_IN_RECURSION,
-                 ty_ctx: cx.tcx,
++    nursery,
 +    "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,
-     match ty.kind() {
-         ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
-         ty::Ref(_, t, _) => is_primitive(*t),
-         _ => false,
-     }
++                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);
 +                                }
 +                            }
 +                        }
 +                    }
 +
 +                    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,
 +                        );
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +pub fn is_primitive(ty: Ty<'_>) -> bool {
-     match ty.kind() {
-         ty::Array(..) | ty::Slice(..) => true,
-         ty::Ref(_, t, _) => is_array(*t),
-         _ => false,
-     }
++    let ty = ty.peel_refs();
++    ty.is_primitive() || ty.is_str()
 +}
 +
 +pub fn is_array(ty: Ty<'_>) -> bool {
-     ty_ctx: TyCtxt<'tcx>,
++    let ty = ty.peel_refs();
++    ty.is_array() || ty.is_array_slice()
 +}
 +
 +/// 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>,
-     fn visit_block(&mut self, b: &'tcx Block<'tcx>) {
-         b.stmts.iter().for_each(|stmt| {
-             self.visit_stmt(stmt);
-             self.ret_vars.clear();
-         });
-         walk_list!(self, visit_expr, b.expr);
-     }
++    tcx: TyCtxt<'tcx>,
++    visited_exprs: FxHashSet<HirId>,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
-                 self.ret_vars.clear();
 +    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(i) => {
-                 let item = self.ty_ctx.hir().item(i);
-                 self.visit_item(item);
-                 self.ret_vars.clear();
-             },
-             StmtKind::Expr(e) | StmtKind::Semi(e) => {
-                 self.visit_expr(e);
-                 self.ret_vars.clear();
 +            },
-                 let body = self.ty_ctx.hir().body(body_id);
++            StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => {
++                walk_stmt(self, s);
 +            },
 +            StmtKind::Local(_) => {},
 +        }
++        self.ret_vars.clear();
 +    }
 +
 +    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(_, _, body_id, _, _) => {
++                let body = self.tcx.hir().body(body_id);
 +                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 visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) {
 +        if let Res::Local(id) = path.res {
 +            self.ret_vars.push((id, false));
 +        }
 +    }
 +}
 +
 +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 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 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);
 +        }
 +    }
 +
 +    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;
 +            },
 +        }
 +    }
 +
 +    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;
 +            }
 +        }
 +
 +        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;
 +            }
 +        }
 +
 +        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;
 +        }
 +
 +        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 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(_, 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());
 +                }
 +                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);
 +            }
 +        }
 +
 +        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;
 +        }
 +    }
 +}
index c9f807f2aa3aadfe756369ac9e889dc8cafaf182,0000000000000000000000000000000000000000..ea5a8f0858b66aa14d084b55a585fa8ae7950be0
mode 100644,000000..100644
--- /dev/null
@@@ -1,186 -1,0 +1,186 @@@
- struct OptionIfLetElseOccurence {
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +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_hir::LangItem::OptionSome;
 +use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, Mutability, PatKind, Path, QPath, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Lints usage of `if let Some(v) = ... { y } else { x }` which is 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 _ = 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_else(||{
 +    ///     let y = do_complicated_function();
 +    ///     y*y
 +    /// }, |foo| foo);
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub OPTION_IF_LET_ELSE,
 +    nursery,
 +    "reimplementation of Option::map_or"
 +}
 +
 +declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
 +
 +/// 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.
- /// this function returns an `OptionIfLetElseOccurence` struct with details if
++struct OptionIfLetElseOccurrence {
 +    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 this expression is the option if let/else construct we're detecting, then
- fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurence> {
++/// this function returns an `OptionIfLetElseOccurrence` struct with details if
 +/// this construct is found, or None if this construct is not found.
-             Some(OptionIfLetElseOccurence {
++fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurrence> {
 +    if_chain! {
 +        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;
 +        if let Some(some_captures) = can_move_expr_to_closure(cx, if_then);
 +        if let Some(none_captures) = can_move_expr_to_closure(cx, if_else);
 +        if some_captures
 +            .iter()
 +            .filter_map(|(id, &c)| none_captures.get(id).map(|&c2| (c, c2)))
 +            .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref());
 +
 +        then {
 +            let capture_mut = if bind_annotation == &BindingAnnotation::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();
 +            let (as_ref, as_mut) = match &let_expr.kind {
 +                ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
 +                ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
 +                _ => (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,
 +            };
 +            // 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(|| ()).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 => (),
 +                    }
 +                }
 +            }
++            Some(OptionIfLetElseOccurrence {
 +                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, "..")),
 +            })
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
 +        if let Some(detection) = detect_option_if_let_else(cx, expr) {
 +            span_lint_and_sugg(
 +                cx,
 +                OPTION_IF_LET_ELSE,
 +                expr.span,
 +                format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(),
 +                "try",
 +                format!(
 +                    "{}.{}({}, {})",
 +                    detection.option, detection.method_sugg, detection.none_expr, detection.some_expr,
 +                ),
 +                Applicability::MaybeIncorrect,
 +            );
 +        }
 +    }
 +}
index 48a2666a2e0cef399b2cfeb4a5fd3e808d9b1655,0000000000000000000000000000000000000000..c35eeeac67a35c0567501ba3b38e7dcd1bc61f5e
mode 100644,000000..100644
--- /dev/null
@@@ -1,666 -1,0 +1,675 @@@
-     self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArg,
 +//! Checks for usage of  `&Vec[_]` and `&String`.
 +
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::expr_sig;
++use clippy_utils::visitors::contains_unsafe_block;
 +use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths};
 +use if_chain::if_chain;
 +use rustc_errors::{Applicability, MultiSpan};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::hir_id::HirIdMap;
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{
-     TraitItem, TraitItemKind, TyKind,
++    self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg,
 +    ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn,
-     /// This lint checks for functions that take immutable
-     /// references and return mutable ones.
++    TraitItem, TraitItemKind, TyKind, Unsafety,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::sym;
 +use rustc_span::symbol::Symbol;
 +use std::fmt;
 +use std::iter;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for function arguments of type `&String`, `&Vec`,
 +    /// `&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls
 +    /// with the appropriate `.to_owned()`/`to_string()` calls.
 +    ///
 +    /// ### Why is this bad?
 +    /// Requiring the argument to be of the specific size
 +    /// makes the function less useful for no benefit; slices in the form of `&[T]`
 +    /// or `&str` usually suffice and can be obtained from other types, too.
 +    ///
 +    /// ### Known problems
 +    /// There may be `fn(&Vec)`-typed references pointing to your function.
 +    /// If you have them, you will get a compiler error after applying this lint's
 +    /// suggestions. You then have the choice to undo your changes or change the
 +    /// type of the reference.
 +    ///
 +    /// Note that if the function is part of your public interface, there may be
 +    /// other crates referencing it, of which you may not be aware. Carefully
 +    /// deprecate the function before applying the lint suggestions in this case.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// // Bad
 +    /// fn foo(&Vec<u32>) { .. }
 +    ///
 +    /// // Good
 +    /// fn foo(&[u32]) { .. }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub PTR_ARG,
 +    style,
 +    "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for equality comparisons with `ptr::null`
 +    ///
 +    /// ### Why is this bad?
 +    /// It's easier and more readable to use the inherent
 +    /// `.is_null()`
 +    /// method instead
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// // Bad
 +    /// if x == ptr::null {
 +    ///     ..
 +    /// }
 +    ///
 +    /// // Good
 +    /// if x.is_null() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CMP_NULL,
 +    style,
 +    "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
-     /// This is trivially unsound, as one can create two
-     /// mutable references from the same (immutable!) source.
-     /// This [error](https://github.com/rust-lang/rust/issues/39465)
-     /// actually lead to an interim Rust release 1.15.1.
++    /// This lint checks for functions that take immutable references and return
++    /// mutable ones. This will not trigger if no unsafe code exists as there
++    /// are multiple safe functions which will do this transformation
++    ///
++    /// To be on the conservative side, if there's at least one mutable
++    /// reference with the output lifetime, this lint will not trigger.
 +    ///
 +    /// ### Why is this bad?
-     /// To be on the conservative side, if there's at least one
-     /// mutable reference with the output lifetime, this lint will not trigger.
-     /// In practice, this case is unlikely anyway.
++    /// Creating a mutable reference which can be repeatably derived from an
++    /// immutable reference is unsound as it allows creating multiple live
++    /// mutable references to the same object.
++    ///
++    /// This [error](https://github.com/rust-lang/rust/issues/39465) actually
++    /// lead to an interim Rust release 1.15.1.
 +    ///
 +    /// ### Known problems
-             check_mut_from_ref(cx, sig.decl);
++    /// This pattern is used by memory allocators to allow allocating multiple
++    /// objects while returning mutable references to each one. So long as
++    /// different mutable references are returned each time such a function may
++    /// be safe.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// fn foo(&Foo) -> &mut Bar { .. }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MUT_FROM_REF,
 +    correctness,
 +    "fns that create mutable refs from immutable ref args"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for invalid usages of `ptr::null`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This causes undefined behavior.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// // Bad. Undefined behavior
 +    /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); }
 +    /// ```
 +    ///
 +    /// ```ignore
 +    /// // Good
 +    /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub INVALID_NULL_PTR_USAGE,
 +    correctness,
 +    "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
 +}
 +
 +declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Ptr {
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if let TraitItemKind::Fn(sig, trait_method) = &item.kind {
 +            if matches!(trait_method, TraitFn::Provided(_)) {
 +                // Handled by check body.
 +                return;
 +            }
 +
-         let (item_id, decl, is_trait_item) = match parents.next() {
++            check_mut_from_ref(cx, sig, None);
 +            for arg in check_fn_args(
 +                cx,
 +                cx.tcx.fn_sig(item.def_id).skip_binder().inputs(),
 +                sig.decl.inputs,
 +                &[],
 +            )
 +            .filter(|arg| arg.mutability() == Mutability::Not)
 +            {
 +                span_lint_and_sugg(
 +                    cx,
 +                    PTR_ARG,
 +                    arg.span,
 +                    &arg.build_msg(),
 +                    "change this to",
 +                    format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
 +                    Applicability::Unspecified,
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
 +        let hir = cx.tcx.hir();
 +        let mut parents = hir.parent_iter(body.value.hir_id);
-                     (i.def_id, sig.decl, false)
++        let (item_id, sig, is_trait_item) = match parents.next() {
 +            Some((_, Node::Item(i))) => {
 +                if let ItemKind::Fn(sig, ..) = &i.kind {
-                     (i.def_id, sig.decl, false)
++                    (i.def_id, sig, false)
 +                } else {
 +                    return;
 +                }
 +            },
 +            Some((_, Node::ImplItem(i))) => {
 +                if !matches!(parents.next(),
 +                    Some((_, Node::Item(i))) if matches!(&i.kind, ItemKind::Impl(i) if i.of_trait.is_none())
 +                ) {
 +                    return;
 +                }
 +                if let ImplItemKind::Fn(sig, _) = &i.kind {
-                     (i.def_id, sig.decl, true)
++                    (i.def_id, sig, false)
 +                } else {
 +                    return;
 +                }
 +            },
 +            Some((_, Node::TraitItem(i))) => {
 +                if let TraitItemKind::Fn(sig, _) = &i.kind {
-         check_mut_from_ref(cx, decl);
++                    (i.def_id, sig, true)
 +                } else {
 +                    return;
 +                }
 +            },
 +            _ => return,
 +        };
 +
- fn check_mut_from_ref(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
-     if let FnRetTy::Return(ty) = decl.output {
-         if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) {
-             let mut immutables = vec![];
-             for (_, mutbl, argspan) in decl
-                 .inputs
-                 .iter()
-                 .filter_map(get_rptr_lm)
-                 .filter(|&(lt, _, _)| lt.name == out.name)
-             {
-                 if mutbl == Mutability::Mut {
-                     return;
-                 }
-                 immutables.push(argspan);
-             }
-             if immutables.is_empty() {
-                 return;
-             }
++        check_mut_from_ref(cx, sig, Some(body));
++        let decl = sig.decl;
 +        let sig = cx.tcx.fn_sig(item_id).skip_binder();
 +        let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params)
 +            .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
 +            .collect();
 +        let results = check_ptr_arg_usage(cx, body, &lint_args);
 +
 +        for (result, args) in results.iter().zip(lint_args.iter()).filter(|(r, _)| !r.skip) {
 +            span_lint_and_then(cx, PTR_ARG, args.span, &args.build_msg(), |diag| {
 +                diag.multipart_suggestion(
 +                    "change this to",
 +                    iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx))))
 +                        .chain(result.replacements.iter().map(|r| {
 +                            (
 +                                r.expr_span,
 +                                format!("{}{}", snippet_opt(cx, r.self_span).unwrap(), r.replacement),
 +                            )
 +                        }))
 +                        .collect(),
 +                    Applicability::Unspecified,
 +                );
 +            });
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::Binary(ref op, l, r) = expr.kind {
 +            if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(cx, l) || is_null_path(cx, r)) {
 +                span_lint(
 +                    cx,
 +                    CMP_NULL,
 +                    expr.span,
 +                    "comparing with null is better expressed by the `.is_null()` method",
 +                );
 +            }
 +        } else {
 +            check_invalid_ptr_usage(cx, expr);
 +        }
 +    }
 +}
 +
 +fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +    // (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B.
 +    const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 16] = [
 +        (&paths::SLICE_FROM_RAW_PARTS, &[0]),
 +        (&paths::SLICE_FROM_RAW_PARTS_MUT, &[0]),
 +        (&paths::PTR_COPY, &[0, 1]),
 +        (&paths::PTR_COPY_NONOVERLAPPING, &[0, 1]),
 +        (&paths::PTR_READ, &[0]),
 +        (&paths::PTR_READ_UNALIGNED, &[0]),
 +        (&paths::PTR_READ_VOLATILE, &[0]),
 +        (&paths::PTR_REPLACE, &[0]),
 +        (&paths::PTR_SLICE_FROM_RAW_PARTS, &[0]),
 +        (&paths::PTR_SLICE_FROM_RAW_PARTS_MUT, &[0]),
 +        (&paths::PTR_SWAP, &[0, 1]),
 +        (&paths::PTR_SWAP_NONOVERLAPPING, &[0, 1]),
 +        (&paths::PTR_WRITE, &[0]),
 +        (&paths::PTR_WRITE_UNALIGNED, &[0]),
 +        (&paths::PTR_WRITE_VOLATILE, &[0]),
 +        (&paths::PTR_WRITE_BYTES, &[0]),
 +    ];
 +
 +    if_chain! {
 +        if let ExprKind::Call(fun, args) = expr.kind;
 +        if let ExprKind::Path(ref qpath) = fun.kind;
 +        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
 +        let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::<Vec<_>>();
 +        if let Some(&(_, arg_indices)) = INVALID_NULL_PTR_USAGE_TABLE
 +            .iter()
 +            .find(|&&(fn_path, _)| fn_path == fun_def_path);
 +        then {
 +            for &arg_idx in arg_indices {
 +                if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        INVALID_NULL_PTR_USAGE,
 +                        arg.span,
 +                        "pointer must be non-null",
 +                        "change this to",
 +                        "core::ptr::NonNull::dangling().as_ptr()".to_string(),
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#[derive(Default)]
 +struct PtrArgResult {
 +    skip: bool,
 +    replacements: Vec<PtrArgReplacement>,
 +}
 +
 +struct PtrArgReplacement {
 +    expr_span: Span,
 +    self_span: Span,
 +    replacement: &'static str,
 +}
 +
 +struct PtrArg<'tcx> {
 +    idx: usize,
 +    span: Span,
 +    ty_did: DefId,
 +    ty_name: Symbol,
 +    method_renames: &'static [(&'static str, &'static str)],
 +    ref_prefix: RefPrefix,
 +    deref_ty: DerefTy<'tcx>,
 +}
 +impl PtrArg<'_> {
 +    fn build_msg(&self) -> String {
 +        format!(
 +            "writing `&{}{}` instead of `&{}{}` involves a new object where a slice will do",
 +            self.ref_prefix.mutability.prefix_str(),
 +            self.ty_name,
 +            self.ref_prefix.mutability.prefix_str(),
 +            self.deref_ty.argless_str(),
 +        )
 +    }
 +
 +    fn mutability(&self) -> Mutability {
 +        self.ref_prefix.mutability
 +    }
 +}
 +
 +struct RefPrefix {
 +    lt: LifetimeName,
 +    mutability: Mutability,
 +}
 +impl fmt::Display for RefPrefix {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        use fmt::Write;
 +        f.write_char('&')?;
 +        match self.lt {
 +            LifetimeName::Param(ParamName::Plain(name)) => {
 +                name.fmt(f)?;
 +                f.write_char(' ')?;
 +            },
 +            LifetimeName::Underscore => f.write_str("'_ ")?,
 +            LifetimeName::Static => f.write_str("'static ")?,
 +            _ => (),
 +        }
 +        f.write_str(self.mutability.prefix_str())
 +    }
 +}
 +
 +struct DerefTyDisplay<'a, 'tcx>(&'a LateContext<'tcx>, &'a DerefTy<'tcx>);
 +impl fmt::Display for DerefTyDisplay<'_, '_> {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        use std::fmt::Write;
 +        match self.1 {
 +            DerefTy::Str => f.write_str("str"),
 +            DerefTy::Path => f.write_str("Path"),
 +            DerefTy::Slice(hir_ty, ty) => {
 +                f.write_char('[')?;
 +                match hir_ty.and_then(|s| snippet_opt(self.0, s)) {
 +                    Some(s) => f.write_str(&s)?,
 +                    None => ty.fmt(f)?,
 +                }
 +                f.write_char(']')
 +            },
 +        }
 +    }
 +}
 +
 +enum DerefTy<'tcx> {
 +    Str,
 +    Path,
 +    Slice(Option<Span>, Ty<'tcx>),
 +}
 +impl<'tcx> DerefTy<'tcx> {
 +    fn argless_str(&self) -> &'static str {
 +        match *self {
 +            Self::Str => "str",
 +            Self::Path => "Path",
 +            Self::Slice(..) => "[_]",
 +        }
 +    }
 +
 +    fn display<'a>(&'a self, cx: &'a LateContext<'tcx>) -> DerefTyDisplay<'a, 'tcx> {
 +        DerefTyDisplay(cx, self)
 +    }
 +}
 +
 +fn check_fn_args<'cx, 'tcx: 'cx>(
 +    cx: &'cx LateContext<'tcx>,
 +    tys: &'tcx [Ty<'_>],
 +    hir_tys: &'tcx [hir::Ty<'_>],
 +    params: &'tcx [Param<'_>],
 +) -> impl Iterator<Item = PtrArg<'tcx>> + 'cx {
 +    tys.iter()
 +        .zip(hir_tys.iter())
 +        .enumerate()
 +        .filter_map(|(i, (ty, hir_ty))| {
 +            if_chain! {
 +                if let ty::Ref(_, ty, mutability) = *ty.kind();
 +                if let ty::Adt(adt, substs) = *ty.kind();
 +
 +                if let TyKind::Rptr(lt, ref ty) = hir_ty.kind;
 +                if let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind;
 +
 +                // Check that the name as typed matches the actual name of the type.
 +                // e.g. `fn foo(_: &Foo)` shouldn't trigger the lint when `Foo` is an alias for `Vec`
 +                if let [.., name] = path.segments;
 +                if cx.tcx.item_name(adt.did()) == name.ident.name;
 +
 +                if !is_lint_allowed(cx, PTR_ARG, hir_ty.hir_id);
 +                if params.get(i).map_or(true, |p| !is_lint_allowed(cx, PTR_ARG, p.hir_id));
 +
 +                then {
 +                    let (method_renames, deref_ty) = match cx.tcx.get_diagnostic_name(adt.did()) {
 +                        Some(sym::Vec) => (
 +                            [("clone", ".to_owned()")].as_slice(),
 +                            DerefTy::Slice(
 +                                name.args
 +                                    .and_then(|args| args.args.first())
 +                                    .and_then(|arg| if let GenericArg::Type(ty) = arg {
 +                                        Some(ty.span)
 +                                    } else {
 +                                        None
 +                                    }),
 +                                substs.type_at(0),
 +                            ),
 +                        ),
 +                        Some(sym::String) => (
 +                            [("clone", ".to_owned()"), ("as_str", "")].as_slice(),
 +                            DerefTy::Str,
 +                        ),
 +                        Some(sym::PathBuf) => (
 +                            [("clone", ".to_path_buf()"), ("as_path", "")].as_slice(),
 +                            DerefTy::Path,
 +                        ),
 +                        Some(sym::Cow) if mutability == Mutability::Not => {
 +                            let ty_name = name.args
 +                                .and_then(|args| {
 +                                    args.args.iter().find_map(|a| match a {
 +                                        GenericArg::Type(x) => Some(x),
 +                                        _ => None,
 +                                    })
 +                                })
 +                                .and_then(|arg| snippet_opt(cx, arg.span))
 +                                .unwrap_or_else(|| substs.type_at(1).to_string());
 +                            span_lint_and_sugg(
 +                                cx,
 +                                PTR_ARG,
 +                                hir_ty.span,
 +                                "using a reference to `Cow` is not recommended",
 +                                "change this to",
 +                                format!("&{}{}", mutability.prefix_str(), ty_name),
 +                                Applicability::Unspecified,
 +                            );
 +                            return None;
 +                        },
 +                        _ => return None,
 +                    };
 +                    return Some(PtrArg {
 +                        idx: i,
 +                        span: hir_ty.span,
 +                        ty_did: adt.did(),
 +                        ty_name: name.ident.name,
 +                        method_renames,
 +                        ref_prefix: RefPrefix {
 +                            lt: lt.name,
 +                            mutability,
 +                        },
 +                        deref_ty,
 +                    });
 +                }
 +            }
 +            None
 +        })
 +}
 +
-                     let ms = MultiSpan::from_spans(immutables);
++fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) {
++    if let FnRetTy::Return(ty) = sig.decl.output
++        && let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty)
++    {
++        let args: Option<Vec<_>> = sig
++            .decl
++            .inputs
++            .iter()
++            .filter_map(get_rptr_lm)
++            .filter(|&(lt, _, _)| lt.name == out.name)
++            .map(|(_, mutability, span)| (mutability == Mutability::Not).then(|| span))
++            .collect();
++        if let Some(args) = args
++            && !args.is_empty()
++            && body.map_or(true, |body| {
++                sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, &body.value)
++            })
++        {
 +            span_lint_and_then(
 +                cx,
 +                MUT_FROM_REF,
 +                ty.span,
 +                "mutable borrow from immutable input(s)",
 +                |diag| {
++                    let ms = MultiSpan::from_spans(args);
 +                    diag.span_note(ms, "immutable borrow here");
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: &[PtrArg<'tcx>]) -> Vec<PtrArgResult> {
 +    struct V<'cx, 'tcx> {
 +        cx: &'cx LateContext<'tcx>,
 +        /// Map from a local id to which argument it came from (index into `Self::args` and
 +        /// `Self::results`)
 +        bindings: HirIdMap<usize>,
 +        /// The arguments being checked.
 +        args: &'cx [PtrArg<'tcx>],
 +        /// The results for each argument (len should match args.len)
 +        results: Vec<PtrArgResult>,
 +        /// The number of arguments which can't be linted. Used to return early.
 +        skip_count: usize,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.skip_count == self.args.len() {
 +                return;
 +            }
 +
 +            // Check if this is local we care about
 +            let args_idx = match path_to_local(e).and_then(|id| self.bindings.get(&id)) {
 +                Some(&i) => i,
 +                None => return walk_expr(self, e),
 +            };
 +            let args = &self.args[args_idx];
 +            let result = &mut self.results[args_idx];
 +
 +            // Helper function to handle early returns.
 +            let mut set_skip_flag = || {
 +                if !result.skip {
 +                    self.skip_count += 1;
 +                }
 +                result.skip = true;
 +            };
 +
 +            match get_expr_use_or_unification_node(self.cx.tcx, e) {
 +                Some((Node::Stmt(_), _)) => (),
 +                Some((Node::Local(l), _)) => {
 +                    // Only trace simple bindings. e.g `let x = y;`
 +                    if let PatKind::Binding(BindingAnnotation::Unannotated, id, _, None) = l.pat.kind {
 +                        self.bindings.insert(id, args_idx);
 +                    } else {
 +                        set_skip_flag();
 +                    }
 +                },
 +                Some((Node::Expr(e), child_id)) => match e.kind {
 +                    ExprKind::Call(f, expr_args) => {
 +                        let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
 +                        if expr_sig(self.cx, f)
 +                            .map(|sig| sig.input(i).skip_binder().peel_refs())
 +                            .map_or(true, |ty| match *ty.kind() {
 +                                ty::Param(_) => true,
 +                                ty::Adt(def, _) => def.did() == args.ty_did,
 +                                _ => false,
 +                            })
 +                        {
 +                            // Passed to a function taking the non-dereferenced type.
 +                            set_skip_flag();
 +                        }
 +                    },
 +                    ExprKind::MethodCall(name, expr_args @ [self_arg, ..], _) => {
 +                        let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
 +                        if i == 0 {
 +                            // Check if the method can be renamed.
 +                            let name = name.ident.as_str();
 +                            if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
 +                                result.replacements.push(PtrArgReplacement {
 +                                    expr_span: e.span,
 +                                    self_span: self_arg.span,
 +                                    replacement,
 +                                });
 +                                return;
 +                            }
 +                        }
 +
 +                        let id = if let Some(x) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) {
 +                            x
 +                        } else {
 +                            set_skip_flag();
 +                            return;
 +                        };
 +
 +                        match *self.cx.tcx.fn_sig(id).skip_binder().inputs()[i].peel_refs().kind() {
 +                            ty::Param(_) => {
 +                                set_skip_flag();
 +                            },
 +                            // If the types match check for methods which exist on both types. e.g. `Vec::len` and
 +                            // `slice::len`
 +                            ty::Adt(def, _) if def.did() == args.ty_did => {
 +                                set_skip_flag();
 +                            },
 +                            _ => (),
 +                        }
 +                    },
 +                    // Indexing is fine for currently supported types.
 +                    ExprKind::Index(e, _) if e.hir_id == child_id => (),
 +                    _ => set_skip_flag(),
 +                },
 +                _ => set_skip_flag(),
 +            }
 +        }
 +    }
 +
 +    let mut skip_count = 0;
 +    let mut results = args.iter().map(|_| PtrArgResult::default()).collect::<Vec<_>>();
 +    let mut v = V {
 +        cx,
 +        bindings: args
 +            .iter()
 +            .enumerate()
 +            .filter_map(|(i, arg)| {
 +                let param = &body.params[arg.idx];
 +                match param.pat.kind {
 +                    PatKind::Binding(BindingAnnotation::Unannotated, id, _, None)
 +                        if !is_lint_allowed(cx, PTR_ARG, param.hir_id) =>
 +                    {
 +                        Some((id, i))
 +                    },
 +                    _ => {
 +                        skip_count += 1;
 +                        results[i].skip = true;
 +                        None
 +                    },
 +                }
 +            })
 +            .collect(),
 +        args,
 +        results,
 +        skip_count,
 +    };
 +    v.visit_expr(&body.value);
 +    v.results
 +}
 +
 +fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
 +    if let TyKind::Rptr(ref lt, ref m) = ty.kind {
 +        Some((lt, m.mutbl, ty.span))
 +    } else {
 +        None
 +    }
 +}
 +
 +fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Call(pathexp, []) = expr.kind {
 +        path_def_id(cx, pathexp).map_or(false, |id| {
 +            matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))
 +        })
 +    } else {
 +        false
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9d2b0cedb60a165f18a843a25cb8817468654f00
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++use clippy_utils::diagnostics::span_lint_and_help;
++use rustc_ast::ast::{Item, ItemKind, VisibilityKind};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// ### What it does
++    ///
++    /// Restricts the usage of `pub use ...`
++    ///
++    /// ### Why is this bad?
++    ///
++    /// `pub use` is usually fine, but a project may wish to limit `pub use` instances to prevent
++    /// unintentional exports or to encourage placing exported items directly in public modules
++    ///
++    /// ### Example
++    /// ```rust
++    /// pub mod outer {
++    ///     mod inner {
++    ///         pub struct Test {}
++    ///     }
++    ///     pub use inner::Test;
++    /// }
++    ///
++    /// use outer::Test;
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// pub mod outer {
++    ///     pub struct Test {}
++    /// }
++    ///
++    /// use outer::Test;
++    /// ```
++    #[clippy::version = "1.62.0"]
++    pub PUB_USE,
++    restriction,
++    "restricts the usage of `pub use`"
++}
++declare_lint_pass!(PubUse => [PUB_USE]);
++
++impl EarlyLintPass for PubUse {
++    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
++        if let ItemKind::Use(_) = item.kind &&
++            let VisibilityKind::Public = item.vis.kind {
++                span_lint_and_help(
++                    cx,
++                    PUB_USE,
++                    item.span,
++                    "using `pub use`",
++                    None,
++                    "move the exported item to a public module instead",
++                );
++            }
++    }
++}
index e2e2400f8e267ae8e398778ed0de72ba17b1bdbb,0000000000000000000000000000000000000000..323326381d4079149ce2591abd3c6a402b4e453b
mode 100644,000000..100644
--- /dev/null
@@@ -1,77 -1,0 +1,94 @@@
-         if cx.tcx.visibility(item.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()) {
-             if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false) {
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use rustc_errors::Applicability;
++use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::{Item, ItemKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::def_id::CRATE_DEF_ID;
++use rustc_span::hygiene::MacroKind;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for items declared `pub(crate)` that are not crate visible because they
 +    /// are inside a private module.
 +    ///
 +    /// ### Why is this bad?
 +    /// Writing `pub(crate)` is misleading when it's redundant due to the parent
 +    /// module's visibility.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// mod internal {
 +    ///     pub(crate) fn internal_fn() { }
 +    /// }
 +    /// ```
 +    /// This function is not visible outside the module and it can be declared with `pub` or
 +    /// private visibility
 +    /// ```rust
 +    /// mod internal {
 +    ///     pub fn internal_fn() { }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.44.0"]
 +    pub REDUNDANT_PUB_CRATE,
 +    nursery,
 +    "Using `pub(crate)` visibility on items that are not crate visible due to the visibility of the module that contains them."
 +}
 +
 +#[derive(Default)]
 +pub struct RedundantPubCrate {
 +    is_exported: Vec<bool>,
 +}
 +
 +impl_lint_pass!(RedundantPubCrate => [REDUNDANT_PUB_CRATE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
++        if_chain! {
++            if cx.tcx.visibility(item.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id());
++            if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false);
++            if is_not_macro_export(item);
++            then {
 +                let span = item.span.with_hi(item.ident.span.hi());
 +                let descr = cx.tcx.def_kind(item.def_id).descr(item.def_id.to_def_id());
 +                span_lint_and_then(
 +                    cx,
 +                    REDUNDANT_PUB_CRATE,
 +                    span,
 +                    &format!("pub(crate) {} inside private module", descr),
 +                    |diag| {
 +                        diag.span_suggestion(
 +                            item.vis_span,
 +                            "consider using",
 +                            "pub".to_string(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                );
 +            }
 +        }
 +
 +        if let ItemKind::Mod { .. } = item.kind {
 +            self.is_exported.push(cx.access_levels.is_exported(item.def_id));
 +        }
 +    }
 +
 +    fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
 +        if let ItemKind::Mod { .. } = item.kind {
 +            self.is_exported.pop().expect("unbalanced check_item/check_item_post");
 +        }
 +    }
 +}
++
++fn is_not_macro_export<'tcx>(item: &'tcx Item<'tcx>) -> bool {
++    if let ItemKind::Use(path, _) = item.kind {
++        if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = path.res {
++            return false;
++        }
++    } else if let ItemKind::Macro(..) = item.kind {
++        return false;
++    }
++
++    true
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bfc03116fe2d9542678069c605ebf0088137b99a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++// This file is managed by `cargo dev rename_lint`. Prefer using that when possible.
++
++#[rustfmt::skip]
++pub static RENAMED_LINTS: &[(&str, &str)] = &[
++    ("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"),
++    ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"),
++    ("clippy::box_vec", "clippy::box_collection"),
++    ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"),
++    ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"),
++    ("clippy::disallowed_method", "clippy::disallowed_methods"),
++    ("clippy::disallowed_type", "clippy::disallowed_types"),
++    ("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"),
++    ("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"),
++    ("clippy::identity_conversion", "clippy::useless_conversion"),
++    ("clippy::if_let_some_result", "clippy::match_result_ok"),
++    ("clippy::new_without_default_derive", "clippy::new_without_default"),
++    ("clippy::option_and_then_some", "clippy::bind_instead_of_map"),
++    ("clippy::option_expect_used", "clippy::expect_used"),
++    ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"),
++    ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"),
++    ("clippy::option_unwrap_used", "clippy::unwrap_used"),
++    ("clippy::ref_in_deref", "clippy::needless_borrow"),
++    ("clippy::result_expect_used", "clippy::expect_used"),
++    ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"),
++    ("clippy::result_unwrap_used", "clippy::unwrap_used"),
++    ("clippy::single_char_push_str", "clippy::single_char_add_str"),
++    ("clippy::stutter", "clippy::module_name_repetitions"),
++    ("clippy::to_string_in_display", "clippy::recursive_format_impl"),
++    ("clippy::zero_width_space", "clippy::invisible_characters"),
++    ("clippy::drop_bounds", "drop_bounds"),
++    ("clippy::into_iter_on_array", "array_into_iter"),
++    ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"),
++    ("clippy::invalid_ref", "invalid_value"),
++    ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"),
++    ("clippy::panic_params", "non_fmt_panics"),
++    ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"),
++    ("clippy::unknown_clippy_lints", "unknown_lints"),
++    ("clippy::unused_label", "unused_labels"),
++];
index a01e2f2db3afb2ee9c52ff3e6645c37ebaef4fe6,0000000000000000000000000000000000000000..f63925a2f1438ea8a71e6f500f1a5c11a70cd55e
mode 100644,000000..100644
--- /dev/null
@@@ -1,166 -1,0 +1,162 @@@
-             if !matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl) {
-                 continue;
-             }
-             let item = cx.tcx.hir().item(id);
-             if let ItemKind::Impl(Impl {
-                 items,
-                 of_trait,
-                 self_ty,
-                 ..
-             }) = &item.kind
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::{Impl, ItemKind, Node, Path, QPath, TraitRef, TyKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::AssocKind;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::Symbol;
 +use rustc_span::Span;
 +use std::collections::{BTreeMap, BTreeSet};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// It lints if a struct has two methods with the same name:
 +    /// one from a trait, another not from trait.
 +    ///
 +    /// ### Why is this bad?
 +    /// Confusing.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// trait T {
 +    ///     fn foo(&self) {}
 +    /// }
 +    ///
 +    /// struct S;
 +    ///
 +    /// impl T for S {
 +    ///     fn foo(&self) {}
 +    /// }
 +    ///
 +    /// impl S {
 +    ///     fn foo(&self) {}
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.57.0"]
 +    pub SAME_NAME_METHOD,
 +    restriction,
 +    "two method with same name"
 +}
 +
 +declare_lint_pass!(SameNameMethod => [SAME_NAME_METHOD]);
 +
 +struct ExistingName {
 +    impl_methods: BTreeMap<Symbol, Span>,
 +    trait_methods: BTreeMap<Symbol, Vec<Span>>,
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
 +    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
 +        let mut map = FxHashMap::<Res, ExistingName>::default();
 +
 +        for id in cx.tcx.hir().items() {
-                 if let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind {
-                     if !map.contains_key(res) {
-                         map.insert(
-                             *res,
-                             ExistingName {
-                                 impl_methods: BTreeMap::new(),
-                                 trait_methods: BTreeMap::new(),
-                             },
-                         );
-                     }
-                     let existing_name = map.get_mut(res).unwrap();
-                     match of_trait {
-                         Some(trait_ref) => {
-                             let mut methods_in_trait: BTreeSet<Symbol> = if_chain! {
-                                 if let Some(Node::TraitRef(TraitRef { path, .. })) =
-                                     cx.tcx.hir().find(trait_ref.hir_ref_id);
-                                 if let Res::Def(DefKind::Trait, did) = path.res;
-                                 then{
-                                     // FIXME: if
-                                     // `rustc_middle::ty::assoc::AssocItems::items` is public,
-                                     // we can iterate its keys instead of `in_definition_order`,
-                                     // which's more efficient
-                                     cx.tcx
-                                         .associated_items(did)
-                                         .in_definition_order()
-                                         .filter(|assoc_item| {
-                                             matches!(assoc_item.kind, AssocKind::Fn)
-                                         })
-                                         .map(|assoc_item| assoc_item.name)
-                                         .collect()
-                                 }else{
-                                     BTreeSet::new()
-                                 }
-                             };
-                             let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| {
-                                 if let Some(impl_span) = existing_name.impl_methods.get(&method_name) {
-                                     span_lint_and_then(
-                                         cx,
-                                         SAME_NAME_METHOD,
-                                         *impl_span,
-                                         "method's name is the same as an existing method in a trait",
-                                         |diag| {
-                                             diag.span_note(
-                                                 trait_method_span,
-                                                 &format!("existing `{}` defined here", method_name),
-                                             );
-                                         },
-                                     );
-                                 }
-                                 if let Some(v) = existing_name.trait_methods.get_mut(&method_name) {
-                                     v.push(trait_method_span);
-                                 } else {
-                                     existing_name.trait_methods.insert(method_name, vec![trait_method_span]);
-                                 }
-                             };
++            if matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl)
++                && let item = cx.tcx.hir().item(id)
++                && let ItemKind::Impl(Impl {
++                    items,
++                    of_trait,
++                    self_ty,
++                    ..
++                }) = &item.kind
++                && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind
 +            {
-                             for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
-                                 matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
-                             }) {
-                                 let method_name = impl_item_ref.ident.name;
-                                 methods_in_trait.remove(&method_name);
-                                 check_trait_method(method_name, impl_item_ref.span);
++                if !map.contains_key(res) {
++                    map.insert(
++                        *res,
++                        ExistingName {
++                            impl_methods: BTreeMap::new(),
++                            trait_methods: BTreeMap::new(),
++                        },
++                    );
++                }
++                let existing_name = map.get_mut(res).unwrap();
 +
-                             for method_name in methods_in_trait {
-                                 check_trait_method(method_name, item.span);
++                match of_trait {
++                    Some(trait_ref) => {
++                        let mut methods_in_trait: BTreeSet<Symbol> = if_chain! {
++                            if let Some(Node::TraitRef(TraitRef { path, .. })) =
++                                cx.tcx.hir().find(trait_ref.hir_ref_id);
++                            if let Res::Def(DefKind::Trait, did) = path.res;
++                            then{
++                                // FIXME: if
++                                // `rustc_middle::ty::assoc::AssocItems::items` is public,
++                                // we can iterate its keys instead of `in_definition_order`,
++                                // which's more efficient
++                                cx.tcx
++                                    .associated_items(did)
++                                    .in_definition_order()
++                                    .filter(|assoc_item| {
++                                        matches!(assoc_item.kind, AssocKind::Fn)
++                                    })
++                                    .map(|assoc_item| assoc_item.name)
++                                    .collect()
++                            }else{
++                                BTreeSet::new()
 +                            }
++                        };
 +
-                         },
-                         None => {
-                             for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
-                                 matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
-                             }) {
-                                 let method_name = impl_item_ref.ident.name;
-                                 let impl_span = impl_item_ref.span;
-                                 if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) {
-                                     span_lint_and_then(
-                                         cx,
-                                         SAME_NAME_METHOD,
-                                         impl_span,
-                                         "method's name is the same as an existing method in a trait",
-                                         |diag| {
-                                             // TODO should we `span_note` on every trait?
-                                             // iterate on trait_spans?
-                                             diag.span_note(
-                                                 trait_spans[0],
-                                                 &format!("existing `{}` defined here", method_name),
-                                             );
-                                         },
-                                     );
-                                 }
-                                 existing_name.impl_methods.insert(method_name, impl_span);
++                        let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| {
++                            if let Some(impl_span) = existing_name.impl_methods.get(&method_name) {
++                                span_lint_and_then(
++                                    cx,
++                                    SAME_NAME_METHOD,
++                                    *impl_span,
++                                    "method's name is the same as an existing method in a trait",
++                                    |diag| {
++                                        diag.span_note(
++                                            trait_method_span,
++                                            &format!("existing `{}` defined here", method_name),
++                                        );
++                                    },
++                                );
 +                            }
-                         },
-                     }
++                            if let Some(v) = existing_name.trait_methods.get_mut(&method_name) {
++                                v.push(trait_method_span);
++                            } else {
++                                existing_name.trait_methods.insert(method_name, vec![trait_method_span]);
 +                            }
++                        };
++
++                        for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
++                            matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
++                        }) {
++                            let method_name = impl_item_ref.ident.name;
++                            methods_in_trait.remove(&method_name);
++                            check_trait_method(method_name, impl_item_ref.span);
++                        }
++
++                        for method_name in methods_in_trait {
++                            check_trait_method(method_name, item.span);
++                        }
++                    },
++                    None => {
++                        for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
++                            matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
++                        }) {
++                            let method_name = impl_item_ref.ident.name;
++                            let impl_span = impl_item_ref.span;
++                            if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) {
++                                span_lint_and_then(
++                                    cx,
++                                    SAME_NAME_METHOD,
++                                    impl_span,
++                                    "method's name is the same as an existing method in a trait",
++                                    |diag| {
++                                        // TODO should we `span_note` on every trait?
++                                        // iterate on trait_spans?
++                                        diag.span_note(
++                                            trait_spans[0],
++                                            &format!("existing `{}` defined here", method_name),
++                                        );
++                                    },
++                                );
++                            }
++                            existing_name.impl_methods.insert(method_name, impl_span);
++                        }
++                    },
 +                }
 +            }
 +        }
 +    }
 +}
index bcd28b429784a488520f6107255ab106f23b7ecf,0000000000000000000000000000000000000000..a6c685df721d6fac310117306cb98cd2b46f9a96
mode 100644,000000..100644
--- /dev/null
@@@ -1,135 -1,0 +1,145 @@@
-     /// as arrays, slices, and tuples of such items), it is better to
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::{is_slice_of_primitives, sugg::Sugg};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// When sorting primitive values (integers, bools, chars, as well
-     /// Using a stable sort consumes more memory and cpu cycles. Because
-     /// values which compare equal are identical, preserving their
++    /// 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?
-     perf,
++    /// 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,
-                         "an unstable sort would perform faster without any observable difference for this data type",
++    pedantic,
 +    "use of sort() when sort_unstable() is equivalent"
 +}
 +
 +declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]);
 +
 +/// The three "kinds" of sorts
 +enum SortingKind {
 +    Vanilla,
 +    /* The other kinds of lint are currently commented out because they
 +     * can map distinct values to equal ones. If the key function is
 +     * provably one-to-one, or if the Cmp function conserves equality,
 +     * then they could be linted on, but I don't know if we can check
 +     * for that. */
 +
 +    /* ByKey,
 +     * ByCmp, */
 +}
 +impl SortingKind {
 +    /// The name of the stable version of this kind of sort
 +    fn stable_name(&self) -> &str {
 +        match self {
 +            SortingKind::Vanilla => "sort",
 +            /* SortingKind::ByKey => "sort_by_key",
 +             * SortingKind::ByCmp => "sort_by", */
 +        }
 +    }
 +    /// The name of the unstable version of this kind of sort
 +    fn unstable_name(&self) -> &str {
 +        match self {
 +            SortingKind::Vanilla => "sort_unstable",
 +            /* SortingKind::ByKey => "sort_unstable_by_key",
 +             * SortingKind::ByCmp => "sort_unstable_by", */
 +        }
 +    }
 +    /// Takes the name of a function call and returns the kind of sort
 +    /// that corresponds to that function name (or None if it isn't)
 +    fn from_stable_name(name: &str) -> Option<SortingKind> {
 +        match name {
 +            "sort" => Some(SortingKind::Vanilla),
 +            // "sort_by" => Some(SortingKind::ByCmp),
 +            // "sort_by_key" => Some(SortingKind::ByKey),
 +            _ => None,
 +        }
 +    }
 +}
 +
 +/// A detected instance of this lint
 +struct LintDetection {
 +    slice_name: String,
 +    method: SortingKind,
 +    method_args: String,
 +    slice_type: String,
 +}
 +
 +fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintDetection> {
 +    if_chain! {
 +        if let ExprKind::MethodCall(method_name, args, _) = &expr.kind;
 +        if let Some(slice) = &args.get(0);
 +        if let Some(method) = SortingKind::from_stable_name(method_name.ident.name.as_str());
 +        if let Some(slice_type) = is_slice_of_primitives(cx, slice);
 +        then {
 +            let args_str = args.iter().skip(1).map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::<Vec<String>>().join(", ");
 +            Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str, slice_type })
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +impl LateLintPass<'_> for StableSortPrimitive {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        if let Some(detection) = detect_stable_sort_primitive(cx, expr) {
 +            span_lint_and_then(
 +                cx,
 +                STABLE_SORT_PRIMITIVE,
 +                expr.span,
 +                format!(
 +                    "used `{}` on primitive type `{}`",
 +                    detection.method.stable_name(),
 +                    detection.slice_type,
 +                )
 +                .as_str(),
 +                |diag| {
 +                    diag.span_suggestion(
 +                        expr.span,
 +                        "try",
 +                        format!(
 +                            "{}.{}({})",
 +                            detection.slice_name,
 +                            detection.method.unstable_name(),
 +                            detection.method_args,
 +                        ),
 +                        Applicability::MachineApplicable,
 +                    );
 +                    diag.note(
++                        "an unstable sort typically performs faster without any observable difference for this data type",
 +                    );
 +                },
 +            );
 +        }
 +    }
 +}
index 3573f632a3671e6adde5cd641030f8beb9d85e6e,0000000000000000000000000000000000000000..7c196ccaa8ccd2fb610f430e510b4a6aaa918314
mode 100644,000000..100644
--- /dev/null
@@@ -1,453 -1,0 +1,509 @@@
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg};
 +use clippy_utils::source::{snippet, snippet_with_applicability};
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths};
 +use clippy_utils::{peel_blocks, SpanlessEq};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
++use rustc_hir::def_id::DefId;
 +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Spanned;
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for string appends of the form `x = x + y` (without
 +    /// `let`!).
 +    ///
 +    /// ### Why is this bad?
 +    /// It's not really bad, but some people think that the
 +    /// `.push_str(_)` method is more readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut x = "Hello".to_owned();
 +    /// x = x + ", World";
 +    ///
 +    /// // More readable
 +    /// x += ", World";
 +    /// x.push_str(", World");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub STRING_ADD_ASSIGN,
 +    pedantic,
 +    "using `x = x + ..` where x is a `String` instead of `push_str()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for all instances of `x + _` where `x` is of type
 +    /// `String`, but only if [`string_add_assign`](#string_add_assign) does *not*
 +    /// match.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's not bad in and of itself. However, this particular
 +    /// `Add` implementation is asymmetric (the other operand need not be `String`,
 +    /// but `x` does), while addition as mathematically defined is symmetric, also
 +    /// the `String::push_str(_)` function is a perfectly good replacement.
 +    /// Therefore, some dislike it and wish not to have it in their code.
 +    ///
 +    /// That said, other people think that string addition, having a long tradition
 +    /// in other languages is actually fine, which is why we decided to make this
 +    /// particular lint `allow` by default.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = "Hello".to_owned();
 +    /// x + ", World";
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub STRING_ADD,
 +    restriction,
 +    "using `x + ..` where x is a `String` instead of `push_str()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the `as_bytes` method called on string literals
 +    /// that contain only ASCII characters.
 +    ///
 +    /// ### Why is this bad?
 +    /// Byte string literals (e.g., `b"foo"`) can be used
 +    /// instead. They are shorter but less discoverable than `as_bytes()`.
 +    ///
 +    /// ### Known problems
 +    /// `"str".as_bytes()` and the suggested replacement of `b"str"` are not
 +    /// equivalent because they have different types. The former is `&[u8]`
 +    /// while the latter is `&[u8; 3]`. That means in general they will have a
 +    /// different set of methods and different trait implementations.
 +    ///
 +    /// ```compile_fail
 +    /// fn f(v: Vec<u8>) {}
 +    ///
 +    /// f("...".as_bytes().to_owned()); // works
 +    /// f(b"...".to_owned()); // does not work, because arg is [u8; 3] not Vec<u8>
 +    ///
 +    /// fn g(r: impl std::io::Read) {}
 +    ///
 +    /// g("...".as_bytes()); // works
 +    /// g(b"..."); // does not work
 +    /// ```
 +    ///
 +    /// The actual equivalent of `"str".as_bytes()` with the same type is not
 +    /// `b"str"` but `&b"str"[..]`, which is a great deal of punctuation and not
 +    /// more readable than a function call.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let bs = "a byte string".as_bytes();
 +    ///
 +    /// // Good
 +    /// let bs = b"a byte string";
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub STRING_LIT_AS_BYTES,
 +    nursery,
 +    "calling `as_bytes` on a string literal instead of using a byte string literal"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for slice operations on strings
 +    ///
 +    /// ### Why is this bad?
 +    /// UTF-8 characters span multiple bytes, and it is easy to inadvertently confuse character
 +    /// counts and string indices. This may lead to panics, and should warrant some test cases
 +    /// containing wide UTF-8 characters. This lint is most useful in code that should avoid
 +    /// panics at all costs.
 +    ///
 +    /// ### Known problems
 +    /// Probably lots of false positives. If an index comes from a known valid position (e.g.
 +    /// obtained via `char_indices` over the same string), it is totally OK.
 +    ///
 +    /// # Example
 +    /// ```rust,should_panic
 +    /// &"Ölkanne"[1..];
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub STRING_SLICE,
 +    restriction,
 +    "slicing a string"
 +}
 +
 +declare_lint_pass!(StringAdd => [STRING_ADD, STRING_ADD_ASSIGN, STRING_SLICE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for StringAdd {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if in_external_macro(cx.sess(), e.span) {
 +            return;
 +        }
 +        match e.kind {
 +            ExprKind::Binary(
 +                Spanned {
 +                    node: BinOpKind::Add, ..
 +                },
 +                left,
 +                _,
 +            ) => {
 +                if is_string(cx, left) {
 +                    if !is_lint_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) {
 +                        let parent = get_parent_expr(cx, e);
 +                        if let Some(p) = parent {
 +                            if let ExprKind::Assign(target, _, _) = p.kind {
 +                                // avoid duplicate matches
 +                                if SpanlessEq::new(cx).eq_expr(target, left) {
 +                                    return;
 +                                }
 +                            }
 +                        }
 +                    }
 +                    span_lint(
 +                        cx,
 +                        STRING_ADD,
 +                        e.span,
 +                        "you added something to a string. Consider using `String::push_str()` instead",
 +                    );
 +                }
 +            },
 +            ExprKind::Assign(target, src, _) => {
 +                if is_string(cx, target) && is_add(cx, src, target) {
 +                    span_lint(
 +                        cx,
 +                        STRING_ADD_ASSIGN,
 +                        e.span,
 +                        "you assigned the result of adding something to this string. Consider using \
 +                         `String::push_str()` instead",
 +                    );
 +                }
 +            },
 +            ExprKind::Index(target, _idx) => {
 +                let e_ty = cx.typeck_results().expr_ty(target).peel_refs();
 +                if matches!(e_ty.kind(), ty::Str) || is_type_diagnostic_item(cx, e_ty, sym::String) {
 +                    span_lint(
 +                        cx,
 +                        STRING_SLICE,
 +                        e.span,
 +                        "indexing into a string may panic if the index is within a UTF-8 character",
 +                    );
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String)
 +}
 +
 +fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
 +    match peel_blocks(src).kind {
 +        ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Add, ..
 +            },
 +            left,
 +            _,
 +        ) => SpanlessEq::new(cx).eq_expr(target, left),
 +        _ => false,
 +    }
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Check if the string is transformed to byte array and casted back to string.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's unnecessary, the string can be used directly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap();
 +    /// ```
 +    /// could be written as
 +    /// ```rust
 +    /// let _ = &"Hello World!"[6..11];
 +    /// ```
 +    #[clippy::version = "1.50.0"]
 +    pub STRING_FROM_UTF8_AS_BYTES,
 +    complexity,
 +    "casting string slices to byte slices and back"
 +}
 +
 +// Max length a b"foo" string can take
 +const MAX_LENGTH_BYTE_STRING_LIT: usize = 32;
 +
 +declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES, STRING_FROM_UTF8_AS_BYTES]);
 +
 +impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        use rustc_ast::LitKind;
 +
 +        if_chain! {
 +            // Find std::str::converts::from_utf8
 +            if let Some(args) = match_function_call(cx, e, &paths::STR_FROM_UTF8);
 +
 +            // Find string::as_bytes
 +            if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind;
 +            if let ExprKind::Index(left, right) = args.kind;
 +            let (method_names, expressions, _) = method_calls(left, 1);
 +            if method_names.len() == 1;
 +            if expressions.len() == 1;
 +            if expressions[0].len() == 1;
 +            if method_names[0] == sym!(as_bytes);
 +
 +            // Check for slicer
 +            if let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), _, _) = right.kind;
 +
 +            then {
 +                let mut applicability = Applicability::MachineApplicable;
 +                let string_expression = &expressions[0][0];
 +
 +                let snippet_app = snippet_with_applicability(
 +                    cx,
 +                    string_expression.span, "..",
 +                    &mut applicability,
 +                );
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    STRING_FROM_UTF8_AS_BYTES,
 +                    e.span,
 +                    "calling a slice of `as_bytes()` with `from_utf8` should be not necessary",
 +                    "try",
 +                    format!("Some(&{}[{}])", snippet_app, snippet(cx, right.span, "..")),
 +                    applicability
 +                )
 +            }
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::MethodCall(path, args, _) = &e.kind;
 +            if path.ident.name == sym!(as_bytes);
 +            if let ExprKind::Lit(lit) = &args[0].kind;
 +            if let LitKind::Str(lit_content, _) = &lit.node;
 +            then {
 +                let callsite = snippet(cx, args[0].span.source_callsite(), r#""foo""#);
 +                let mut applicability = Applicability::MachineApplicable;
 +                if callsite.starts_with("include_str!") {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        STRING_LIT_AS_BYTES,
 +                        e.span,
 +                        "calling `as_bytes()` on `include_str!(..)`",
 +                        "consider using `include_bytes!(..)` instead",
 +                        snippet_with_applicability(cx, args[0].span, r#""foo""#, &mut applicability).replacen(
 +                            "include_str",
 +                            "include_bytes",
 +                            1,
 +                        ),
 +                        applicability,
 +                    );
 +                } else if lit_content.as_str().is_ascii()
 +                    && lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT
 +                    && !args[0].span.from_expansion()
 +                {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        STRING_LIT_AS_BYTES,
 +                        e.span,
 +                        "calling `as_bytes()` on a string literal",
 +                        "consider using a byte string literal instead",
 +                        format!(
 +                            "b{}",
 +                            snippet_with_applicability(cx, args[0].span, r#""foo""#, &mut applicability)
 +                        ),
 +                        applicability,
 +                    );
 +                }
 +            }
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::MethodCall(path, [recv], _) = &e.kind;
 +            if path.ident.name == sym!(into_bytes);
 +            if let ExprKind::MethodCall(path, [recv], _) = &recv.kind;
 +            if matches!(path.ident.name.as_str(), "to_owned" | "to_string");
 +            if let ExprKind::Lit(lit) = &recv.kind;
 +            if let LitKind::Str(lit_content, _) = &lit.node;
 +
 +            if lit_content.as_str().is_ascii();
 +            if lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT;
 +            if !recv.span.from_expansion();
 +            then {
 +                let mut applicability = Applicability::MachineApplicable;
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    STRING_LIT_AS_BYTES,
 +                    e.span,
 +                    "calling `into_bytes()` on a string literal",
 +                    "consider using a byte string literal instead",
 +                    format!(
 +                        "b{}.to_vec()",
 +                        snippet_with_applicability(cx, recv.span, r#""..""#, &mut applicability)
 +                    ),
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for `.to_string()` method calls on values of type `&str`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `to_string` method is also used on other types to convert them to a string.
 +    /// When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better
 +    /// expressed with `.to_owned()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // example code where clippy issues a warning
 +    /// let _ = "str".to_string();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// // example code which does not raise clippy warning
 +    /// let _ = "str".to_owned();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub STR_TO_STRING,
 +    restriction,
 +    "using `to_string()` on a `&str`, which should be `to_owned()`"
 +}
 +
 +declare_lint_pass!(StrToString => [STR_TO_STRING]);
 +
 +impl<'tcx> LateLintPass<'tcx> for StrToString {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
 +            if path.ident.name == sym!(to_string);
 +            let ty = cx.typeck_results().expr_ty(self_arg);
 +            if let ty::Ref(_, ty, ..) = ty.kind();
 +            if *ty.kind() == ty::Str;
 +            then {
 +                span_lint_and_help(
 +                    cx,
 +                    STR_TO_STRING,
 +                    expr.span,
 +                    "`to_string()` called on a `&str`",
 +                    None,
 +                    "consider using `.to_owned()`",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for `.to_string()` method calls on values of type `String`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `to_string` method is also used on other types to convert them to a string.
 +    /// When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // example code where clippy issues a warning
 +    /// let msg = String::from("Hello World");
 +    /// let _ = msg.to_string();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// // example code which does not raise clippy warning
 +    /// let msg = String::from("Hello World");
 +    /// let _ = msg.clone();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub STRING_TO_STRING,
 +    restriction,
 +    "using `to_string()` on a `String`, which should be `clone()`"
 +}
 +
 +declare_lint_pass!(StringToString => [STRING_TO_STRING]);
 +
 +impl<'tcx> LateLintPass<'tcx> for StringToString {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
 +            if path.ident.name == sym!(to_string);
 +            let ty = cx.typeck_results().expr_ty(self_arg);
 +            if is_type_diagnostic_item(cx, ty, sym::String);
 +            then {
 +                span_lint_and_help(
 +                    cx,
 +                    STRING_TO_STRING,
 +                    expr.span,
 +                    "`to_string()` called on a `String`",
 +                    None,
 +                    "consider using `.clone()`",
 +                );
 +            }
 +        }
 +    }
 +}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Warns about calling `str::trim` (or variants) before `str::split_whitespace`.
++    ///
++    /// ### Why is this bad?
++    /// `split_whitespace` already ignores leading and trailing whitespace.
++    ///
++    /// ### Example
++    /// ```rust
++    /// " A B C ".trim().split_whitespace();
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// " A B C ".split_whitespace();
++    /// ```
++    #[clippy::version = "1.62.0"]
++    pub TRIM_SPLIT_WHITESPACE,
++    style,
++    "using `str::trim()` or alike before `str::split_whitespace`"
++}
++declare_lint_pass!(TrimSplitWhitespace => [TRIM_SPLIT_WHITESPACE]);
++
++impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace {
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
++        let tyckres = cx.typeck_results();
++        if_chain! {
++            if let ExprKind::MethodCall(path, [split_recv], split_ws_span) = expr.kind;
++            if path.ident.name == sym!(split_whitespace);
++            if let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id);
++            if cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id);
++            if let ExprKind::MethodCall(path, [_trim_recv], trim_span) = split_recv.kind;
++            if let trim_fn_name @ ("trim" | "trim_start" | "trim_end") = path.ident.name.as_str();
++            if let Some(trim_def_id) = tyckres.type_dependent_def_id(split_recv.hir_id);
++            if is_one_of_trim_diagnostic_items(cx, trim_def_id);
++            then {
++                span_lint_and_sugg(
++                    cx,
++                    TRIM_SPLIT_WHITESPACE,
++                    trim_span.with_hi(split_ws_span.lo()),
++                    &format!("found call to `str::{}` before `str::split_whitespace`", trim_fn_name),
++                    &format!("remove `{}()`", trim_fn_name),
++                    String::new(),
++                    Applicability::MachineApplicable,
++                );
++            }
++        }
++    }
++}
++
++fn is_one_of_trim_diagnostic_items(cx: &LateContext<'_>, trim_def_id: DefId) -> bool {
++    cx.tcx.is_diagnostic_item(sym::str_trim, trim_def_id)
++        || cx.tcx.is_diagnostic_item(sym::str_trim_start, trim_def_id)
++        || cx.tcx.is_diagnostic_item(sym::str_trim_end, trim_def_id)
++}
index b5dd27ff80de4052ea5960c26d63cb64e6f76610,0000000000000000000000000000000000000000..c4c1aa11004acbc523d2ffe22a25c4148e96f9ff
mode 100644,000000..100644
--- /dev/null
@@@ -1,693 -1,0 +1,693 @@@
-     // If it turns out that problematic cases are more prelavent than we assume,
 +use clippy_utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use core::ops::{Add, AddAssign};
 +use if_chain::if_chain;
 +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, StmtKind};
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Spanned;
 +use rustc_span::symbol::Ident;
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unlikely usages of binary operators that are almost
 +    /// certainly typos and/or copy/paste errors, given the other usages
 +    /// of binary operators nearby.
 +    ///
 +    /// ### Why is this bad?
 +    /// They are probably bugs and if they aren't then they look like bugs
 +    /// and you should add a comment explaining why you are doing such an
 +    /// odd set of operations.
 +    ///
 +    /// ### Known problems
 +    /// There may be some false positives if you are trying to do something
 +    /// unusual that happens to look like a typo.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Vec3 {
 +    ///     x: f64,
 +    ///     y: f64,
 +    ///     z: f64,
 +    /// }
 +    ///
 +    /// impl Eq for Vec3 {}
 +    ///
 +    /// impl PartialEq for Vec3 {
 +    ///     fn eq(&self, other: &Self) -> bool {
 +    ///         // This should trigger the lint because `self.x` is compared to `other.y`
 +    ///         self.x == other.y && self.y == other.y && self.z == other.z
 +    ///     }
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # struct Vec3 {
 +    /// #     x: f64,
 +    /// #     y: f64,
 +    /// #     z: f64,
 +    /// # }
 +    /// // same as above except:
 +    /// impl PartialEq for Vec3 {
 +    ///     fn eq(&self, other: &Self) -> bool {
 +    ///         // Note we now compare other.x to self.x
 +    ///         self.x == other.x && self.y == other.y && self.z == other.z
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.50.0"]
 +    pub SUSPICIOUS_OPERATION_GROUPINGS,
 +    nursery,
 +    "groupings of binary operations that look suspiciously like typos"
 +}
 +
 +declare_lint_pass!(SuspiciousOperationGroupings => [SUSPICIOUS_OPERATION_GROUPINGS]);
 +
 +impl EarlyLintPass for SuspiciousOperationGroupings {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let Some(binops) = extract_related_binops(&expr.kind) {
 +            check_binops(cx, &binops.iter().collect::<Vec<_>>());
 +
 +            let mut op_types = Vec::with_capacity(binops.len());
 +            // We could use a hashmap, etc. to avoid being O(n*m) here, but
 +            // we want the lints to be emitted in a consistent order. Besides,
 +            // m, (the number of distinct `BinOpKind`s in `binops`)
 +            // will often be small, and does have an upper limit.
 +            binops.iter().map(|b| b.op).for_each(|op| {
 +                if !op_types.contains(&op) {
 +                    op_types.push(op);
 +                }
 +            });
 +
 +            for op_type in op_types {
 +                let ops: Vec<_> = binops.iter().filter(|b| b.op == op_type).collect();
 +
 +                check_binops(cx, &ops);
 +            }
 +        }
 +    }
 +}
 +
 +fn check_binops(cx: &EarlyContext<'_>, binops: &[&BinaryOp<'_>]) {
 +    let binop_count = binops.len();
 +    if binop_count < 2 {
 +        // Single binary operation expressions would likely be false
 +        // positives.
 +        return;
 +    }
 +
 +    let mut one_ident_difference_count = 0;
 +    let mut no_difference_info = None;
 +    let mut double_difference_info = None;
 +    let mut expected_ident_loc = None;
 +
 +    let mut paired_identifiers = FxHashSet::default();
 +
 +    for (i, BinaryOp { left, right, op, .. }) in binops.iter().enumerate() {
 +        match ident_difference_expr(left, right) {
 +            IdentDifference::NoDifference => {
 +                if is_useless_with_eq_exprs(*op) {
 +                    // The `eq_op` lint should catch this in this case.
 +                    return;
 +                }
 +
 +                no_difference_info = Some(i);
 +            },
 +            IdentDifference::Single(ident_loc) => {
 +                one_ident_difference_count += 1;
 +                if let Some(previous_expected) = expected_ident_loc {
 +                    if previous_expected != ident_loc {
 +                        // This expression doesn't match the form we're
 +                        // looking for.
 +                        return;
 +                    }
 +                } else {
 +                    expected_ident_loc = Some(ident_loc);
 +                }
 +
 +                // If there was only a single difference, all other idents
 +                // must have been the same, and thus were paired.
 +                for id in skip_index(IdentIter::from(*left), ident_loc.index) {
 +                    paired_identifiers.insert(id);
 +                }
 +            },
 +            IdentDifference::Double(ident_loc1, ident_loc2) => {
 +                double_difference_info = Some((i, ident_loc1, ident_loc2));
 +            },
 +            IdentDifference::Multiple | IdentDifference::NonIdent => {
 +                // It's too hard to know whether this is a bug or not.
 +                return;
 +            },
 +        }
 +    }
 +
 +    let mut applicability = Applicability::MachineApplicable;
 +
 +    if let Some(expected_loc) = expected_ident_loc {
 +        match (no_difference_info, double_difference_info) {
 +            (Some(i), None) => attempt_to_emit_no_difference_lint(cx, binops, i, expected_loc),
 +            (None, Some((double_difference_index, ident_loc1, ident_loc2))) => {
 +                if_chain! {
 +                    if one_ident_difference_count == binop_count - 1;
 +                    if let Some(binop) = binops.get(double_difference_index);
 +                    then {
 +                        let changed_loc = if ident_loc1 == expected_loc {
 +                            ident_loc2
 +                        } else if ident_loc2 == expected_loc {
 +                            ident_loc1
 +                        } else {
 +                            // This expression doesn't match the form we're
 +                            // looking for.
 +                            return;
 +                        };
 +
 +                        if let Some(sugg) = ident_swap_sugg(
 +                            cx,
 +                            &paired_identifiers,
 +                            binop,
 +                            changed_loc,
 +                            &mut applicability,
 +                        ) {
 +                            emit_suggestion(
 +                                cx,
 +                                binop.span,
 +                                sugg,
 +                                applicability,
 +                            );
 +                        }
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +fn attempt_to_emit_no_difference_lint(
 +    cx: &EarlyContext<'_>,
 +    binops: &[&BinaryOp<'_>],
 +    i: usize,
 +    expected_loc: IdentLocation,
 +) {
 +    if let Some(binop) = binops.get(i).copied() {
 +        // We need to try and figure out which identifier we should
 +        // suggest using instead. Since there could be multiple
 +        // replacement candidates in a given expression, and we're
 +        // just taking the first one, we may get some bad lint
 +        // messages.
 +        let mut applicability = Applicability::MaybeIncorrect;
 +
 +        // We assume that the correct ident is one used elsewhere in
 +        // the other binops, in a place that there was a single
 +        // difference between idents before.
 +        let old_left_ident = get_ident(binop.left, expected_loc);
 +        let old_right_ident = get_ident(binop.right, expected_loc);
 +
 +        for b in skip_index(binops.iter(), i) {
 +            if_chain! {
 +                if let (Some(old_ident), Some(new_ident)) =
 +                (old_left_ident, get_ident(b.left, expected_loc));
 +                if old_ident != new_ident;
 +                if let Some(sugg) = suggestion_with_swapped_ident(
 +                    cx,
 +                    binop.left,
 +                    expected_loc,
 +                    new_ident,
 +                    &mut applicability,
 +                );
 +                then {
 +                    emit_suggestion(
 +                        cx,
 +                        binop.span,
 +                        replace_left_sugg(cx, binop, &sugg, &mut applicability),
 +                        applicability,
 +                    );
 +                    return;
 +                }
 +            }
 +
 +            if_chain! {
 +                if let (Some(old_ident), Some(new_ident)) =
 +                    (old_right_ident, get_ident(b.right, expected_loc));
 +                if old_ident != new_ident;
 +                if let Some(sugg) = suggestion_with_swapped_ident(
 +                    cx,
 +                    binop.right,
 +                    expected_loc,
 +                    new_ident,
 +                    &mut applicability,
 +                );
 +                then {
 +                    emit_suggestion(
 +                        cx,
 +                        binop.span,
 +                        replace_right_sugg(cx, binop, &sugg, &mut applicability),
 +                        applicability,
 +                    );
 +                    return;
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn emit_suggestion(cx: &EarlyContext<'_>, span: Span, sugg: String, applicability: Applicability) {
 +    span_lint_and_sugg(
 +        cx,
 +        SUSPICIOUS_OPERATION_GROUPINGS,
 +        span,
 +        "this sequence of operators looks suspiciously like a bug",
 +        "did you mean",
 +        sugg,
 +        applicability,
 +    );
 +}
 +
 +fn ident_swap_sugg(
 +    cx: &EarlyContext<'_>,
 +    paired_identifiers: &FxHashSet<Ident>,
 +    binop: &BinaryOp<'_>,
 +    location: IdentLocation,
 +    applicability: &mut Applicability,
 +) -> Option<String> {
 +    let left_ident = get_ident(binop.left, location)?;
 +    let right_ident = get_ident(binop.right, location)?;
 +
 +    let sugg = match (
 +        paired_identifiers.contains(&left_ident),
 +        paired_identifiers.contains(&right_ident),
 +    ) {
 +        (true, true) | (false, false) => {
 +            // We don't have a good guess of what ident should be
 +            // used instead, in these cases.
 +            *applicability = Applicability::MaybeIncorrect;
 +
 +            // We arbitrarily choose one side to suggest changing,
 +            // since we don't have a better guess. If the user
 +            // ends up duplicating a clause, the `logic_bug` lint
 +            // should catch it.
 +
 +            let right_suggestion = suggestion_with_swapped_ident(cx, binop.right, location, left_ident, applicability)?;
 +
 +            replace_right_sugg(cx, binop, &right_suggestion, applicability)
 +        },
 +        (false, true) => {
 +            // We haven't seen a pair involving the left one, so
 +            // it's probably what is wanted.
 +
 +            let right_suggestion = suggestion_with_swapped_ident(cx, binop.right, location, left_ident, applicability)?;
 +
 +            replace_right_sugg(cx, binop, &right_suggestion, applicability)
 +        },
 +        (true, false) => {
 +            // We haven't seen a pair involving the right one, so
 +            // it's probably what is wanted.
 +            let left_suggestion = suggestion_with_swapped_ident(cx, binop.left, location, right_ident, applicability)?;
 +
 +            replace_left_sugg(cx, binop, &left_suggestion, applicability)
 +        },
 +    };
 +
 +    Some(sugg)
 +}
 +
 +fn replace_left_sugg(
 +    cx: &EarlyContext<'_>,
 +    binop: &BinaryOp<'_>,
 +    left_suggestion: &str,
 +    applicability: &mut Applicability,
 +) -> String {
 +    format!(
 +        "{} {} {}",
 +        left_suggestion,
 +        binop.op.to_string(),
 +        snippet_with_applicability(cx, binop.right.span, "..", applicability),
 +    )
 +}
 +
 +fn replace_right_sugg(
 +    cx: &EarlyContext<'_>,
 +    binop: &BinaryOp<'_>,
 +    right_suggestion: &str,
 +    applicability: &mut Applicability,
 +) -> String {
 +    format!(
 +        "{} {} {}",
 +        snippet_with_applicability(cx, binop.left.span, "..", applicability),
 +        binop.op.to_string(),
 +        right_suggestion,
 +    )
 +}
 +
 +#[derive(Clone, Debug)]
 +struct BinaryOp<'exprs> {
 +    op: BinOpKind,
 +    span: Span,
 +    left: &'exprs Expr,
 +    right: &'exprs Expr,
 +}
 +
 +impl<'exprs> BinaryOp<'exprs> {
 +    fn new(op: BinOpKind, span: Span, (left, right): (&'exprs Expr, &'exprs Expr)) -> Self {
 +        Self { op, span, left, right }
 +    }
 +}
 +
 +fn strip_non_ident_wrappers(expr: &Expr) -> &Expr {
 +    let mut output = expr;
 +    loop {
 +        output = match &output.kind {
 +            ExprKind::Paren(ref inner) | ExprKind::Unary(_, ref inner) => inner,
 +            _ => {
 +                return output;
 +            },
 +        };
 +    }
 +}
 +
 +fn extract_related_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> {
 +    append_opt_vecs(chained_binops(kind), if_statement_binops(kind))
 +}
 +
 +fn if_statement_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> {
 +    match kind {
 +        ExprKind::If(ref condition, _, _) => chained_binops(&condition.kind),
 +        ExprKind::Paren(ref e) => if_statement_binops(&e.kind),
 +        ExprKind::Block(ref block, _) => {
 +            let mut output = None;
 +            for stmt in &block.stmts {
 +                match stmt.kind {
 +                    StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => {
 +                        output = append_opt_vecs(output, if_statement_binops(&e.kind));
 +                    },
 +                    _ => {},
 +                }
 +            }
 +            output
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn append_opt_vecs<A>(target_opt: Option<Vec<A>>, source_opt: Option<Vec<A>>) -> Option<Vec<A>> {
 +    match (target_opt, source_opt) {
 +        (Some(mut target), Some(source)) => {
 +            target.reserve(source.len());
 +            for op in source {
 +                target.push(op);
 +            }
 +            Some(target)
 +        },
 +        (Some(v), None) | (None, Some(v)) => Some(v),
 +        (None, None) => None,
 +    }
 +}
 +
 +fn chained_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> {
 +    match kind {
 +        ExprKind::Binary(_, left_outer, right_outer) => chained_binops_helper(left_outer, right_outer),
 +        ExprKind::Paren(ref e) | ExprKind::Unary(_, ref e) => chained_binops(&e.kind),
 +        _ => None,
 +    }
 +}
 +
 +fn chained_binops_helper<'expr>(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option<Vec<BinaryOp<'expr>>> {
 +    match (&left_outer.kind, &right_outer.kind) {
 +        (
 +            ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e),
 +            ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e),
 +        ) => chained_binops_helper(left_e, right_e),
 +        (ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), _) => chained_binops_helper(left_e, right_outer),
 +        (_, ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e)) => {
 +            chained_binops_helper(left_outer, right_e)
 +        },
 +        (
 +            ExprKind::Binary(Spanned { node: left_op, .. }, ref left_left, ref left_right),
 +            ExprKind::Binary(Spanned { node: right_op, .. }, ref right_left, ref right_right),
 +        ) => match (
 +            chained_binops_helper(left_left, left_right),
 +            chained_binops_helper(right_left, right_right),
 +        ) {
 +            (Some(mut left_ops), Some(right_ops)) => {
 +                left_ops.reserve(right_ops.len());
 +                for op in right_ops {
 +                    left_ops.push(op);
 +                }
 +                Some(left_ops)
 +            },
 +            (Some(mut left_ops), _) => {
 +                left_ops.push(BinaryOp::new(*right_op, right_outer.span, (right_left, right_right)));
 +                Some(left_ops)
 +            },
 +            (_, Some(mut right_ops)) => {
 +                right_ops.insert(0, BinaryOp::new(*left_op, left_outer.span, (left_left, left_right)));
 +                Some(right_ops)
 +            },
 +            (None, None) => Some(vec![
 +                BinaryOp::new(*left_op, left_outer.span, (left_left, left_right)),
 +                BinaryOp::new(*right_op, right_outer.span, (right_left, right_right)),
 +            ]),
 +        },
 +        _ => None,
 +    }
 +}
 +
 +#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
 +struct IdentLocation {
 +    index: usize,
 +}
 +
 +impl Add for IdentLocation {
 +    type Output = IdentLocation;
 +
 +    fn add(self, other: Self) -> Self::Output {
 +        Self {
 +            index: self.index + other.index,
 +        }
 +    }
 +}
 +
 +impl AddAssign for IdentLocation {
 +    fn add_assign(&mut self, other: Self) {
 +        *self = *self + other;
 +    }
 +}
 +
 +#[derive(Clone, Copy, Debug)]
 +enum IdentDifference {
 +    NoDifference,
 +    Single(IdentLocation),
 +    Double(IdentLocation, IdentLocation),
 +    Multiple,
 +    NonIdent,
 +}
 +
 +impl Add for IdentDifference {
 +    type Output = IdentDifference;
 +
 +    fn add(self, other: Self) -> Self::Output {
 +        match (self, other) {
 +            (Self::NoDifference, output) | (output, Self::NoDifference) => output,
 +            (Self::Multiple, _)
 +            | (_, Self::Multiple)
 +            | (Self::Double(_, _), Self::Single(_))
 +            | (Self::Single(_) | Self::Double(_, _), Self::Double(_, _)) => Self::Multiple,
 +            (Self::NonIdent, _) | (_, Self::NonIdent) => Self::NonIdent,
 +            (Self::Single(il1), Self::Single(il2)) => Self::Double(il1, il2),
 +        }
 +    }
 +}
 +
 +impl AddAssign for IdentDifference {
 +    fn add_assign(&mut self, other: Self) {
 +        *self = *self + other;
 +    }
 +}
 +
 +impl IdentDifference {
 +    /// Returns true if learning about more differences will not change the value
 +    /// of this `IdentDifference`, and false otherwise.
 +    fn is_complete(&self) -> bool {
 +        match self {
 +            Self::NoDifference | Self::Single(_) | Self::Double(_, _) => false,
 +            Self::Multiple | Self::NonIdent => true,
 +        }
 +    }
 +}
 +
 +fn ident_difference_expr(left: &Expr, right: &Expr) -> IdentDifference {
 +    ident_difference_expr_with_base_location(left, right, IdentLocation::default()).0
 +}
 +
 +fn ident_difference_expr_with_base_location(
 +    left: &Expr,
 +    right: &Expr,
 +    mut base: IdentLocation,
 +) -> (IdentDifference, IdentLocation) {
 +    // Ideally, this function should not use IdentIter because it should return
 +    // early if the expressions have any non-ident differences. We want that early
 +    // return because if without that restriction the lint would lead to false
 +    // positives.
 +    //
 +    // But, we cannot (easily?) use a `rustc_ast::visit::Visitor`, since we need
 +    // the two expressions to be walked in lockstep. And without a `Visitor`, we'd
 +    // have to do all the AST traversal ourselves, which is a lot of work, since to
 +    // do it properly we'd need to be able to handle more or less every possible
 +    // AST node since `Item`s can be written inside `Expr`s.
 +    //
 +    // In practice, it seems likely that expressions, above a certain size, that
 +    // happen to use the exact same idents in the exact same order, and which are
 +    // not structured the same, would be rare. Therefore it seems likely that if
 +    // we do only the first layer of matching ourselves and eventually fallback on
 +    // IdentIter, then the output of this function will be almost always be correct
 +    // in practice.
 +    //
++    // If it turns out that problematic cases are more prevalent than we assume,
 +    // then we should be able to change this function to do the correct traversal,
 +    // without needing to change the rest of the code.
 +
 +    #![allow(clippy::enum_glob_use)]
 +    use ExprKind::*;
 +
 +    match (
 +        &strip_non_ident_wrappers(left).kind,
 +        &strip_non_ident_wrappers(right).kind,
 +    ) {
 +        (Yield(_), Yield(_))
 +        | (Try(_), Try(_))
 +        | (Paren(_), Paren(_))
 +        | (Repeat(_, _), Repeat(_, _))
 +        | (Struct(_), Struct(_))
 +        | (MacCall(_), MacCall(_))
 +        | (InlineAsm(_), InlineAsm(_))
 +        | (Ret(_), Ret(_))
 +        | (Continue(_), Continue(_))
 +        | (Break(_, _), Break(_, _))
 +        | (AddrOf(_, _, _), AddrOf(_, _, _))
 +        | (Path(_, _), Path(_, _))
 +        | (Range(_, _, _), Range(_, _, _))
 +        | (Index(_, _), Index(_, _))
 +        | (Field(_, _), Field(_, _))
 +        | (AssignOp(_, _, _), AssignOp(_, _, _))
 +        | (Assign(_, _, _), Assign(_, _, _))
 +        | (TryBlock(_), TryBlock(_))
 +        | (Await(_), Await(_))
 +        | (Async(_, _, _), Async(_, _, _))
 +        | (Block(_, _), Block(_, _))
 +        | (Closure(_, _, _, _, _, _), Closure(_, _, _, _, _, _))
 +        | (Match(_, _), Match(_, _))
 +        | (Loop(_, _), Loop(_, _))
 +        | (ForLoop(_, _, _, _), ForLoop(_, _, _, _))
 +        | (While(_, _, _), While(_, _, _))
 +        | (If(_, _, _), If(_, _, _))
 +        | (Let(_, _, _), Let(_, _, _))
 +        | (Type(_, _), Type(_, _))
 +        | (Cast(_, _), Cast(_, _))
 +        | (Lit(_), Lit(_))
 +        | (Unary(_, _), Unary(_, _))
 +        | (Binary(_, _, _), Binary(_, _, _))
 +        | (Tup(_), Tup(_))
 +        | (MethodCall(_, _, _), MethodCall(_, _, _))
 +        | (Call(_, _), Call(_, _))
 +        | (ConstBlock(_), ConstBlock(_))
 +        | (Array(_), Array(_))
 +        | (Box(_), Box(_)) => {
 +            // keep going
 +        },
 +        _ => {
 +            return (IdentDifference::NonIdent, base);
 +        },
 +    }
 +
 +    let mut difference = IdentDifference::NoDifference;
 +
 +    for (left_attr, right_attr) in left.attrs.iter().zip(right.attrs.iter()) {
 +        let (new_difference, new_base) =
 +            ident_difference_via_ident_iter_with_base_location(left_attr, right_attr, base);
 +        base = new_base;
 +        difference += new_difference;
 +        if difference.is_complete() {
 +            return (difference, base);
 +        }
 +    }
 +
 +    let (new_difference, new_base) = ident_difference_via_ident_iter_with_base_location(left, right, base);
 +    base = new_base;
 +    difference += new_difference;
 +
 +    (difference, base)
 +}
 +
 +fn ident_difference_via_ident_iter_with_base_location<Iterable: Into<IdentIter>>(
 +    left: Iterable,
 +    right: Iterable,
 +    mut base: IdentLocation,
 +) -> (IdentDifference, IdentLocation) {
 +    // See the note in `ident_difference_expr_with_base_location` about `IdentIter`
 +    let mut difference = IdentDifference::NoDifference;
 +
 +    let mut left_iterator = left.into();
 +    let mut right_iterator = right.into();
 +
 +    loop {
 +        match (left_iterator.next(), right_iterator.next()) {
 +            (Some(left_ident), Some(right_ident)) => {
 +                if !eq_id(left_ident, right_ident) {
 +                    difference += IdentDifference::Single(base);
 +                    if difference.is_complete() {
 +                        return (difference, base);
 +                    }
 +                }
 +            },
 +            (Some(_), None) | (None, Some(_)) => {
 +                return (IdentDifference::NonIdent, base);
 +            },
 +            (None, None) => {
 +                return (difference, base);
 +            },
 +        }
 +        base += IdentLocation { index: 1 };
 +    }
 +}
 +
 +fn get_ident(expr: &Expr, location: IdentLocation) -> Option<Ident> {
 +    IdentIter::from(expr).nth(location.index)
 +}
 +
 +fn suggestion_with_swapped_ident(
 +    cx: &EarlyContext<'_>,
 +    expr: &Expr,
 +    location: IdentLocation,
 +    new_ident: Ident,
 +    applicability: &mut Applicability,
 +) -> Option<String> {
 +    get_ident(expr, location).and_then(|current_ident| {
 +        if eq_id(current_ident, new_ident) {
 +            // We never want to suggest a non-change
 +            return None;
 +        }
 +
 +        Some(format!(
 +            "{}{}{}",
 +            snippet_with_applicability(cx, expr.span.with_hi(current_ident.span.lo()), "..", applicability),
 +            new_ident,
 +            snippet_with_applicability(cx, expr.span.with_lo(current_ident.span.hi()), "..", applicability),
 +        ))
 +    })
 +}
 +
 +fn skip_index<A, Iter>(iter: Iter, index: usize) -> impl Iterator<Item = A>
 +where
 +    Iter: Iterator<Item = A>,
 +{
 +    iter.enumerate()
 +        .filter_map(move |(i, a)| if i == index { None } else { Some(a) })
 +}
index 3d1b2ee925bcebfd9d5511895d51e7ebf1f8c987,0000000000000000000000000000000000000000..78e388a49af1d470291e93a36958e66e6c2c6572
mode 100644,000000..100644
--- /dev/null
@@@ -1,258 -1,0 +1,259 @@@
-     pedantic,
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::source::{snippet, snippet_with_applicability};
 +use clippy_utils::{SpanlessEq, SpanlessHash};
 +use core::hash::{Hash, Hasher};
 +use if_chain::if_chain;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_data_structures::unhash::UnhashMap;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::Res;
 +use rustc_hir::{
 +    GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, QPath, TraitItem, Ty, TyKind, WherePredicate,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::Span;
++use std::fmt::Write as _;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns about unnecessary type repetitions in trait bounds
 +    ///
 +    /// ### Why is this bad?
 +    /// Repeating the type for every bound makes the code
 +    /// less readable than combining the bounds
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// pub fn foo<T>(t: T) where T: Copy, T: Clone {}
 +    /// ```
 +    ///
 +    /// Could be written as:
 +    ///
 +    /// ```rust
 +    /// pub fn foo<T>(t: T) where T: Copy + Clone {}
 +    /// ```
 +    #[clippy::version = "1.38.0"]
 +    pub TYPE_REPETITION_IN_BOUNDS,
-     pedantic,
++    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 {}
 +    /// ```
 +    ///
 +    /// Could be written as:
 +    ///
 +    /// ```rust
 +    /// fn func<T: Clone + Default>(arg: T) {}
 +    /// ```
 +    /// or
 +    ///
 +    /// ```rust
 +    /// fn func<T>(arg: T) where T: Clone + Default {}
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub TRAIT_DUPLICATION_IN_BOUNDS,
-                             hint_string.push_str(&format!(
++    nursery,
 +    "Check if the same trait bounds are specified twice during a function declaration"
 +}
 +
 +#[derive(Copy, Clone)]
 +pub struct TraitBounds {
 +    max_trait_bounds: u64,
 +}
 +
 +impl TraitBounds {
 +    #[must_use]
 +    pub fn new(max_trait_bounds: u64) -> Self {
 +        Self { max_trait_bounds }
 +    }
 +}
 +
 +impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for TraitBounds {
 +    fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
 +        self.check_type_repetition(cx, gen);
 +        check_trait_bound_duplication(cx, gen);
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) {
 +        let mut self_bounds_map = FxHashMap::default();
 +
 +        for predicate in item.generics.predicates {
 +            if_chain! {
 +                if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
 +                if !bound_predicate.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.bounds.len() as u64 <= self.max_trait_bounds;
 +                if !p.span.from_expansion();
 +                if let Some(ref v) = map.insert(
 +                    SpanlessTy { ty: p.bounded_ty, cx },
 +                    p.bounds.iter().collect::<Vec<_>>()
 +                );
 +
 +                then {
 +                    let mut hint_string = format!(
 +                        "consider combining the bounds: `{}:",
 +                        snippet(cx, p.bounded_ty.span, "_")
 +                    );
 +                    for b in v.iter() {
 +                        if let GenericBound::Trait(ref poly_trait_ref, _) = b {
 +                            let path = &poly_trait_ref.trait_ref.path;
-                             ));
++                            let _ = write!(hint_string,
 +                                " {} +",
 +                                snippet_with_applicability(cx, path.span, "..", &mut applicability)
-                             hint_string.push_str(&format!(
++                            );
 +                        }
 +                    }
 +                    for b in p.bounds.iter() {
 +                        if let GenericBound::Trait(ref poly_trait_ref, _) = b {
 +                            let path = &poly_trait_ref.trait_ref.path;
-                             ));
++                            let _ = write!(hint_string,
 +                                " {} +",
 +                                snippet_with_applicability(cx, path.span, "..", &mut applicability)
-                         trait_resolutions_direct.push((res_where, span_where))
++                            );
 +                        }
 +                    }
 +                    hint_string.truncate(hint_string.len() - 2);
 +                    hint_string.push('`');
 +                    span_lint_and_help(
 +                        cx,
 +                        TYPE_REPETITION_IN_BOUNDS,
 +                        p.span,
 +                        "this type has already been used as a bound predicate",
 +                        None,
 +                        &hint_string,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
 +    if gen.span.from_expansion() || gen.params.is_empty() || gen.predicates.is_empty() {
 +        return;
 +    }
 +
 +    let mut map = FxHashMap::<_, Vec<_>>::default();
 +    for predicate in gen.predicates {
 +        if_chain! {
 +            if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
 +            if !bound_predicate.span.from_expansion();
 +            if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
 +            if let Some(segment) = segments.first();
 +            then {
 +                for (res_where, _, span_where) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
 +                    let trait_resolutions_direct = map.entry(segment.ident).or_default();
 +                    if let Some((_, span_direct)) = trait_resolutions_direct
 +                                                .iter()
 +                                                .find(|(res_direct, _)| *res_direct == res_where) {
 +                        span_lint_and_help(
 +                            cx,
 +                            TRAIT_DUPLICATION_IN_BOUNDS,
 +                            *span_direct,
 +                            "this trait bound is already specified in the where clause",
 +                            None,
 +                            "consider removing this trait bound",
 +                        );
 +                    }
 +                    else {
++                        trait_resolutions_direct.push((res_where, span_where));
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'a [PathSegment<'a>], Span)> {
 +    if let GenericBound::Trait(t, _) = bound {
 +        Some((t.trait_ref.path.res, t.trait_ref.path.segments, t.span))
 +    } else {
 +        None
 +    }
 +}
index f359b606e4548b1736876e4fc3074ed6ace86ff5,0000000000000000000000000000000000000000..0cbf5ccefa6d89ea6435cfc3a7647c81fee84e9e
mode 100644,000000..100644
--- /dev/null
@@@ -1,106 -1,0 +1,102 @@@
- use clippy_utils::ty::is_normalizable;
 +use clippy_utils::last_path_segment;
 +use clippy_utils::source::snippet;
- use rustc_middle::ty::{self, cast::CastKind, Ty};
 +use if_chain::if_chain;
 +use rustc_hir::{Expr, GenericArg, QPath, TyKind};
 +use rustc_lint::LateContext;
-     let empty_param_env = ty::ParamEnv::empty();
-     // check if `from` and `to` are normalizable to avoid ICE (#4968)
-     if !(is_normalizable(cx, empty_param_env, from) && is_normalizable(cx, empty_param_env, to)) {
-         return false;
-     }
-     let from_ty_layout = cx.tcx.layout_of(empty_param_env.and(from));
-     let to_ty_layout = cx.tcx.layout_of(empty_param_env.and(to));
-     if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) {
-         from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi
++use rustc_middle::ty::{cast::CastKind, Ty};
 +use rustc_span::DUMMY_SP;
 +use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited};
 +
 +/// Gets the snippet of `Bar` in `…::transmute<Foo, &Bar>`. If that snippet is
 +/// not available , use
 +/// the type's `ToString` implementation. In weird cases it could lead to types
 +/// with invalid `'_`
 +/// lifetime, but it should be rare.
 +pub(super) fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty: Ty<'_>) -> String {
 +    let seg = last_path_segment(path);
 +    if_chain! {
 +        if let Some(params) = seg.args;
 +        if !params.parenthesized;
 +        if let Some(to_ty) = params.args.iter().filter_map(|arg| match arg {
 +            GenericArg::Type(ty) => Some(ty),
 +            _ => None,
 +        }).nth(1);
 +        if let TyKind::Rptr(_, ref to_ty) = to_ty.kind;
 +        then {
 +            return snippet(cx, to_ty.ty.span, &to_ref_ty.to_string()).to_string();
 +        }
 +    }
 +
 +    to_ref_ty.to_string()
 +}
 +
 +// check if the component types of the transmuted collection and the result have different ABI,
 +// size or alignment
 +pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
-             // errors in the fcx instead of returing Err in some cases. Those cases
++    if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from)
++        && let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to)
++        && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from))
++        && let Ok(to_layout) = cx.tcx.layout_of(cx.param_env.and(to))
++    {
++        from_layout.size != to_layout.size || from_layout.align.abi != to_layout.align.abi
 +    } else {
 +        // no idea about layout, so don't lint
 +        false
 +    }
 +}
 +
 +/// Check if the type conversion can be expressed as a pointer cast, instead of
 +/// a transmute. In certain cases, including some invalid casts from array
 +/// references to pointers, this may cause additional errors to be emitted and/or
 +/// ICE error messages. This function will panic if that occurs.
 +pub(super) fn can_be_expressed_as_pointer_cast<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'_>,
 +    from_ty: Ty<'tcx>,
 +    to_ty: Ty<'tcx>,
 +) -> bool {
 +    use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast};
 +    matches!(
 +        check_cast(cx, e, from_ty, to_ty),
 +        Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast)
 +    )
 +}
 +
 +/// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of
 +/// the cast. In certain cases, including some invalid casts from array references
 +/// to pointers, this may cause additional errors to be emitted and/or ICE error
 +/// messages. This function will panic if that occurs.
 +fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option<CastKind> {
 +    let hir_id = e.hir_id;
 +    let local_def_id = hir_id.owner;
 +
 +    Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
 +        let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, hir_id);
 +
 +        // If we already have errors, we can't be sure we can pointer cast.
 +        assert!(
 +            !fn_ctxt.errors_reported_since_creation(),
 +            "Newly created FnCtxt contained errors"
 +        );
 +
 +        if let Ok(check) = CastCheck::new(
 +            &fn_ctxt, e, from_ty, to_ty,
 +            // We won't show any error to the user, so we don't care what the span is here.
 +            DUMMY_SP, DUMMY_SP,
 +        ) {
 +            let res = check.do_check(&fn_ctxt);
 +
 +            // do_check's documentation says that it might return Ok and create
++            // errors in the fcx instead of returning Err in some cases. Those cases
 +            // should be filtered out before getting here.
 +            assert!(
 +                !fn_ctxt.errors_reported_since_creation(),
 +                "`fn_ctxt` contained errors after cast check!"
 +            );
 +
 +            res.ok()
 +        } else {
 +            None
 +        }
 +    })
 +}
index 67cc8913318962f2f849ab1441a166dd7b9d9eaa,0000000000000000000000000000000000000000..353a6f6b899ea3e743ed57982b8acd130d185e14
mode 100644,000000..100644
--- /dev/null
@@@ -1,574 -1,0 +1,574 @@@
-         // FIXME: idially we would like to warn *if the compicated type can be simplified*, but it's hard to
-         // check.
 +mod borrowed_box;
 +mod box_collection;
 +mod linked_list;
 +mod option_option;
 +mod rc_buffer;
 +mod rc_mutex;
 +mod redundant_allocation;
 +mod type_complexity;
 +mod utils;
 +mod vec_box;
 +
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::FnKind;
 +use rustc_hir::{
 +    Body, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitItem,
 +    TraitItemKind, TyKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `Box<T>` where T is a collection such as Vec anywhere in the code.
 +    /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
 +    ///
 +    /// ### Why is this bad?
 +    /// Collections already keeps their contents in a separate area on
 +    /// the heap. So if you `Box` them, you just add another level of indirection
 +    /// without any benefit whatsoever.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// struct X {
 +    ///     values: Box<Vec<Foo>>,
 +    /// }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust,ignore
 +    /// struct X {
 +    ///     values: Vec<Foo>,
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.57.0"]
 +    pub BOX_COLLECTION,
 +    perf,
 +    "usage of `Box<Vec<T>>`, vector elements are already on the heap"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code.
 +    /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
 +    ///
 +    /// ### Why is this bad?
 +    /// `Vec` already keeps its contents in a separate area on
 +    /// the heap. So if you `Box` its contents, you just add another level of indirection.
 +    ///
 +    /// ### Known problems
 +    /// Vec<Box<T: Sized>> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530),
 +    /// 1st comment).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct X {
 +    ///     values: Vec<Box<i32>>,
 +    /// }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust
 +    /// struct X {
 +    ///     values: Vec<i32>,
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.33.0"]
 +    pub VEC_BOX,
 +    complexity,
 +    "usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `Option<Option<_>>` in function signatures and type
 +    /// definitions
 +    ///
 +    /// ### Why is this bad?
 +    /// `Option<_>` represents an optional value. `Option<Option<_>>`
 +    /// represents an optional optional value which is logically the same thing as an optional
 +    /// value but has an unneeded extra level of wrapping.
 +    ///
 +    /// If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases,
 +    /// consider a custom `enum` instead, with clear names for each case.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn get_data() -> Option<Option<u32>> {
 +    ///     None
 +    /// }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust
 +    /// pub enum Contents {
 +    ///     Data(Vec<u8>), // Was Some(Some(Vec<u8>))
 +    ///     NotYetFetched, // Was Some(None)
 +    ///     None,          // Was None
 +    /// }
 +    ///
 +    /// fn get_data() -> Contents {
 +    ///     Contents::None
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OPTION_OPTION,
 +    pedantic,
 +    "usage of `Option<Option<T>>`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of any `LinkedList`, suggesting to use a
 +    /// `Vec` or a `VecDeque` (formerly called `RingBuf`).
 +    ///
 +    /// ### Why is this bad?
 +    /// Gankro says:
 +    ///
 +    /// > The TL;DR of `LinkedList` is that it's built on a massive amount of
 +    /// pointers and indirection.
 +    /// > It wastes memory, it has terrible cache locality, and is all-around slow.
 +    /// `RingBuf`, while
 +    /// > "only" amortized for push/pop, should be faster in the general case for
 +    /// almost every possible
 +    /// > workload, and isn't even amortized at all if you can predict the capacity
 +    /// you need.
 +    /// >
 +    /// > `LinkedList`s are only really good if you're doing a lot of merging or
 +    /// splitting of lists.
 +    /// > This is because they can just mangle some pointers instead of actually
 +    /// copying the data. Even
 +    /// > if you're doing a lot of insertion in the middle of the list, `RingBuf`
 +    /// can still be better
 +    /// > because of how expensive it is to seek to the middle of a `LinkedList`.
 +    ///
 +    /// ### Known problems
 +    /// False positives – the instances where using a
 +    /// `LinkedList` makes sense are few and far between, but they can still happen.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::LinkedList;
 +    /// let x: LinkedList<usize> = LinkedList::new();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub LINKEDLIST,
 +    pedantic,
 +    "usage of LinkedList, usually a vector is faster, or a more specialized data structure like a `VecDeque`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `&Box<T>` anywhere in the code.
 +    /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
 +    ///
 +    /// ### Why is this bad?
 +    /// A `&Box<T>` parameter requires the function caller to box `T` first before passing it to a function.
 +    /// Using `&T` defines a concrete type for the parameter and generalizes the function, this would also
 +    /// auto-deref to `&T` at the function call site if passed a `&Box<T>`.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// fn foo(bar: &Box<T>) { ... }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust,ignore
 +    /// fn foo(bar: &T) { ... }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub BORROWED_BOX,
 +    complexity,
 +    "a borrow of a boxed type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of redundant allocations anywhere in the code.
 +    ///
 +    /// ### Why is this bad?
 +    /// Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Arc<T>>`, `Rc<Box<T>>`, `Arc<&T>`, `Arc<Rc<T>>`,
 +    /// `Arc<Arc<T>>`, `Arc<Box<T>>`, `Box<&T>`, `Box<Rc<T>>`, `Box<Arc<T>>`, `Box<Box<T>>`, add an unnecessary level of indirection.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// fn foo(bar: Rc<&usize>) {}
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust
 +    /// fn foo(bar: &usize) {}
 +    /// ```
 +    #[clippy::version = "1.44.0"]
 +    pub REDUNDANT_ALLOCATION,
 +    perf,
 +    "redundant allocation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `Rc<T>` and `Arc<T>` when `T` is a mutable buffer type such as `String` or `Vec`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Expressions such as `Rc<String>` usually have no advantage over `Rc<str>`, since
 +    /// it is larger and involves an extra level of indirection, and doesn't implement `Borrow<str>`.
 +    ///
 +    /// While mutating a buffer type would still be possible with `Rc::get_mut()`, it only
 +    /// works if there are no additional references yet, which usually defeats the purpose of
 +    /// enclosing it in a shared ownership type. Instead, additionally wrapping the inner
 +    /// type with an interior mutable container (such as `RefCell` or `Mutex`) would normally
 +    /// be used.
 +    ///
 +    /// ### Known problems
 +    /// This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for
 +    /// cases where mutation only happens before there are any additional references.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// # use std::rc::Rc;
 +    /// fn foo(interned: Rc<String>) { ... }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust,ignore
 +    /// fn foo(interned: Rc<str>) { ... }
 +    /// ```
 +    #[clippy::version = "1.48.0"]
 +    pub RC_BUFFER,
 +    restriction,
 +    "shared ownership of a buffer type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for types used in structs, parameters and `let`
 +    /// declarations above a certain complexity threshold.
 +    ///
 +    /// ### Why is this bad?
 +    /// Too complex types make the code less readable. Consider
 +    /// using a `type` definition to simplify them.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// struct Foo {
 +    ///     inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>,
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TYPE_COMPLEXITY,
 +    complexity,
 +    "usage of very complex types that might be better factored into `type` definitions"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `Rc<Mutex<T>>`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `Rc` is used in single thread and `Mutex` is used in multi thread.
 +    /// Consider using `Rc<RefCell<T>>` in single thread or `Arc<Mutex<T>>` in multi thread.
 +    ///
 +    /// ### Known problems
 +    /// Sometimes combining generic types can lead to the requirement that a
 +    /// type use Rc in conjunction with Mutex. We must consider those cases false positives, but
 +    /// alas they are quite hard to rule out. Luckily they are also rare.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// use std::rc::Rc;
 +    /// use std::sync::Mutex;
 +    /// fn foo(interned: Rc<Mutex<i32>>) { ... }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust,ignore
 +    /// use std::rc::Rc;
 +    /// use std::cell::RefCell
 +    /// fn foo(interned: Rc<RefCell<i32>>) { ... }
 +    /// ```
 +    #[clippy::version = "1.55.0"]
 +    pub RC_MUTEX,
 +    restriction,
 +    "usage of `Rc<Mutex<T>>`"
 +}
 +
 +pub struct Types {
 +    vec_box_size_threshold: u64,
 +    type_complexity_threshold: u64,
 +    avoid_breaking_exported_api: bool,
 +}
 +
 +impl_lint_pass!(Types => [BOX_COLLECTION, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Types {
 +    fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) {
 +        let is_in_trait_impl =
 +            if let Some(hir::Node::Item(item)) = cx.tcx.hir().find_by_def_id(cx.tcx.hir().get_parent_item(id)) {
 +                matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
 +            } else {
 +                false
 +            };
 +
 +        let is_exported = cx.access_levels.is_exported(cx.tcx.hir().local_def_id(id));
 +
 +        self.check_fn_decl(
 +            cx,
 +            decl,
 +            CheckTyContext {
 +                is_in_trait_impl,
 +                is_exported,
 +                ..CheckTyContext::default()
 +            },
 +        );
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        let is_exported = cx.access_levels.is_exported(item.def_id);
 +
 +        match item.kind {
 +            ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty(
 +                cx,
 +                ty,
 +                CheckTyContext {
 +                    is_exported,
 +                    ..CheckTyContext::default()
 +                },
 +            ),
 +            // functions, enums, structs, impls and traits are covered
 +            _ => (),
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
 +        match item.kind {
 +            ImplItemKind::Const(ty, _) => {
 +                let is_in_trait_impl = if let Some(hir::Node::Item(item)) =
 +                    cx.tcx.hir().find_by_def_id(cx.tcx.hir().get_parent_item(item.hir_id()))
 +                {
 +                    matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
 +                } else {
 +                    false
 +                };
 +
 +                self.check_ty(
 +                    cx,
 +                    ty,
 +                    CheckTyContext {
 +                        is_in_trait_impl,
 +                        ..CheckTyContext::default()
 +                    },
 +                );
 +            },
 +            // Methods are covered by check_fn.
 +            // Type aliases are ignored because oftentimes it's impossible to
 +            // make type alias declaration in trait simpler, see #1013
 +            ImplItemKind::Fn(..) | ImplItemKind::TyAlias(..) => (),
 +        }
 +    }
 +
 +    fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) {
 +        let is_exported = cx.access_levels.is_exported(cx.tcx.hir().local_def_id(field.hir_id));
 +
 +        self.check_ty(
 +            cx,
 +            field.ty,
 +            CheckTyContext {
 +                is_exported,
 +                ..CheckTyContext::default()
 +            },
 +        );
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &TraitItem<'_>) {
 +        let is_exported = cx.access_levels.is_exported(item.def_id);
 +
 +        let context = CheckTyContext {
 +            is_exported,
 +            ..CheckTyContext::default()
 +        };
 +
 +        match item.kind {
 +            TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => {
 +                self.check_ty(cx, ty, context);
 +            },
 +            TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, context),
 +            TraitItemKind::Type(..) => (),
 +        }
 +    }
 +
 +    fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
 +        if let Some(ty) = local.ty {
 +            self.check_ty(
 +                cx,
 +                ty,
 +                CheckTyContext {
 +                    is_local: true,
 +                    ..CheckTyContext::default()
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +impl Types {
 +    pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64, avoid_breaking_exported_api: bool) -> Self {
 +        Self {
 +            vec_box_size_threshold,
 +            type_complexity_threshold,
 +            avoid_breaking_exported_api,
 +        }
 +    }
 +
 +    fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, context: CheckTyContext) {
 +        // Ignore functions in trait implementations as they are usually forced by the trait definition.
 +        //
++        // FIXME: ideally we would like to warn *if the complicated type can be simplified*, but it's hard
++        // to check.
 +        if context.is_in_trait_impl {
 +            return;
 +        }
 +
 +        for input in decl.inputs {
 +            self.check_ty(cx, input, context);
 +        }
 +
 +        if let FnRetTy::Return(ty) = decl.output {
 +            self.check_ty(cx, ty, context);
 +        }
 +    }
 +
 +    /// Recursively check for `TypePass` lints in the given type. Stop at the first
 +    /// lint found.
 +    ///
 +    /// The parameter `is_local` distinguishes the context of the type.
 +    fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, mut context: CheckTyContext) {
 +        if hir_ty.span.from_expansion() {
 +            return;
 +        }
 +
 +        // Skip trait implementations; see issue #605.
 +        if context.is_in_trait_impl {
 +            return;
 +        }
 +
 +        if !context.is_nested_call && type_complexity::check(cx, hir_ty, self.type_complexity_threshold) {
 +            return;
 +        }
 +
 +        match hir_ty.kind {
 +            TyKind::Path(ref qpath) if !context.is_local => {
 +                let hir_id = hir_ty.hir_id;
 +                let res = cx.qpath_res(qpath, hir_id);
 +                if let Some(def_id) = res.opt_def_id() {
 +                    if self.is_type_change_allowed(context) {
 +                        // All lints that are being checked in this block are guarded by
 +                        // the `avoid_breaking_exported_api` configuration. When adding a
 +                        // new lint, please also add the name to the configuration documentation
 +                        // in `clippy_lints::utils::conf.rs`
 +
 +                        let mut triggered = false;
 +                        triggered |= box_collection::check(cx, hir_ty, qpath, def_id);
 +                        triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id);
 +                        triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id);
 +                        triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold);
 +                        triggered |= option_option::check(cx, hir_ty, qpath, def_id);
 +                        triggered |= linked_list::check(cx, hir_ty, def_id);
 +                        triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id);
 +
 +                        if triggered {
 +                            return;
 +                        }
 +                    }
 +                }
 +                match *qpath {
 +                    QPath::Resolved(Some(ty), p) => {
 +                        context.is_nested_call = true;
 +                        self.check_ty(cx, ty, context);
 +                        for ty in p.segments.iter().flat_map(|seg| {
 +                            seg.args
 +                                .as_ref()
 +                                .map_or_else(|| [].iter(), |params| params.args.iter())
 +                                .filter_map(|arg| match arg {
 +                                    GenericArg::Type(ty) => Some(ty),
 +                                    _ => None,
 +                                })
 +                        }) {
 +                            self.check_ty(cx, ty, context);
 +                        }
 +                    },
 +                    QPath::Resolved(None, p) => {
 +                        context.is_nested_call = true;
 +                        for ty in p.segments.iter().flat_map(|seg| {
 +                            seg.args
 +                                .as_ref()
 +                                .map_or_else(|| [].iter(), |params| params.args.iter())
 +                                .filter_map(|arg| match arg {
 +                                    GenericArg::Type(ty) => Some(ty),
 +                                    _ => None,
 +                                })
 +                        }) {
 +                            self.check_ty(cx, ty, context);
 +                        }
 +                    },
 +                    QPath::TypeRelative(ty, seg) => {
 +                        context.is_nested_call = true;
 +                        self.check_ty(cx, ty, context);
 +                        if let Some(params) = seg.args {
 +                            for ty in params.args.iter().filter_map(|arg| match arg {
 +                                GenericArg::Type(ty) => Some(ty),
 +                                _ => None,
 +                            }) {
 +                                self.check_ty(cx, ty, context);
 +                            }
 +                        }
 +                    },
 +                    QPath::LangItem(..) => {},
 +                }
 +            },
 +            TyKind::Rptr(ref lt, ref mut_ty) => {
 +                context.is_nested_call = true;
 +                if !borrowed_box::check(cx, hir_ty, lt, mut_ty) {
 +                    self.check_ty(cx, mut_ty.ty, context);
 +                }
 +            },
 +            TyKind::Slice(ty) | TyKind::Array(ty, _) | TyKind::Ptr(MutTy { ty, .. }) => {
 +                context.is_nested_call = true;
 +                self.check_ty(cx, ty, context);
 +            },
 +            TyKind::Tup(tys) => {
 +                context.is_nested_call = true;
 +                for ty in tys {
 +                    self.check_ty(cx, ty, context);
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +
 +    /// This function checks if the type is allowed to change in the current context
 +    /// based on the `avoid_breaking_exported_api` configuration
 +    fn is_type_change_allowed(&self, context: CheckTyContext) -> bool {
 +        !(context.is_exported && self.avoid_breaking_exported_api)
 +    }
 +}
 +
 +#[allow(clippy::struct_excessive_bools)]
 +#[derive(Clone, Copy, Default)]
 +struct CheckTyContext {
 +    is_in_trait_impl: bool,
 +    /// `true` for types on local variables.
 +    is_local: bool,
 +    /// `true` for types that are part of the public API.
 +    is_exported: bool,
 +    is_nested_call: bool,
 +}
index c8912a18f1854e3dcf5ee2252b1dcb52ced5d754,0000000000000000000000000000000000000000..465d8a914fb290b2a4e466750f923bc6be9cc4da
mode 100644,000000..100644
--- /dev/null
@@@ -1,197 -1,0 +1,198 @@@
-             src.get(start.to_usize() - offset..end.to_usize() - offset)
-                 .map(|text| (start.to_usize(), text.trim_start()))
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::is_lint_allowed;
 +use clippy_utils::source::walk_span_to_context;
 +use rustc_data_structures::sync::Lrc;
 +use rustc_hir::{Block, BlockCheckMode, UnsafeSource};
 +use rustc_lexer::{tokenize, TokenKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{BytePos, Pos, SyntaxContext};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `unsafe` blocks without a `// SAFETY: ` comment
 +    /// explaining why the unsafe operations performed inside
 +    /// the block are safe.
 +    ///
 +    /// Note the comment must appear on the line(s) preceding the unsafe block
 +    /// with nothing appearing in between. The following is ok:
 +    /// ```ignore
 +    /// foo(
 +    ///     // SAFETY:
 +    ///     // This is a valid safety comment
 +    ///     unsafe { *x }
 +    /// )
 +    /// ```
 +    /// But neither of these are:
 +    /// ```ignore
 +    /// // SAFETY:
 +    /// // This is not a valid safety comment
 +    /// foo(
 +    ///     /* SAFETY: Neither is this */ unsafe { *x },
 +    /// );
 +    /// ```
 +    ///
 +    /// ### Why is this bad?
 +    /// Undocumented unsafe blocks can make it difficult to
 +    /// read and maintain code, as well as uncover unsoundness
 +    /// and bugs.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::ptr::NonNull;
 +    /// let a = &mut 42;
 +    ///
 +    /// let ptr = unsafe { NonNull::new_unchecked(a) };
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::ptr::NonNull;
 +    /// let a = &mut 42;
 +    ///
 +    /// // SAFETY: references are guaranteed to be non-null.
 +    /// let ptr = unsafe { NonNull::new_unchecked(a) };
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub UNDOCUMENTED_UNSAFE_BLOCKS,
 +    restriction,
 +    "creating an unsafe block without explaining why it is safe"
 +}
 +
 +declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]);
 +
 +impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
 +    fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) {
 +        if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
 +            && !in_external_macro(cx.tcx.sess, block.span)
 +            && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id)
 +            && !is_unsafe_from_proc_macro(cx, block)
 +            && !block_has_safety_comment(cx, block)
 +        {
 +            let source_map = cx.tcx.sess.source_map();
 +            let span = if source_map.is_multiline(block.span) {
 +                source_map.span_until_char(block.span, '\n')
 +            } else {
 +                block.span
 +            };
 +
 +            span_lint_and_help(
 +                cx,
 +                UNDOCUMENTED_UNSAFE_BLOCKS,
 +                span,
 +                "unsafe block missing a safety comment",
 +                None,
 +                "consider adding a safety comment on the preceding line",
 +            );
 +        }
 +    }
 +}
 +
 +fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, block: &Block<'_>) -> bool {
 +    let source_map = cx.sess().source_map();
 +    let file_pos = source_map.lookup_byte_offset(block.span.lo());
 +    file_pos
 +        .sf
 +        .src
 +        .as_deref()
 +        .and_then(|src| src.get(file_pos.pos.to_usize()..))
 +        .map_or(true, |src| !src.starts_with("unsafe"))
 +}
 +
 +/// Checks if the lines immediately preceding the block contain a safety comment.
 +fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool {
 +    // This intentionally ignores text before the start of a function so something like:
 +    // ```
 +    //     // SAFETY: reason
 +    //     fn foo() { unsafe { .. } }
 +    // ```
 +    // won't work. This is to avoid dealing with where such a comment should be place relative to
 +    // attributes and doc comments.
 +
 +    let source_map = cx.sess().source_map();
 +    let ctxt = block.span.ctxt();
 +    if ctxt != SyntaxContext::root() {
 +        // From a macro expansion. Get the text from the start of the macro declaration to start of the unsafe block.
 +        //     macro_rules! foo { () => { stuff }; (x) => { unsafe { stuff } }; }
 +        //     ^--------------------------------------------^
 +        if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo())
 +            && let Ok(macro_line) = source_map.lookup_line(ctxt.outer_expn_data().def_site.lo())
 +            && Lrc::ptr_eq(&unsafe_line.sf, &macro_line.sf)
 +            && let Some(src) = unsafe_line.sf.src.as_deref()
 +        {
 +            macro_line.line < unsafe_line.line && text_has_safety_comment(
 +                src,
 +                &unsafe_line.sf.lines[macro_line.line + 1..=unsafe_line.line],
 +                unsafe_line.sf.start_pos.to_usize(),
 +            )
 +        } else {
 +            // Problem getting source text. Pretend a comment was found.
 +            true
 +        }
 +    } else if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo())
 +        && let Some(body) = cx.enclosing_body
 +        && let Some(body_span) = walk_span_to_context(cx.tcx.hir().body(body).value.span, SyntaxContext::root())
 +        && let Ok(body_line) = source_map.lookup_line(body_span.lo())
 +        && Lrc::ptr_eq(&unsafe_line.sf, &body_line.sf)
 +        && let Some(src) = unsafe_line.sf.src.as_deref()
 +    {
 +        // Get the text from the start of function body to the unsafe block.
 +        //     fn foo() { some_stuff; unsafe { stuff }; other_stuff; }
 +        //              ^-------------^
 +        body_line.line < unsafe_line.line && text_has_safety_comment(
 +            src,
 +            &unsafe_line.sf.lines[body_line.line + 1..=unsafe_line.line],
 +            unsafe_line.sf.start_pos.to_usize(),
 +        )
 +    } else {
 +        // Problem getting source text. Pretend a comment was found.
 +        true
 +    }
 +}
 +
 +/// Checks if the given text has a safety comment for the immediately proceeding line.
 +fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> bool {
 +    let mut lines = line_starts
 +        .array_windows::<2>()
 +        .rev()
 +        .map_while(|[start, end]| {
-             let src = src[line_start..line_starts.last().unwrap().to_usize()].trim_start();
++            let start = start.to_usize() - offset;
++            let end = end.to_usize() - offset;
++            src.get(start..end).map(|text| (start, text.trim_start()))
 +        })
 +        .filter(|(_, text)| !text.is_empty());
 +
 +    let Some((line_start, line)) = lines.next() else {
 +        return false;
 +    };
 +    // Check for a sequence of line comments.
 +    if line.starts_with("//") {
 +        let mut line = line;
 +        loop {
 +            if line.to_ascii_uppercase().contains("SAFETY:") {
 +                return true;
 +            }
 +            match lines.next() {
 +                Some((_, x)) if x.starts_with("//") => line = x,
 +                _ => return false,
 +            }
 +        }
 +    }
 +    // No line comments; look for the start of a block comment.
 +    // This will only find them if they are at the start of a line.
 +    let (mut line_start, mut line) = (line_start, line);
 +    loop {
 +        if line.starts_with("/*") {
++            let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start();
 +            let mut tokens = tokenize(src);
 +            return src[..tokens.next().unwrap().len]
 +                .to_ascii_uppercase()
 +                .contains("SAFETY:")
 +                && tokens.all(|t| t.kind == TokenKind::Whitespace);
 +        }
 +        match lines.next() {
 +            Some(x) => (line_start, line) = x,
 +            None => return false,
 +        }
 +    }
 +}
index b25a6e3375bb4176b3d45f610e633a8f9ce1086b,0000000000000000000000000000000000000000..f3f1f53aac5652f6c07a0e79daa9bf85a7d21ff9
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,105 @@@
- use rustc_hir::{Stmt, StmtKind};
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet_with_macro_callsite;
++use clippy_utils::visitors::for_each_value_source;
++use core::ops::ControlFlow;
 +use rustc_errors::Applicability;
-     if let StmtKind::Local(local) = stmt.kind {
-         if cx.typeck_results().pat_ty(local.pat).is_unit() {
-             if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() {
-                 return;
++use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty::{self, Ty, TypeFoldable, TypeVisitor};
 +
 +use super::LET_UNIT_VALUE;
 +
 +pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
++    if let StmtKind::Local(local) = stmt.kind
++        && let Some(init) = local.init
++        && !local.pat.span.from_expansion()
++        && !in_external_macro(cx.sess(), stmt.span)
++        && cx.typeck_results().pat_ty(local.pat).is_unit()
++    {
++        let needs_inferred = for_each_value_source(init, &mut |e| if needs_inferred_result_ty(cx, e) {
++            ControlFlow::Continue(())
++        } else {
++            ControlFlow::Break(())
++        }).is_continue();
++
++        if needs_inferred {
++            if !matches!(local.pat.kind, PatKind::Wild) {
++                span_lint_and_then(
++                    cx,
++                    LET_UNIT_VALUE,
++                    stmt.span,
++                    "this let-binding has unit value",
++                    |diag| {
++                            diag.span_suggestion(
++                                local.pat.span,
++                                "use a wild (`_`) binding",
++                                "_",
++                                Applicability::MaybeIncorrect, // snippet
++                            );
++                    },
++                );
 +            }
++        } else {
 +            span_lint_and_then(
 +                cx,
 +                LET_UNIT_VALUE,
 +                stmt.span,
 +                "this let-binding has unit value",
 +                |diag| {
 +                    if let Some(expr) = &local.init {
 +                        let snip = snippet_with_macro_callsite(cx, expr.span, "()");
 +                        diag.span_suggestion(
 +                            stmt.span,
 +                            "omit the `let` binding",
 +                            format!("{};", snip),
 +                            Applicability::MachineApplicable, // snippet
 +                        );
 +                    }
 +                },
 +            );
 +        }
 +    }
 +}
++
++fn needs_inferred_result_ty(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
++    let id = match e.kind {
++        ExprKind::Call(
++            Expr {
++                kind: ExprKind::Path(ref path),
++                hir_id,
++                ..
++            },
++            _,
++        ) => cx.qpath_res(path, *hir_id).opt_def_id(),
++        ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(e.hir_id),
++        _ => return false,
++    };
++    if let Some(id) = id
++        && let sig = cx.tcx.fn_sig(id).skip_binder()
++        && let ty::Param(output_ty) = *sig.output().kind()
++    {
++        sig.inputs().iter().all(|&ty| !ty_contains_param(ty, output_ty.index))
++    } else {
++        false
++    }
++}
++
++fn ty_contains_param(ty: Ty<'_>, index: u32) -> bool {
++    struct Visitor(u32);
++    impl<'tcx> TypeVisitor<'tcx> for Visitor {
++        type BreakTy = ();
++        fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
++            if let ty::Param(ty) = *ty.kind() {
++                if ty.index == self.0 {
++                    ControlFlow::BREAK
++                } else {
++                    ControlFlow::CONTINUE
++                }
++            } else {
++                ty.super_visit_with(self)
++            }
++        }
++    }
++    ty.visit_with(&mut Visitor(index)).is_break()
++}
index d9f5b53b413a03dd5734a2edadfa202f7bf7ee98,0000000000000000000000000000000000000000..a9e2073dec251de0d50b62c3b8d12df6531409f3
mode 100644,000000..100644
--- /dev/null
@@@ -1,110 -1,0 +1,110 @@@
-     pedantic,
 +mod let_unit_value;
 +mod unit_arg;
 +mod unit_cmp;
 +mod utils;
 +
 +use rustc_hir::{Expr, Stmt};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for binding a unit value.
 +    ///
 +    /// ### Why is this bad?
 +    /// A unit value cannot usefully be used anywhere. So
 +    /// binding one is kind of pointless.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = {
 +    ///     1;
 +    /// };
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub LET_UNIT_VALUE,
++    style,
 +    "creating a `let` binding to a value of unit type, which usually can't be used afterwards"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for comparisons to unit. This includes all binary
 +    /// comparisons (like `==` and `<`) and asserts.
 +    ///
 +    /// ### Why is this bad?
 +    /// Unit is always equal to itself, and thus is just a
 +    /// clumsily written constant. Mostly this happens when someone accidentally
 +    /// adds semicolons at the end of the operands.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn foo() {};
 +    /// # fn bar() {};
 +    /// # fn baz() {};
 +    /// if {
 +    ///     foo();
 +    /// } == {
 +    ///     bar();
 +    /// } {
 +    ///     baz();
 +    /// }
 +    /// ```
 +    /// is equal to
 +    /// ```rust
 +    /// # fn foo() {};
 +    /// # fn bar() {};
 +    /// # fn baz() {};
 +    /// {
 +    ///     foo();
 +    ///     bar();
 +    ///     baz();
 +    /// }
 +    /// ```
 +    ///
 +    /// For asserts:
 +    /// ```rust
 +    /// # fn foo() {};
 +    /// # fn bar() {};
 +    /// assert_eq!({ foo(); }, { bar(); });
 +    /// ```
 +    /// will always succeed
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNIT_CMP,
 +    correctness,
 +    "comparing unit values"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for passing a unit value as an argument to a function without using a
 +    /// unit literal (`()`).
 +    ///
 +    /// ### Why is this bad?
 +    /// This is likely the result of an accidental semicolon.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// foo({
 +    ///     let a = bar();
 +    ///     baz(a);
 +    /// })
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNIT_ARG,
 +    complexity,
 +    "passing unit to a function"
 +}
 +
 +declare_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP, UNIT_ARG]);
 +
 +impl LateLintPass<'_> for UnitTypes {
 +    fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
 +        let_unit_value::check(cx, stmt);
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        unit_cmp::check(cx, expr);
 +        unit_arg::check(cx, expr);
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8a4f4c0ad9719f041d32083c357a910ebbfe20d4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,81 @@@
++use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item};
++use clippy_utils::{match_def_path, paths};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::sym;
++
++declare_clippy_lint! {
++    /// ### What it does
++    ///
++    /// Detects cases of owned empty strings being passed as an argument to a function expecting `&str`
++    ///
++    /// ### Why is this bad?
++    ///
++    /// This results in longer and less readable code
++    ///
++    /// ### Example
++    /// ```rust
++    /// vec!["1", "2", "3"].join(&String::new());
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// vec!["1", "2", "3"].join("");
++    /// ```
++    #[clippy::version = "1.62.0"]
++    pub UNNECESSARY_OWNED_EMPTY_STRINGS,
++    style,
++    "detects cases of references to owned empty strings being passed as an argument to a function expecting `&str`"
++}
++declare_lint_pass!(UnnecessaryOwnedEmptyStrings => [UNNECESSARY_OWNED_EMPTY_STRINGS]);
++
++impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings {
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
++        if_chain! {
++            if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner_expr) = expr.kind;
++            if let ExprKind::Call(fun, args) = inner_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 let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
++            if inner_str.is_str();
++            then {
++                if match_def_path(cx, fun_def_id, &paths::STRING_NEW) {
++                     span_lint_and_sugg(
++                            cx,
++                            UNNECESSARY_OWNED_EMPTY_STRINGS,
++                            expr.span,
++                            "usage of `&String::new()` for a function expecting a `&str` argument",
++                            "try",
++                            "\"\"".to_owned(),
++                            Applicability::MachineApplicable,
++                        );
++                } else {
++                    if_chain! {
++                        if match_def_path(cx, fun_def_id, &paths::FROM_FROM);
++                        if let [.., last_arg] = args;
++                        if let ExprKind::Lit(spanned) = &last_arg.kind;
++                        if let LitKind::Str(symbol, _) = spanned.node;
++                        if symbol.is_empty();
++                        let inner_expr_type = cx.typeck_results().expr_ty(inner_expr);
++                        if is_type_diagnostic_item(cx, inner_expr_type, sym::String);
++                        then {
++                            span_lint_and_sugg(
++                                cx,
++                                UNNECESSARY_OWNED_EMPTY_STRINGS,
++                                expr.span,
++                                "usage of `&String::from(\"\")` for a function expecting a `&str` argument",
++                                "try",
++                                "\"\"".to_owned(),
++                                Applicability::MachineApplicable,
++                            );
++                        }
++                    }
++                }
++            }
++        }
++    }
++}
index 1d4fe9cfd3cf40a8ac5459cf206f9c0704f711b0,0000000000000000000000000000000000000000..ae431aac83b82c0ccaf7eaf3a1dfcc1317c5795d
mode 100644,000000..100644
--- /dev/null
@@@ -1,424 -1,0 +1,428 @@@
- use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
 +#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
 +
 +use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path};
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::{meets_msrv, msrvs, over};
 +use rustc_ast::mut_visit::*;
 +use rustc_ast::ptr::P;
-     /// In the example above, `Some` is repeated, which unncessarily complicates the pattern.
++use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
 +use rustc_ast_pretty::pprust;
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::DUMMY_SP;
 +
 +use std::cell::Cell;
 +use std::mem;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and
 +    /// suggests replacing the pattern with a nested one, `Some(0 | 2)`.
 +    ///
 +    /// Another way to think of this is that it rewrites patterns in
 +    /// *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*.
 +    ///
 +    /// ### Why is this bad?
-         // Transform `&m x | ... | &m y` into `&m (x | y)`.
-         Ref(target, m1) => extend_with_matching(
++    /// In the example above, `Some` is repeated, which unnecessarily complicates the pattern.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn main() {
 +    ///     if let Some(0) | Some(2) = Some(0) {}
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn main() {
 +    ///     if let Some(0 | 2) = Some(0) {}
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.46.0"]
 +    pub UNNESTED_OR_PATTERNS,
 +    pedantic,
 +    "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
 +}
 +
 +#[derive(Clone, Copy)]
 +pub struct UnnestedOrPatterns {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl UnnestedOrPatterns {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]);
 +
 +impl EarlyLintPass for UnnestedOrPatterns {
 +    fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) {
 +        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
 +            lint_unnested_or_patterns(cx, &a.pat);
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
 +        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
 +            if let ast::ExprKind::Let(pat, _, _) = &e.kind {
 +                lint_unnested_or_patterns(cx, pat);
 +            }
 +        }
 +    }
 +
 +    fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) {
 +        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
 +            lint_unnested_or_patterns(cx, &p.pat);
 +        }
 +    }
 +
 +    fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) {
 +        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
 +            lint_unnested_or_patterns(cx, &l.pat);
 +        }
 +    }
 +
 +    extract_msrv_attr!(EarlyContext);
 +}
 +
 +fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) {
 +    if let Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind {
 +        // This is a leaf pattern, so cloning is unprofitable.
 +        return;
 +    }
 +
 +    let mut pat = P(pat.clone());
 +
 +    // Nix all the paren patterns everywhere so that they aren't in our way.
 +    remove_all_parens(&mut pat);
 +
 +    // Transform all unnested or-patterns into nested ones, and if there were none, quit.
 +    if !unnest_or_patterns(&mut pat) {
 +        return;
 +    }
 +
 +    span_lint_and_then(cx, UNNESTED_OR_PATTERNS, pat.span, "unnested or-patterns", |db| {
 +        insert_necessary_parens(&mut pat);
 +        db.span_suggestion_verbose(
 +            pat.span,
 +            "nest the patterns",
 +            pprust::pat_to_string(&pat),
 +            Applicability::MachineApplicable,
 +        );
 +    });
 +}
 +
 +/// Remove all `(p)` patterns in `pat`.
 +fn remove_all_parens(pat: &mut P<Pat>) {
 +    struct Visitor;
 +    impl MutVisitor for Visitor {
 +        fn visit_pat(&mut self, pat: &mut P<Pat>) {
 +            noop_visit_pat(pat, self);
 +            let inner = match &mut pat.kind {
 +                Paren(i) => mem::replace(&mut i.kind, Wild),
 +                _ => return,
 +            };
 +            pat.kind = inner;
 +        }
 +    }
 +    Visitor.visit_pat(pat);
 +}
 +
 +/// Insert parens where necessary according to Rust's precedence rules for patterns.
 +fn insert_necessary_parens(pat: &mut P<Pat>) {
 +    struct Visitor;
 +    impl MutVisitor for Visitor {
 +        fn visit_pat(&mut self, pat: &mut P<Pat>) {
 +            use ast::{BindingMode::*, Mutability::*};
 +            noop_visit_pat(pat, self);
 +            let target = match &mut pat.kind {
 +                // `i @ a | b`, `box a | b`, and `& mut? a | b`.
 +                Ident(.., Some(p)) | Box(p) | Ref(p, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p,
 +                Ref(p, Not) if matches!(p.kind, Ident(ByValue(Mut), ..)) => p, // `&(mut x)`
 +                _ => return,
 +            };
 +            target.kind = Paren(P(take_pat(target)));
 +        }
 +    }
 +    Visitor.visit_pat(pat);
 +}
 +
 +/// Unnest or-patterns `p0 | ... | p1` in the pattern `pat`.
 +/// For example, this would transform `Some(0) | FOO | Some(2)` into `Some(0 | 2) | FOO`.
 +fn unnest_or_patterns(pat: &mut P<Pat>) -> bool {
 +    struct Visitor {
 +        changed: bool,
 +    }
 +    impl MutVisitor for Visitor {
 +        fn visit_pat(&mut self, p: &mut P<Pat>) {
 +            // This is a bottom up transformation, so recurse first.
 +            noop_visit_pat(p, self);
 +
 +            // Don't have an or-pattern? Just quit early on.
 +            let alternatives = match &mut p.kind {
 +                Or(ps) => ps,
 +                _ => return,
 +            };
 +
 +            // Collapse or-patterns directly nested in or-patterns.
 +            let mut idx = 0;
 +            let mut this_level_changed = false;
 +            while idx < alternatives.len() {
 +                let inner = if let Or(ps) = &mut alternatives[idx].kind {
 +                    mem::take(ps)
 +                } else {
 +                    idx += 1;
 +                    continue;
 +                };
 +                this_level_changed = true;
 +                alternatives.splice(idx..=idx, inner);
 +            }
 +
 +            // Focus on `p_n` and then try to transform all `p_i` where `i > n`.
 +            let mut focus_idx = 0;
 +            while focus_idx < alternatives.len() {
 +                this_level_changed |= transform_with_focus_on_idx(alternatives, focus_idx);
 +                focus_idx += 1;
 +            }
 +            self.changed |= this_level_changed;
 +
 +            // Deal with `Some(Some(0)) | Some(Some(1))`.
 +            if this_level_changed {
 +                noop_visit_pat(p, self);
 +            }
 +        }
 +    }
 +
 +    let mut visitor = Visitor { changed: false };
 +    visitor.visit_pat(pat);
 +    visitor.changed
 +}
 +
 +/// Match `$scrutinee` against `$pat` and extract `$then` from it.
 +/// Panics if there is no match.
 +macro_rules! always_pat {
 +    ($scrutinee:expr, $pat:pat => $then:expr) => {
 +        match $scrutinee {
 +            $pat => $then,
 +            _ => unreachable!(),
 +        }
 +    };
 +}
 +
 +/// Focus on `focus_idx` in `alternatives`,
 +/// attempting to extend it with elements of the same constructor `C`
 +/// in `alternatives[focus_idx + 1..]`.
 +fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize) -> bool {
 +    // Extract the kind; we'll need to make some changes in it.
 +    let mut focus_kind = mem::replace(&mut alternatives[focus_idx].kind, PatKind::Wild);
 +    // We'll focus on `alternatives[focus_idx]`,
 +    // so we're draining from `alternatives[focus_idx + 1..]`.
 +    let start = focus_idx + 1;
 +
 +    // We're trying to find whatever kind (~"constructor") we found in `alternatives[start..]`.
 +    let changed = match &mut focus_kind {
 +        // These pattern forms are "leafs" and do not have sub-patterns.
 +        // Therefore they are not some form of constructor `C`,
 +        // with which a pattern `C(p_0)` may be formed,
 +        // which we would want to join with other `C(p_j)`s.
 +        Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_)
++        // Skip immutable refs, as grouping them saves few characters,
++        // and almost always requires adding parens (increasing noisiness).
++        // In the case of only two patterns, replacement adds net characters.
++        | Ref(_, Mutability::Not)
 +        // Dealt with elsewhere.
 +        | Or(_) | Paren(_) => false,
 +        // Transform `box x | ... | box y` into `box (x | y)`.
 +        //
 +        // The cases below until `Slice(...)` deal with *singleton* products.
 +        // These patterns have the shape `C(p)`, and not e.g., `C(p0, ..., pn)`.
 +        Box(target) => extend_with_matching(
 +            target, start, alternatives,
 +            |k| matches!(k, Box(_)),
 +            |k| always_pat!(k, Box(p) => p),
 +        ),
-             |k| matches!(k, Ref(_, m2) if m1 == m2), // Mutabilities must match.
++        // Transform `&mut x | ... | &mut y` into `&mut (x | y)`.
++        Ref(target, Mutability::Mut) => extend_with_matching(
 +            target, start, alternatives,
++            |k| matches!(k, Ref(_, Mutability::Mut)),
 +            |k| always_pat!(k, Ref(p, _) => p),
 +        ),
 +        // Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`.
 +        Ident(b1, i1, Some(target)) => extend_with_matching(
 +            target, start, alternatives,
 +            // Binding names must match.
 +            |k| matches!(k, Ident(b2, i2, Some(_)) if b1 == b2 && eq_id(*i1, *i2)),
 +            |k| always_pat!(k, Ident(_, _, Some(p)) => p),
 +        ),
 +        // Transform `[pre, x, post] | ... | [pre, y, post]` into `[pre, x | y, post]`.
 +        Slice(ps1) => extend_with_matching_product(
 +            ps1, start, alternatives,
 +            |k, ps1, idx| matches!(k, Slice(ps2) if eq_pre_post(ps1, ps2, idx)),
 +            |k| always_pat!(k, Slice(ps) => ps),
 +        ),
 +        // Transform `(pre, x, post) | ... | (pre, y, post)` into `(pre, x | y, post)`.
 +        Tuple(ps1) => extend_with_matching_product(
 +            ps1, start, alternatives,
 +            |k, ps1, idx| matches!(k, Tuple(ps2) if eq_pre_post(ps1, ps2, idx)),
 +            |k| always_pat!(k, Tuple(ps) => ps),
 +        ),
 +        // Transform `S(pre, x, post) | ... | S(pre, y, post)` into `S(pre, x | y, post)`.
 +        TupleStruct(qself1, path1, ps1) => extend_with_matching_product(
 +            ps1, start, alternatives,
 +            |k, ps1, idx| matches!(
 +                k,
 +                TupleStruct(qself2, path2, ps2)
 +                    if eq_maybe_qself(qself1, qself2) && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx)
 +            ),
 +            |k| always_pat!(k, TupleStruct(_, _, ps) => ps),
 +        ),
 +        // Transform a record pattern `S { fp_0, ..., fp_n }`.
 +        Struct(qself1, path1, fps1, rest1) => extend_with_struct_pat(qself1, path1, fps1, *rest1, start, alternatives),
 +    };
 +
 +    alternatives[focus_idx].kind = focus_kind;
 +    changed
 +}
 +
 +/// Here we focusing on a record pattern `S { fp_0, ..., fp_n }`.
 +/// In particular, for a record pattern, the order in which the field patterns is irrelevant.
 +/// So when we fixate on some `ident_k: pat_k`, we try to find `ident_k` in the other pattern
 +/// and check that all `fp_i` where `i ∈ ((0...n) \ k)` between two patterns are equal.
 +fn extend_with_struct_pat(
 +    qself1: &Option<ast::QSelf>,
 +    path1: &ast::Path,
 +    fps1: &mut [ast::PatField],
 +    rest1: bool,
 +    start: usize,
 +    alternatives: &mut Vec<P<Pat>>,
 +) -> bool {
 +    (0..fps1.len()).any(|idx| {
 +        let pos_in_2 = Cell::new(None); // The element `k`.
 +        let tail_or = drain_matching(
 +            start,
 +            alternatives,
 +            |k| {
 +                matches!(k, Struct(qself2, path2, fps2, rest2)
 +                if rest1 == *rest2 // If one struct pattern has `..` so must the other.
 +                && eq_maybe_qself(qself1, qself2)
 +                && eq_path(path1, path2)
 +                && fps1.len() == fps2.len()
 +                && fps1.iter().enumerate().all(|(idx_1, fp1)| {
 +                    if idx_1 == idx {
 +                        // In the case of `k`, we merely require identical field names
 +                        // so that we will transform into `ident_k: p1_k | p2_k`.
 +                        let pos = fps2.iter().position(|fp2| eq_id(fp1.ident, fp2.ident));
 +                        pos_in_2.set(pos);
 +                        pos.is_some()
 +                    } else {
 +                        fps2.iter().any(|fp2| eq_field_pat(fp1, fp2))
 +                    }
 +                }))
 +            },
 +            // Extract `p2_k`.
 +            |k| always_pat!(k, Struct(_, _, mut fps, _) => fps.swap_remove(pos_in_2.take().unwrap()).pat),
 +        );
 +        extend_with_tail_or(&mut fps1[idx].pat, tail_or)
 +    })
 +}
 +
 +/// Like `extend_with_matching` but for products with > 1 factor, e.g., `C(p_0, ..., p_n)`.
 +/// Here, the idea is that we fixate on some `p_k` in `C`,
 +/// allowing it to vary between two `targets` and `ps2` (returned by `extract`),
 +/// while also requiring `ps1[..n] ~ ps2[..n]` (pre) and `ps1[n + 1..] ~ ps2[n + 1..]` (post),
 +/// where `~` denotes semantic equality.
 +fn extend_with_matching_product(
 +    targets: &mut [P<Pat>],
 +    start: usize,
 +    alternatives: &mut Vec<P<Pat>>,
 +    predicate: impl Fn(&PatKind, &[P<Pat>], usize) -> bool,
 +    extract: impl Fn(PatKind) -> Vec<P<Pat>>,
 +) -> bool {
 +    (0..targets.len()).any(|idx| {
 +        let tail_or = drain_matching(
 +            start,
 +            alternatives,
 +            |k| predicate(k, targets, idx),
 +            |k| extract(k).swap_remove(idx),
 +        );
 +        extend_with_tail_or(&mut targets[idx], tail_or)
 +    })
 +}
 +
 +/// Extract the pattern from the given one and replace it with `Wild`.
 +/// This is meant for temporarily swapping out the pattern for manipulation.
 +fn take_pat(from: &mut Pat) -> Pat {
 +    let dummy = Pat {
 +        id: DUMMY_NODE_ID,
 +        kind: Wild,
 +        span: DUMMY_SP,
 +        tokens: None,
 +    };
 +    mem::replace(from, dummy)
 +}
 +
 +/// Extend `target` as an or-pattern with the alternatives
 +/// in `tail_or` if there are any and return if there were.
 +fn extend_with_tail_or(target: &mut Pat, tail_or: Vec<P<Pat>>) -> bool {
 +    fn extend(target: &mut Pat, mut tail_or: Vec<P<Pat>>) {
 +        match target {
 +            // On an existing or-pattern in the target, append to it.
 +            Pat { kind: Or(ps), .. } => ps.append(&mut tail_or),
 +            // Otherwise convert the target to an or-pattern.
 +            target => {
 +                let mut init_or = vec![P(take_pat(target))];
 +                init_or.append(&mut tail_or);
 +                target.kind = Or(init_or);
 +            },
 +        }
 +    }
 +
 +    let changed = !tail_or.is_empty();
 +    if changed {
 +        // Extend the target.
 +        extend(target, tail_or);
 +    }
 +    changed
 +}
 +
 +// Extract all inner patterns in `alternatives` matching our `predicate`.
 +// Only elements beginning with `start` are considered for extraction.
 +fn drain_matching(
 +    start: usize,
 +    alternatives: &mut Vec<P<Pat>>,
 +    predicate: impl Fn(&PatKind) -> bool,
 +    extract: impl Fn(PatKind) -> P<Pat>,
 +) -> Vec<P<Pat>> {
 +    let mut tail_or = vec![];
 +    let mut idx = 0;
 +    for pat in alternatives.drain_filter(|p| {
 +        // Check if we should extract, but only if `idx >= start`.
 +        idx += 1;
 +        idx > start && predicate(&p.kind)
 +    }) {
 +        tail_or.push(extract(pat.into_inner().kind));
 +    }
 +    tail_or
 +}
 +
 +fn extend_with_matching(
 +    target: &mut Pat,
 +    start: usize,
 +    alternatives: &mut Vec<P<Pat>>,
 +    predicate: impl Fn(&PatKind) -> bool,
 +    extract: impl Fn(PatKind) -> P<Pat>,
 +) -> bool {
 +    extend_with_tail_or(target, drain_matching(start, alternatives, predicate, extract))
 +}
 +
 +/// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`?
 +fn eq_pre_post(ps1: &[P<Pat>], ps2: &[P<Pat>], idx: usize) -> bool {
 +    ps1.len() == ps2.len()
 +        && ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`.
 +        && over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r))
 +        && over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r))
 +}
index f8e1021af0ea11b9bd46acc78dfe62ea9ea4ccaf,0000000000000000000000000000000000000000..138f8bccb3f5741ba14987fc31d73fa9e6d34a5f
mode 100644,000000..100644
--- /dev/null
@@@ -1,312 -1,0 +1,312 @@@
-     /// - False positive with assotiated types in traits (#4140)
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::ty::same_type_and_consts;
 +use clippy_utils::{meets_msrv, msrvs};
 +use if_chain::if_chain;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    self as hir,
 +    def::{CtorOf, DefKind, Res},
 +    def_id::LocalDefId,
 +    intravisit::{walk_inf, walk_ty, Visitor},
 +    Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath,
 +    TyKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::Span;
 +use rustc_typeck::hir_ty_to_ty;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary repetition of structure name when a
 +    /// replacement with `Self` is applicable.
 +    ///
 +    /// ### Why is this bad?
 +    /// Unnecessary repetition. Mixed use of `Self` and struct
 +    /// name
 +    /// feels inconsistent.
 +    ///
 +    /// ### Known problems
 +    /// - Unaddressed false negative in fn bodies of trait implementations
++    /// - False positive with associated types in traits (#4140)
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Foo;
 +    /// impl Foo {
 +    ///     fn new() -> Foo {
 +    ///         Foo {}
 +    ///     }
 +    /// }
 +    /// ```
 +    /// could be
 +    /// ```rust
 +    /// struct Foo;
 +    /// impl Foo {
 +    ///     fn new() -> Self {
 +    ///         Self {}
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USE_SELF,
 +    nursery,
 +    "unnecessary structure name repetition whereas `Self` is applicable"
 +}
 +
 +#[derive(Default)]
 +pub struct UseSelf {
 +    msrv: Option<RustcVersion>,
 +    stack: Vec<StackItem>,
 +}
 +
 +impl UseSelf {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self {
 +            msrv,
 +            ..Self::default()
 +        }
 +    }
 +}
 +
 +#[derive(Debug)]
 +enum StackItem {
 +    Check {
 +        impl_id: LocalDefId,
 +        in_body: u32,
 +        types_to_skip: FxHashSet<HirId>,
 +    },
 +    NoCheck,
 +}
 +
 +impl_lint_pass!(UseSelf => [USE_SELF]);
 +
 +const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
 +
 +impl<'tcx> LateLintPass<'tcx> for UseSelf {
 +    fn check_item(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) {
 +        if matches!(item.kind, ItemKind::OpaqueTy(_)) {
 +            // skip over `ItemKind::OpaqueTy` in order to lint `foo() -> impl <..>`
 +            return;
 +        }
 +        // We push the self types of `impl`s on a stack here. Only the top type on the stack is
 +        // relevant for linting, since this is the self type of the `impl` we're currently in. To
 +        // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
 +        // we're in an `impl` or nested item, that we don't want to lint
 +        let stack_item = if_chain! {
 +            if let ItemKind::Impl(Impl { self_ty, .. }) = item.kind;
 +            if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind;
 +            let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
 +            if parameters.as_ref().map_or(true, |params| {
 +                !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
 +            });
 +            then {
 +                StackItem::Check {
 +                    impl_id: item.def_id,
 +                    in_body: 0,
 +                    types_to_skip: std::iter::once(self_ty.hir_id).collect(),
 +                }
 +            } else {
 +                StackItem::NoCheck
 +            }
 +        };
 +        self.stack.push(stack_item);
 +    }
 +
 +    fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
 +        if !matches!(item.kind, ItemKind::OpaqueTy(_)) {
 +            self.stack.pop();
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
 +        // We want to skip types in trait `impl`s that aren't declared as `Self` in the trait
 +        // declaration. The collection of those types is all this method implementation does.
 +        if_chain! {
 +            if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind;
 +            if let Some(&mut StackItem::Check {
 +                impl_id,
 +                ref mut types_to_skip,
 +                ..
 +            }) = self.stack.last_mut();
 +            if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_id);
 +            then {
 +                // `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
 +                // `Self`.
 +                let self_ty = impl_trait_ref.self_ty();
 +
 +                // `trait_method_sig` is the signature of the function, how it is declared in the
 +                // trait, not in the impl of the trait.
 +                let trait_method = cx
 +                    .tcx
 +                    .associated_item(impl_item.def_id)
 +                    .trait_item_def_id
 +                    .expect("impl method matches a trait method");
 +                let trait_method_sig = cx.tcx.fn_sig(trait_method);
 +                let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig);
 +
 +                // `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the
 +                // implementation of the trait.
 +                let output_hir_ty = if let FnRetTy::Return(ty) = &decl.output {
 +                    Some(&**ty)
 +                } else {
 +                    None
 +                };
 +                let impl_inputs_outputs = decl.inputs.iter().chain(output_hir_ty);
 +
 +                // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
 +                //
 +                // `trait_sem_ty` (of type `ty::Ty`) is the semantic type for the signature in the
 +                // trait declaration. This is used to check if `Self` was used in the trait
 +                // declaration.
 +                //
 +                // If `any`where in the `trait_sem_ty` the `self_ty` was used verbatim (as opposed
 +                // to `Self`), we want to skip linting that type and all subtypes of it. This
 +                // avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`, in an `impl Trait
 +                // for u8`, when the trait always uses `Vec<u8>`.
 +                //
 +                // See also https://github.com/rust-lang/rust-clippy/issues/2894.
 +                for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) {
 +                    if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) {
 +                        let mut visitor = SkipTyCollector::default();
 +                        visitor.visit_ty(impl_hir_ty);
 +                        types_to_skip.extend(visitor.types_to_skip);
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_body(&mut self, _: &LateContext<'_>, _: &hir::Body<'_>) {
 +        // `hir_ty_to_ty` cannot be called in `Body`s or it will panic (sometimes). But in bodies
 +        // we can use `cx.typeck_results.node_type(..)` to get the `ty::Ty` from a `hir::Ty`.
 +        // However the `node_type()` method can *only* be called in bodies.
 +        if let Some(&mut StackItem::Check { ref mut in_body, .. }) = self.stack.last_mut() {
 +            *in_body = in_body.saturating_add(1);
 +        }
 +    }
 +
 +    fn check_body_post(&mut self, _: &LateContext<'_>, _: &hir::Body<'_>) {
 +        if let Some(&mut StackItem::Check { ref mut in_body, .. }) = self.stack.last_mut() {
 +            *in_body = in_body.saturating_sub(1);
 +        }
 +    }
 +
 +    fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
 +        if_chain! {
 +            if !hir_ty.span.from_expansion();
 +            if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
 +            if let Some(&StackItem::Check {
 +                impl_id,
 +                in_body,
 +                ref types_to_skip,
 +            }) = self.stack.last();
 +            if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
 +            if !matches!(path.res, Res::SelfTy { .. } | Res::Def(DefKind::TyParam, _));
 +            if !types_to_skip.contains(&hir_ty.hir_id);
 +            let ty = if in_body > 0 {
 +                cx.typeck_results().node_type(hir_ty.hir_id)
 +            } else {
 +                hir_ty_to_ty(cx.tcx, hir_ty)
 +            };
 +            if same_type_and_consts(ty, cx.tcx.type_of(impl_id));
 +            let hir = cx.tcx.hir();
 +            // prevents false positive on `#[derive(serde::Deserialize)]`
 +            if !hir.span(hir.get_parent_node(hir_ty.hir_id)).in_derive_expansion();
 +            then {
 +                span_lint(cx, hir_ty.span);
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        if_chain! {
 +            if !expr.span.from_expansion();
 +            if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
 +            if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
 +            if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id);
 +            then {} else { return; }
 +        }
 +        match expr.kind {
 +            ExprKind::Struct(QPath::Resolved(_, path), ..) => match path.res {
 +                Res::SelfTy { .. } => (),
 +                Res::Def(DefKind::Variant, _) => lint_path_to_variant(cx, path),
 +                _ => span_lint(cx, path.span),
 +            },
 +            // tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
 +            ExprKind::Call(fun, _) => {
 +                if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind {
 +                    if let Res::Def(DefKind::Ctor(ctor_of, _), ..) = path.res {
 +                        match ctor_of {
 +                            CtorOf::Variant => lint_path_to_variant(cx, path),
 +                            CtorOf::Struct => span_lint(cx, path.span),
 +                        }
 +                    }
 +                }
 +            },
 +            // unit enum variants (`Enum::A`)
 +            ExprKind::Path(QPath::Resolved(_, path)) => lint_path_to_variant(cx, path),
 +            _ => (),
 +        }
 +    }
 +
 +    fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
 +        if_chain! {
 +            if !pat.span.from_expansion();
 +            if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
 +            if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
 +            if let PatKind::Path(QPath::Resolved(_, path)) = pat.kind;
 +            if !matches!(path.res, Res::SelfTy { .. } | Res::Def(DefKind::TyParam, _));
 +            if cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id);
 +            if let [first, ..] = path.segments;
 +            if let Some(hir_id) = first.hir_id;
 +            then {
 +                span_lint(cx, cx.tcx.hir().span(hir_id));
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +#[derive(Default)]
 +struct SkipTyCollector {
 +    types_to_skip: Vec<HirId>,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for SkipTyCollector {
 +    fn visit_infer(&mut self, inf: &hir::InferArg) {
 +        self.types_to_skip.push(inf.hir_id);
 +
 +        walk_inf(self, inf);
 +    }
 +    fn visit_ty(&mut self, hir_ty: &hir::Ty<'_>) {
 +        self.types_to_skip.push(hir_ty.hir_id);
 +
 +        walk_ty(self, hir_ty);
 +    }
 +}
 +
 +fn span_lint(cx: &LateContext<'_>, span: Span) {
 +    span_lint_and_sugg(
 +        cx,
 +        USE_SELF,
 +        span,
 +        "unnecessary structure name repetition",
 +        "use the applicable keyword",
 +        "Self".to_owned(),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) {
 +    if let [.., self_seg, _variant] = path.segments {
 +        let span = path
 +            .span
 +            .with_hi(self_seg.args().span_ext().unwrap_or(self_seg.ident.span).hi());
 +        span_lint(cx, span);
 +    }
 +}
index d23c85c033b2f7e63ce1809f30443d055e6eb605,0000000000000000000000000000000000000000..ff5be825b781712030c5383b956721384f1184d6
mode 100644,000000..100644
--- /dev/null
@@@ -1,735 -1,0 +1,735 @@@
- /// Transforms the given `Option<T>` varibles into `OptionPat<Binding<T>>`.
 +//! A group of attributes that can be attached to Rust code in order
 +//! to generate a clippy lint detecting said code automatically.
 +
 +use clippy_utils::{get_attr, higher};
 +use rustc_ast::ast::{LitFloatType, LitKind};
 +use rustc_ast::LitIntType;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir as hir;
 +use rustc_hir::{ArrayLen, ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::{Ident, Symbol};
 +use std::fmt::{Display, Formatter, Write as _};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Generates clippy code that detects the offending pattern
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // ./tests/ui/my_lint.rs
 +    /// fn foo() {
 +    ///     // detect the following pattern
 +    ///     #[clippy::author]
 +    ///     if x == 42 {
 +    ///         // but ignore everything from here on
 +    ///         #![clippy::author = "ignore"]
 +    ///     }
 +    ///     ()
 +    /// }
 +    /// ```
 +    ///
 +    /// Running `TESTNAME=ui/my_lint cargo uitest` will produce
 +    /// a `./tests/ui/new_lint.stdout` file with the generated code:
 +    ///
 +    /// ```rust,ignore
 +    /// // ./tests/ui/new_lint.stdout
 +    /// if_chain! {
 +    ///     if let ExprKind::If(ref cond, ref then, None) = item.kind,
 +    ///     if let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind,
 +    ///     if let ExprKind::Path(ref path) = left.kind,
 +    ///     if let ExprKind::Lit(ref lit) = right.kind,
 +    ///     if let LitKind::Int(42, _) = lit.node,
 +    ///     then {
 +    ///         // report your lint here
 +    ///     }
 +    /// }
 +    /// ```
 +    pub LINT_AUTHOR,
 +    internal_warn,
 +    "helper for writing lints"
 +}
 +
 +declare_lint_pass!(Author => [LINT_AUTHOR]);
 +
 +/// Writes a line of output with indentation added
 +macro_rules! out {
 +    ($($t:tt)*) => {
 +        println!("    {}", format_args!($($t)*))
 +    };
 +}
 +
 +/// The variables passed in are replaced with `&Binding`s where the `value` field is set
 +/// to the original value of the variable. The `name` field is set to the name of the variable
 +/// (using `stringify!`) and is adjusted to avoid duplicate names.
 +/// Note that the `Binding` may be printed directly to output the `name`.
 +macro_rules! bind {
 +    ($self:ident $(, $name:ident)+) => {
 +        $(let $name = & $self.bind(stringify!($name), $name);)+
 +    };
 +}
 +
++/// Transforms the given `Option<T>` variables into `OptionPat<Binding<T>>`.
 +/// This displays as `Some($name)` or `None` when printed. The name of the inner binding
 +/// is set to the name of the variable passed to the macro.
 +macro_rules! opt_bind {
 +    ($self:ident $(, $name:ident)+) => {
 +        $(let $name = OptionPat::new($name.map(|o| $self.bind(stringify!($name), o)));)+
 +    };
 +}
 +
 +/// Creates a `Binding` that accesses the field of an existing `Binding`
 +macro_rules! field {
 +    ($binding:ident.$field:ident) => {
 +        &Binding {
 +            name: $binding.name.to_string() + stringify!(.$field),
 +            value: $binding.value.$field,
 +        }
 +    };
 +}
 +
 +fn prelude() {
 +    println!("if_chain! {{");
 +}
 +
 +fn done() {
 +    println!("    then {{");
 +    println!("        // report your lint here");
 +    println!("    }}");
 +    println!("}}");
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for Author {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        check_item(cx, item.hir_id());
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
 +        check_item(cx, item.hir_id());
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
 +        check_item(cx, item.hir_id());
 +    }
 +
 +    fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) {
 +        check_node(cx, arm.hir_id, |v| {
 +            v.arm(&v.bind("arm", arm));
 +        });
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        check_node(cx, expr.hir_id, |v| {
 +            v.expr(&v.bind("expr", expr));
 +        });
 +    }
 +
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
 +        match stmt.kind {
 +            StmtKind::Expr(e) | StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return,
 +            _ => {},
 +        }
 +        check_node(cx, stmt.hir_id, |v| {
 +            v.stmt(&v.bind("stmt", stmt));
 +        });
 +    }
 +}
 +
 +fn check_item(cx: &LateContext<'_>, hir_id: HirId) {
 +    let hir = cx.tcx.hir();
 +    if let Some(body_id) = hir.maybe_body_owned_by(hir_id) {
 +        check_node(cx, hir_id, |v| {
 +            v.expr(&v.bind("expr", &hir.body(body_id).value));
 +        });
 +    }
 +}
 +
 +fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, '_>)) {
 +    if has_attr(cx, hir_id) {
 +        prelude();
 +        f(&PrintVisitor::new(cx));
 +        done();
 +    }
 +}
 +
 +struct Binding<T> {
 +    name: String,
 +    value: T,
 +}
 +
 +impl<T> Display for Binding<T> {
 +    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
 +        f.write_str(&self.name)
 +    }
 +}
 +
 +struct OptionPat<T> {
 +    pub opt: Option<T>,
 +}
 +
 +impl<T> OptionPat<T> {
 +    fn new(opt: Option<T>) -> Self {
 +        Self { opt }
 +    }
 +
 +    fn if_some(&self, f: impl Fn(&T)) {
 +        if let Some(t) = &self.opt {
 +            f(t);
 +        }
 +    }
 +}
 +
 +impl<T: Display> Display for OptionPat<T> {
 +    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
 +        match &self.opt {
 +            None => f.write_str("None"),
 +            Some(node) => write!(f, "Some({node})"),
 +        }
 +    }
 +}
 +
 +struct PrintVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    /// Fields are the current index that needs to be appended to pattern
 +    /// binding names
 +    ids: std::cell::Cell<FxHashMap<&'static str, u32>>,
 +}
 +
 +#[allow(clippy::unused_self)]
 +impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            cx,
 +            ids: std::cell::Cell::default(),
 +        }
 +    }
 +
 +    fn next(&self, s: &'static str) -> String {
 +        let mut ids = self.ids.take();
 +        let out = match *ids.entry(s).and_modify(|n| *n += 1).or_default() {
 +            // first usage of the name, use it as is
 +            0 => s.to_string(),
 +            // append a number starting with 1
 +            n => format!("{s}{n}"),
 +        };
 +        self.ids.set(ids);
 +        out
 +    }
 +
 +    fn bind<T>(&self, name: &'static str, value: T) -> Binding<T> {
 +        let name = self.next(name);
 +        Binding { name, value }
 +    }
 +
 +    fn option<T: Copy>(&self, option: &Binding<Option<T>>, name: &'static str, f: impl Fn(&Binding<T>)) {
 +        match option.value {
 +            None => out!("if {option}.is_none();"),
 +            Some(value) => {
 +                let value = &self.bind(name, value);
 +                out!("if let Some({value}) = {option};");
 +                f(value);
 +            },
 +        }
 +    }
 +
 +    fn slice<T>(&self, slice: &Binding<&[T]>, f: impl Fn(&Binding<&T>)) {
 +        if slice.value.is_empty() {
 +            out!("if {slice}.is_empty();");
 +        } else {
 +            out!("if {slice}.len() == {};", slice.value.len());
 +            for (i, value) in slice.value.iter().enumerate() {
 +                let name = format!("{slice}[{i}]");
 +                f(&Binding { name, value });
 +            }
 +        }
 +    }
 +
 +    fn destination(&self, destination: &Binding<hir::Destination>) {
 +        self.option(field!(destination.label), "label", |label| {
 +            self.ident(field!(label.ident));
 +        });
 +    }
 +
 +    fn ident(&self, ident: &Binding<Ident>) {
 +        out!("if {ident}.as_str() == {:?};", ident.value.as_str());
 +    }
 +
 +    fn symbol(&self, symbol: &Binding<Symbol>) {
 +        out!("if {symbol}.as_str() == {:?};", symbol.value.as_str());
 +    }
 +
 +    fn qpath(&self, qpath: &Binding<&QPath<'_>>) {
 +        if let QPath::LangItem(lang_item, ..) = *qpath.value {
 +            out!("if matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _));");
 +        } else {
 +            out!("if match_qpath({qpath}, &[{}]);", path_to_string(qpath.value));
 +        }
 +    }
 +
 +    fn lit(&self, lit: &Binding<&Lit>) {
 +        let kind = |kind| out!("if let LitKind::{kind} = {lit}.node;");
 +        macro_rules! kind {
 +            ($($t:tt)*) => (kind(format_args!($($t)*)));
 +        }
 +
 +        match lit.value.node {
 +            LitKind::Bool(val) => kind!("Bool({val:?})"),
 +            LitKind::Char(c) => kind!("Char({c:?})"),
 +            LitKind::Err(val) => kind!("Err({val})"),
 +            LitKind::Byte(b) => kind!("Byte({b})"),
 +            LitKind::Int(i, suffix) => {
 +                let int_ty = match suffix {
 +                    LitIntType::Signed(int_ty) => format!("LitIntType::Signed(IntTy::{int_ty:?})"),
 +                    LitIntType::Unsigned(uint_ty) => format!("LitIntType::Unsigned(UintTy::{uint_ty:?})"),
 +                    LitIntType::Unsuffixed => String::from("LitIntType::Unsuffixed"),
 +                };
 +                kind!("Int({i}, {int_ty})");
 +            },
 +            LitKind::Float(_, suffix) => {
 +                let float_ty = match suffix {
 +                    LitFloatType::Suffixed(suffix_ty) => format!("LitFloatType::Suffixed(FloatTy::{suffix_ty:?})"),
 +                    LitFloatType::Unsuffixed => String::from("LitFloatType::Unsuffixed"),
 +                };
 +                kind!("Float(_, {float_ty})");
 +            },
 +            LitKind::ByteStr(ref vec) => {
 +                bind!(self, vec);
 +                kind!("ByteStr(ref {vec})");
 +                out!("if let [{:?}] = **{vec};", vec.value);
 +            },
 +            LitKind::Str(s, _) => {
 +                bind!(self, s);
 +                kind!("Str({s}, _)");
 +                self.symbol(s);
 +            },
 +        }
 +    }
 +
 +    fn arm(&self, arm: &Binding<&hir::Arm<'_>>) {
 +        self.pat(field!(arm.pat));
 +        match arm.value.guard {
 +            None => out!("if {arm}.guard.is_none();"),
 +            Some(hir::Guard::If(expr)) => {
 +                bind!(self, expr);
 +                out!("if let Some(Guard::If({expr})) = {arm}.guard;");
 +                self.expr(expr);
 +            },
 +            Some(hir::Guard::IfLet(pat, expr)) => {
 +                bind!(self, pat, expr);
 +                out!("if let Some(Guard::IfLet({pat}, {expr}) = {arm}.guard;");
 +                self.pat(pat);
 +                self.expr(expr);
 +            },
 +        }
 +        self.expr(field!(arm.body));
 +    }
 +
 +    #[allow(clippy::too_many_lines)]
 +    fn expr(&self, expr: &Binding<&hir::Expr<'_>>) {
 +        if let Some(higher::While { condition, body }) = higher::While::hir(expr.value) {
 +            bind!(self, condition, body);
 +            out!(
 +                "if let Some(higher::While {{ condition: {condition}, body: {body} }}) \
 +                = higher::While::hir({expr});"
 +            );
 +            self.expr(condition);
 +            self.expr(body);
 +            return;
 +        }
 +
 +        if let Some(higher::WhileLet {
 +            let_pat,
 +            let_expr,
 +            if_then,
 +        }) = higher::WhileLet::hir(expr.value)
 +        {
 +            bind!(self, let_pat, let_expr, if_then);
 +            out!(
 +                "if let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \
 +                = higher::WhileLet::hir({expr});"
 +            );
 +            self.pat(let_pat);
 +            self.expr(let_expr);
 +            self.expr(if_then);
 +            return;
 +        }
 +
 +        if let Some(higher::ForLoop { pat, arg, body, .. }) = higher::ForLoop::hir(expr.value) {
 +            bind!(self, pat, arg, body);
 +            out!(
 +                "if let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \
 +                = higher::ForLoop::hir({expr});"
 +            );
 +            self.pat(pat);
 +            self.expr(arg);
 +            self.expr(body);
 +            return;
 +        }
 +
 +        let kind = |kind| out!("if let ExprKind::{kind} = {expr}.kind;");
 +        macro_rules! kind {
 +            ($($t:tt)*) => (kind(format_args!($($t)*)));
 +        }
 +
 +        match expr.value.kind {
 +            ExprKind::Let(let_expr) => {
 +                bind!(self, let_expr);
 +                kind!("Let({let_expr})");
 +                self.pat(field!(let_expr.pat));
 +                // Does what ExprKind::Cast does, only adds a clause for the type
 +                // if it's a path
 +                if let Some(TyKind::Path(ref qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) {
 +                    bind!(self, qpath);
 +                    out!("if let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind;");
 +                    self.qpath(qpath);
 +                }
 +                self.expr(field!(let_expr.init));
 +            },
 +            ExprKind::Box(inner) => {
 +                bind!(self, inner);
 +                kind!("Box({inner})");
 +                self.expr(inner);
 +            },
 +            ExprKind::Array(elements) => {
 +                bind!(self, elements);
 +                kind!("Array({elements})");
 +                self.slice(elements, |e| self.expr(e));
 +            },
 +            ExprKind::Call(func, args) => {
 +                bind!(self, func, args);
 +                kind!("Call({func}, {args})");
 +                self.expr(func);
 +                self.slice(args, |e| self.expr(e));
 +            },
 +            ExprKind::MethodCall(method_name, args, _) => {
 +                bind!(self, method_name, args);
 +                kind!("MethodCall({method_name}, {args}, _)");
 +                self.ident(field!(method_name.ident));
 +                self.slice(args, |e| self.expr(e));
 +            },
 +            ExprKind::Tup(elements) => {
 +                bind!(self, elements);
 +                kind!("Tup({elements})");
 +                self.slice(elements, |e| self.expr(e));
 +            },
 +            ExprKind::Binary(op, left, right) => {
 +                bind!(self, op, left, right);
 +                kind!("Binary({op}, {left}, {right})");
 +                out!("if BinOpKind::{:?} == {op}.node;", op.value.node);
 +                self.expr(left);
 +                self.expr(right);
 +            },
 +            ExprKind::Unary(op, inner) => {
 +                bind!(self, inner);
 +                kind!("Unary(UnOp::{op:?}, {inner})");
 +                self.expr(inner);
 +            },
 +            ExprKind::Lit(ref lit) => {
 +                bind!(self, lit);
 +                kind!("Lit(ref {lit})");
 +                self.lit(lit);
 +            },
 +            ExprKind::Cast(expr, cast_ty) => {
 +                bind!(self, expr, cast_ty);
 +                kind!("Cast({expr}, {cast_ty})");
 +                if let TyKind::Path(ref qpath) = cast_ty.value.kind {
 +                    bind!(self, qpath);
 +                    out!("if let TyKind::Path(ref {qpath}) = {cast_ty}.kind;");
 +                    self.qpath(qpath);
 +                }
 +                self.expr(expr);
 +            },
 +            ExprKind::Type(expr, _ty) => {
 +                bind!(self, expr);
 +                kind!("Type({expr}, _)");
 +                self.expr(expr);
 +            },
 +            ExprKind::Loop(body, label, des, _) => {
 +                bind!(self, body);
 +                opt_bind!(self, label);
 +                kind!("Loop({body}, {label}, LoopSource::{des:?}, _)");
 +                self.block(body);
 +                label.if_some(|l| self.ident(field!(l.ident)));
 +            },
 +            ExprKind::If(cond, then, else_expr) => {
 +                bind!(self, cond, then);
 +                opt_bind!(self, else_expr);
 +                kind!("If({cond}, {then}, {else_expr})");
 +                self.expr(cond);
 +                self.expr(then);
 +                else_expr.if_some(|e| self.expr(e));
 +            },
 +            ExprKind::Match(scrutinee, arms, des) => {
 +                bind!(self, scrutinee, arms);
 +                kind!("Match({scrutinee}, {arms}, MatchSource::{des:?})");
 +                self.expr(scrutinee);
 +                self.slice(arms, |arm| self.arm(arm));
 +            },
 +            ExprKind::Closure(capture_by, fn_decl, body_id, _, movability) => {
 +                let movability = OptionPat::new(movability.map(|m| format!("Movability::{m:?}")));
 +
 +                let ret_ty = match fn_decl.output {
 +                    FnRetTy::DefaultReturn(_) => "FnRetTy::DefaultReturn(_)",
 +                    FnRetTy::Return(_) => "FnRetTy::Return(_ty)",
 +                };
 +
 +                bind!(self, fn_decl, body_id);
 +                kind!("Closure(CaptureBy::{capture_by:?}, {fn_decl}, {body_id}, _, {movability})");
 +                out!("if let {ret_ty} = {fn_decl}.output;");
 +                self.body(body_id);
 +            },
 +            ExprKind::Yield(sub, source) => {
 +                bind!(self, sub);
 +                kind!("Yield(sub, YieldSource::{source:?})");
 +                self.expr(sub);
 +            },
 +            ExprKind::Block(block, label) => {
 +                bind!(self, block);
 +                opt_bind!(self, label);
 +                kind!("Block({block}, {label})");
 +                self.block(block);
 +                label.if_some(|l| self.ident(field!(l.ident)));
 +            },
 +            ExprKind::Assign(target, value, _) => {
 +                bind!(self, target, value);
 +                kind!("Assign({target}, {value}, _span)");
 +                self.expr(target);
 +                self.expr(value);
 +            },
 +            ExprKind::AssignOp(op, target, value) => {
 +                bind!(self, op, target, value);
 +                kind!("AssignOp({op}, {target}, {value})");
 +                out!("if BinOpKind::{:?} == {op}.node;", op.value.node);
 +                self.expr(target);
 +                self.expr(value);
 +            },
 +            ExprKind::Field(object, field_name) => {
 +                bind!(self, object, field_name);
 +                kind!("Field({object}, {field_name})");
 +                self.ident(field_name);
 +                self.expr(object);
 +            },
 +            ExprKind::Index(object, index) => {
 +                bind!(self, object, index);
 +                kind!("Index({object}, {index})");
 +                self.expr(object);
 +                self.expr(index);
 +            },
 +            ExprKind::Path(ref qpath) => {
 +                bind!(self, qpath);
 +                kind!("Path(ref {qpath})");
 +                self.qpath(qpath);
 +            },
 +            ExprKind::AddrOf(kind, mutability, inner) => {
 +                bind!(self, inner);
 +                kind!("AddrOf(BorrowKind::{kind:?}, Mutability::{mutability:?}, {inner})");
 +                self.expr(inner);
 +            },
 +            ExprKind::Break(destination, value) => {
 +                bind!(self, destination);
 +                opt_bind!(self, value);
 +                kind!("Break({destination}, {value})");
 +                self.destination(destination);
 +                value.if_some(|e| self.expr(e));
 +            },
 +            ExprKind::Continue(destination) => {
 +                bind!(self, destination);
 +                kind!("Continue({destination})");
 +                self.destination(destination);
 +            },
 +            ExprKind::Ret(value) => {
 +                opt_bind!(self, value);
 +                kind!("Ret({value})");
 +                value.if_some(|e| self.expr(e));
 +            },
 +            ExprKind::InlineAsm(_) => {
 +                kind!("InlineAsm(_)");
 +                out!("// unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment");
 +            },
 +            ExprKind::Struct(qpath, fields, base) => {
 +                bind!(self, qpath, fields);
 +                opt_bind!(self, base);
 +                kind!("Struct({qpath}, {fields}, {base})");
 +                self.qpath(qpath);
 +                self.slice(fields, |field| {
 +                    self.ident(field!(field.ident));
 +                    self.expr(field!(field.expr));
 +                });
 +                base.if_some(|e| self.expr(e));
 +            },
 +            ExprKind::ConstBlock(_) => kind!("ConstBlock(_)"),
 +            ExprKind::Repeat(value, length) => {
 +                bind!(self, value, length);
 +                kind!("Repeat({value}, {length})");
 +                self.expr(value);
 +                match length.value {
 +                    ArrayLen::Infer(..) => out!("if let ArrayLen::Infer(..) = length;"),
 +                    ArrayLen::Body(anon_const) => {
 +                        bind!(self, anon_const);
 +                        out!("if let ArrayLen::Body({anon_const}) = {length};");
 +                        self.body(field!(anon_const.body));
 +                    },
 +                }
 +            },
 +            ExprKind::Err => kind!("Err"),
 +            ExprKind::DropTemps(expr) => {
 +                bind!(self, expr);
 +                kind!("DropTemps({expr})");
 +                self.expr(expr);
 +            },
 +        }
 +    }
 +
 +    fn block(&self, block: &Binding<&hir::Block<'_>>) {
 +        self.slice(field!(block.stmts), |stmt| self.stmt(stmt));
 +        self.option(field!(block.expr), "trailing_expr", |expr| {
 +            self.expr(expr);
 +        });
 +    }
 +
 +    fn body(&self, body_id: &Binding<hir::BodyId>) {
 +        let expr = &self.cx.tcx.hir().body(body_id.value).value;
 +        bind!(self, expr);
 +        out!("let {expr} = &cx.tcx.hir().body({body_id}).value;");
 +        self.expr(expr);
 +    }
 +
 +    fn pat(&self, pat: &Binding<&hir::Pat<'_>>) {
 +        let kind = |kind| out!("if let PatKind::{kind} = {pat}.kind;");
 +        macro_rules! kind {
 +            ($($t:tt)*) => (kind(format_args!($($t)*)));
 +        }
 +
 +        match pat.value.kind {
 +            PatKind::Wild => kind!("Wild"),
 +            PatKind::Binding(anno, .., name, sub) => {
 +                bind!(self, name);
 +                opt_bind!(self, sub);
 +                kind!("Binding(BindingAnnotation::{anno:?}, _, {name}, {sub})");
 +                self.ident(name);
 +                sub.if_some(|p| self.pat(p));
 +            },
 +            PatKind::Struct(ref qpath, fields, ignore) => {
 +                bind!(self, qpath, fields);
 +                kind!("Struct(ref {qpath}, {fields}, {ignore})");
 +                self.qpath(qpath);
 +                self.slice(fields, |field| {
 +                    self.ident(field!(field.ident));
 +                    self.pat(field!(field.pat));
 +                });
 +            },
 +            PatKind::Or(fields) => {
 +                bind!(self, fields);
 +                kind!("Or({fields})");
 +                self.slice(fields, |pat| self.pat(pat));
 +            },
 +            PatKind::TupleStruct(ref qpath, fields, skip_pos) => {
 +                bind!(self, qpath, fields);
 +                kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})");
 +                self.qpath(qpath);
 +                self.slice(fields, |pat| self.pat(pat));
 +            },
 +            PatKind::Path(ref qpath) => {
 +                bind!(self, qpath);
 +                kind!("Path(ref {qpath})");
 +                self.qpath(qpath);
 +            },
 +            PatKind::Tuple(fields, skip_pos) => {
 +                bind!(self, fields);
 +                kind!("Tuple({fields}, {skip_pos:?})");
 +                self.slice(fields, |field| self.pat(field));
 +            },
 +            PatKind::Box(pat) => {
 +                bind!(self, pat);
 +                kind!("Box({pat})");
 +                self.pat(pat);
 +            },
 +            PatKind::Ref(pat, muta) => {
 +                bind!(self, pat);
 +                kind!("Ref({pat}, Mutability::{muta:?})");
 +                self.pat(pat);
 +            },
 +            PatKind::Lit(lit_expr) => {
 +                bind!(self, lit_expr);
 +                kind!("Lit({lit_expr})");
 +                self.expr(lit_expr);
 +            },
 +            PatKind::Range(start, end, end_kind) => {
 +                opt_bind!(self, start, end);
 +                kind!("Range({start}, {end}, RangeEnd::{end_kind:?})");
 +                start.if_some(|e| self.expr(e));
 +                end.if_some(|e| self.expr(e));
 +            },
 +            PatKind::Slice(start, middle, end) => {
 +                bind!(self, start, end);
 +                opt_bind!(self, middle);
 +                kind!("Slice({start}, {middle}, {end})");
 +                middle.if_some(|p| self.pat(p));
 +                self.slice(start, |pat| self.pat(pat));
 +                self.slice(end, |pat| self.pat(pat));
 +            },
 +        }
 +    }
 +
 +    fn stmt(&self, stmt: &Binding<&hir::Stmt<'_>>) {
 +        let kind = |kind| out!("if let StmtKind::{kind} = {stmt}.kind;");
 +        macro_rules! kind {
 +            ($($t:tt)*) => (kind(format_args!($($t)*)));
 +        }
 +
 +        match stmt.value.kind {
 +            StmtKind::Local(local) => {
 +                bind!(self, local);
 +                kind!("Local({local})");
 +                self.option(field!(local.init), "init", |init| {
 +                    self.expr(init);
 +                });
 +                self.pat(field!(local.pat));
 +            },
 +            StmtKind::Item(_) => kind!("Item(item_id)"),
 +            StmtKind::Expr(e) => {
 +                bind!(self, e);
 +                kind!("Expr({e})");
 +                self.expr(e);
 +            },
 +            StmtKind::Semi(e) => {
 +                bind!(self, e);
 +                kind!("Semi({e})");
 +                self.expr(e);
 +            },
 +        }
 +    }
 +}
 +
 +fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
 +    let attrs = cx.tcx.hir().attrs(hir_id);
 +    get_attr(cx.sess(), attrs, "author").count() > 0
 +}
 +
 +fn path_to_string(path: &QPath<'_>) -> String {
 +    fn inner(s: &mut String, path: &QPath<'_>) {
 +        match *path {
 +            QPath::Resolved(_, path) => {
 +                for (i, segment) in path.segments.iter().enumerate() {
 +                    if i > 0 {
 +                        *s += ", ";
 +                    }
 +                    write!(s, "{:?}", segment.ident.as_str()).unwrap();
 +                }
 +            },
 +            QPath::TypeRelative(ty, segment) => match &ty.kind {
 +                hir::TyKind::Path(inner_path) => {
 +                    inner(s, inner_path);
 +                    *s += ", ";
 +                    write!(s, "{:?}", segment.ident.as_str()).unwrap();
 +                },
 +                other => write!(s, "/* unimplemented: {:?}*/", other).unwrap(),
 +            },
 +            QPath::LangItem(..) => panic!("path_to_string: called for lang item qpath"),
 +        }
 +    }
 +    let mut s = String::new();
 +    inner(&mut s, path);
 +    s
 +}
index 271c3a3dd181cef4db12b84a6db0f9a3fa42ebf5,0000000000000000000000000000000000000000..74b0168a1794be449f2c285effd37b27938a27ac
mode 100644,000000..100644
--- /dev/null
@@@ -1,371 -1,0 +1,377 @@@
 +//! Read configurations files.
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor};
 +use serde::Deserialize;
 +use std::error::Error;
 +use std::path::{Path, PathBuf};
 +use std::{env, fmt, fs, io};
 +
 +/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint.
 +#[derive(Clone, Debug, Deserialize)]
 +pub struct Rename {
 +    pub path: String,
 +    pub rename: String,
 +}
 +
 +/// A single disallowed method, used by the `DISALLOWED_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<String>,
 +}
 +
 +impl TryConf {
 +    fn from_error(error: impl Error) -> Self {
 +        Self {
 +            conf: Conf::default(),
 +            errors: vec![error.to_string()],
 +        }
 +    }
 +}
 +
 +macro_rules! define_Conf {
 +    ($(
 +        $(#[doc = $doc:literal])+
 +        $(#[conf_deprecated($dep:literal)])?
 +        ($name:ident: $ty:ty = $default:expr),
 +    )*) => {
 +        /// Clippy lint configuration
 +        pub struct Conf {
 +            $($(#[doc = $doc])+ pub $name: $ty,)*
 +        }
 +
 +        mod defaults {
 +            $(pub fn $name() -> $ty { $default })*
 +        }
 +
 +        impl Default for Conf {
 +            fn default() -> Self {
 +                Self { $($name: defaults::$name(),)* }
 +            }
 +        }
 +
 +        impl<'de> Deserialize<'de> for TryConf {
 +            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
 +                deserializer.deserialize_map(ConfVisitor)
 +            }
 +        }
 +
 +        #[derive(Deserialize)]
 +        #[serde(field_identifier, rename_all = "kebab-case")]
 +        #[allow(non_camel_case_types)]
 +        enum Field { $($name,)* third_party, }
 +
 +        struct ConfVisitor;
 +
 +        impl<'de> Visitor<'de> for ConfVisitor {
 +            type Value = TryConf;
 +
 +            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
 +                formatter.write_str("Conf")
 +            }
 +
 +            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapAccess<'de> {
 +                let mut errors = Vec::new();
 +                $(let mut $name = None;)*
 +                // could get `Field` here directly, but get `str` first for diagnostics
 +                while let Some(name) = map.next_key::<&str>()? {
 +                    match Field::deserialize(name.into_deserializer())? {
 +                        $(Field::$name => {
 +                            $(errors.push(format!("deprecated field `{}`. {}", name, $dep));)?
 +                            match map.next_value() {
 +                                Err(e) => errors.push(e.to_string()),
 +                                Ok(value) => match $name {
 +                                    Some(_) => errors.push(format!("duplicate field `{}`", name)),
 +                                    None => $name = Some(value),
 +                                }
 +                            }
 +                        })*
 +                        // white-listed; ignore
 +                        Field::third_party => drop(map.next_value::<IgnoredAny>())
 +                    }
 +                }
 +                let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
 +                Ok(TryConf { conf, errors })
 +            }
 +        }
 +
 +        #[cfg(feature = "internal")]
 +        pub mod metadata {
 +            use crate::utils::internal_lints::metadata_collector::ClippyConfiguration;
 +
 +            macro_rules! wrap_option {
 +                () => (None);
 +                ($x:literal) => (Some($x));
 +            }
 +
 +            pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
 +                vec![
 +                    $(
 +                        {
 +                            let deprecation_reason = wrap_option!($($dep)?);
 +
 +                            ClippyConfiguration::new(
 +                                stringify!($name),
 +                                stringify!($ty),
 +                                format!("{:?}", super::defaults::$name()),
 +                                concat!($($doc, '\n',)*),
 +                                deprecation_reason,
 +                            )
 +                        },
 +                    )+
 +                ]
 +            }
 +        }
 +    };
 +}
 +
 +define_Conf! {
 +    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
 +    ///
 +    /// Suppress lints whenever the suggested change would cause breakage for other crates.
 +    (avoid_breaking_exported_api: bool = true),
 +    /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED.
 +    ///
 +    /// The minimum rust version that the project supports
 +    (msrv: Option<String> = None),
 +    /// Lint: BLACKLISTED_NAME.
 +    ///
 +    /// The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
 +    (blacklisted_names: Vec<String> = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
 +    /// Lint: COGNITIVE_COMPLEXITY.
 +    ///
 +    /// The maximum cognitive complexity a function can have
 +    (cognitive_complexity_threshold: u64 = 25),
 +    /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY.
 +    ///
 +    /// Use the Cognitive Complexity lint instead.
 +    #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")]
 +    (cyclomatic_complexity_threshold: Option<u64> = None),
 +    /// Lint: DOC_MARKDOWN.
 +    ///
 +    /// The list of words this lint should not consider as identifiers needing ticks
 +    (doc_valid_idents: Vec<String> = [
 +        "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
 +        "DirectX",
 +        "ECMAScript",
 +        "GPLv2", "GPLv3",
 +        "GitHub", "GitLab",
 +        "IPv4", "IPv6",
 +        "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
 +        "NaN", "NaNs",
 +        "OAuth", "GraphQL",
 +        "OCaml",
 +        "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS",
 +        "WebGL",
 +        "TensorFlow",
 +        "TrueType",
 +        "iOS", "macOS", "FreeBSD",
 +        "TeX", "LaTeX", "BibTeX", "BibLaTeX",
 +        "MinGW",
 +        "CamelCase",
 +    ].iter().map(ToString::to_string).collect()),
 +    /// Lint: TOO_MANY_ARGUMENTS.
 +    ///
 +    /// The maximum number of argument a function or method can have
 +    (too_many_arguments_threshold: u64 = 7),
 +    /// Lint: TYPE_COMPLEXITY.
 +    ///
 +    /// The maximum complexity a type can have
 +    (type_complexity_threshold: u64 = 250),
 +    /// Lint: MANY_SINGLE_CHAR_NAMES.
 +    ///
 +    /// The maximum number of single char bindings a scope may have
 +    (single_char_binding_names_threshold: u64 = 4),
 +    /// Lint: BOXED_LOCAL, USELESS_VEC.
 +    ///
 +    /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
 +    (too_large_for_stack: u64 = 200),
 +    /// Lint: ENUM_VARIANT_NAMES.
 +    ///
 +    /// The minimum number of enum variants for the lints about variant names to trigger
 +    (enum_variant_name_threshold: u64 = 3),
 +    /// Lint: LARGE_ENUM_VARIANT.
 +    ///
 +    /// The maximum size of an enum's variant to avoid box suggestion
 +    (enum_variant_size_threshold: u64 = 200),
 +    /// Lint: VERBOSE_BIT_MASK.
 +    ///
 +    /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
 +    (verbose_bit_mask_threshold: u64 = 1),
 +    /// Lint: DECIMAL_LITERAL_REPRESENTATION.
 +    ///
 +    /// The lower bound for linting decimal literals
 +    (literal_representation_threshold: u64 = 16384),
 +    /// Lint: TRIVIALLY_COPY_PASS_BY_REF.
 +    ///
 +    /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
 +    (trivial_copy_size_limit: Option<u64> = None),
 +    /// Lint: LARGE_TYPE_PASS_BY_MOVE.
 +    ///
 +    /// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
 +    (pass_by_value_size_limit: u64 = 256),
 +    /// Lint: TOO_MANY_LINES.
 +    ///
 +    /// The maximum number of lines a function or method can have
 +    (too_many_lines_threshold: u64 = 100),
 +    /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS.
 +    ///
 +    /// The maximum allowed size for arrays on the stack
 +    (array_size_threshold: u64 = 512_000),
 +    /// Lint: VEC_BOX.
 +    ///
 +    /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed
 +    (vec_box_size_threshold: u64 = 4096),
 +    /// Lint: TYPE_REPETITION_IN_BOUNDS.
 +    ///
 +    /// The maximum number of bounds a trait can have to be linted
 +    (max_trait_bounds: u64 = 3),
 +    /// Lint: STRUCT_EXCESSIVE_BOOLS.
 +    ///
 +    /// The maximum number of bool fields a struct can have
 +    (max_struct_bools: u64 = 3),
 +    /// Lint: FN_PARAMS_EXCESSIVE_BOOLS.
 +    ///
 +    /// The maximum number of bool parameters a function can have
 +    (max_fn_params_bools: u64 = 3),
 +    /// Lint: WILDCARD_IMPORTS.
 +    ///
 +    /// Whether to allow certain wildcard imports (prelude, super in tests).
 +    (warn_on_all_wildcard_imports: bool = false),
 +    /// Lint: DISALLOWED_METHODS.
 +    ///
 +    /// The list of disallowed methods, written as fully qualified paths.
 +    (disallowed_methods: Vec<crate::utils::conf::DisallowedMethod> = Vec::new()),
 +    /// Lint: DISALLOWED_TYPES.
 +    ///
 +    /// The list of disallowed types, written as fully qualified paths.
 +    (disallowed_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()),
 +    /// Lint: UNREADABLE_LITERAL.
 +    ///
 +    /// Should the fraction of a decimal be linted to include separators.
 +    (unreadable_literal_lint_fractions: bool = true),
 +    /// Lint: UPPER_CASE_ACRONYMS.
 +    ///
 +    /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other
 +    (upper_case_acronyms_aggressive: bool = false),
 +    /// Lint: _CARGO_COMMON_METADATA.
 +    ///
 +    /// For internal testing only, ignores the current `publish` settings in the Cargo manifest.
 +    (cargo_ignore_publish: bool = false),
 +    /// Lint: NONSTANDARD_MACRO_BRACES.
 +    ///
 +    /// Enforce the named macros always use the braces specified.
 +    ///
 +    /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
 +    /// is could be used with a full path two `MacroMatcher`s have to be added one with the full path
 +    /// `crate_name::macro_name` and one with just the macro name.
 +    (standard_macro_braces: Vec<crate::nonstandard_macro_braces::MacroMatcher> = Vec::new()),
 +    /// Lint: MISSING_ENFORCED_IMPORT_RENAMES.
 +    ///
 +    /// The list of imports to always rename, a fully qualified path followed by the rename.
 +    (enforced_import_renames: Vec<crate::utils::conf::Rename> = Vec::new()),
 +    /// Lint: DISALLOWED_SCRIPT_IDENTS.
 +    ///
 +    /// The list of unicode scripts allowed to be used in the scope.
 +    (allowed_scripts: Vec<String> = ["Latin"].iter().map(ToString::to_string).collect()),
 +    /// Lint: NON_SEND_FIELDS_IN_SEND_TY.
 +    ///
 +    /// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
 +    (enable_raw_pointer_heuristic_for_send: bool = true),
 +    /// Lint: INDEX_REFUTABLE_SLICE.
 +    ///
 +    /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in
 +    /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed.
 +    /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
 +    (max_suggested_slice_pattern_length: u64 = 3),
++    /// Lint: AWAIT_HOLDING_INVALID_TYPE
++    (await_holding_invalid_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()),
++    /// Lint: LARGE_INCLUDE_FILE.
++    ///
++    /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
++    (max_include_file_size: u64 = 1_000_000),
 +}
 +
 +/// Search for the configuration file.
 +pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
 +    /// Possible filename to search for.
 +    const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
 +
 +    // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
 +    // If neither of those exist, use ".".
 +    let mut current = env::var_os("CLIPPY_CONF_DIR")
 +        .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
 +        .map_or_else(|| PathBuf::from("."), PathBuf::from);
 +
 +    let mut found_config: Option<PathBuf> = None;
 +
 +    loop {
 +        for config_file_name in &CONFIG_FILE_NAMES {
 +            if let Ok(config_file) = current.join(config_file_name).canonicalize() {
 +                match fs::metadata(&config_file) {
 +                    Err(e) if e.kind() == io::ErrorKind::NotFound => {},
 +                    Err(e) => return Err(e),
 +                    Ok(md) if md.is_dir() => {},
 +                    Ok(_) => {
 +                        // warn if we happen to find two config files #8323
 +                        if let Some(ref found_config_) = found_config {
 +                            eprintln!(
 +                                "Using config file `{}`\nWarning: `{}` will be ignored.",
 +                                found_config_.display(),
 +                                config_file.display(),
 +                            );
 +                        } else {
 +                            found_config = Some(config_file);
 +                        }
 +                    },
 +                }
 +            }
 +        }
 +
 +        if found_config.is_some() {
 +            return Ok(found_config);
 +        }
 +
 +        // If the current directory has no parent, we're done searching.
 +        if !current.pop() {
 +            return Ok(None);
 +        }
 +    }
 +}
 +
 +/// Read the `toml` configuration file.
 +///
 +/// In case of error, the function tries to continue as much as possible.
 +pub fn read(path: &Path) -> TryConf {
 +    let content = match fs::read_to_string(path) {
 +        Err(e) => return TryConf::from_error(e),
 +        Ok(content) => content,
 +    };
 +    toml::from_str(&content).unwrap_or_else(TryConf::from_error)
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..01efc527a8c3abaf5e49f574769657a2312d35d8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++use clippy_utils::get_attr;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// It formats the attached node with `{:#?}` and writes the result to the
++    /// standard output. This is intended for debugging.
++    ///
++    /// ### Examples
++    /// ```rs
++    /// #[clippy::dump]
++    /// use std::mem;
++    ///
++    /// #[clippy::dump]
++    /// fn foo(input: u32) -> u64 {
++    ///     input as u64
++    /// }
++    /// ```
++    pub DUMP_HIR,
++    internal_warn,
++    "helper to dump info about code"
++}
++
++declare_lint_pass!(DumpHir => [DUMP_HIR]);
++
++impl<'tcx> LateLintPass<'tcx> for DumpHir {
++    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
++        if has_attr(cx, item.hir_id()) {
++            println!("{item:#?}");
++        }
++    }
++
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if has_attr(cx, expr.hir_id) {
++            println!("{expr:#?}");
++        }
++    }
++
++    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
++        match stmt.kind {
++            hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return,
++            _ => {},
++        }
++        if has_attr(cx, stmt.hir_id) {
++            println!("{stmt:#?}");
++        }
++    }
++}
++
++fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
++    let attrs = cx.tcx.hir().attrs(hir_id);
++    get_attr(cx.sess(), attrs, "dump").count() > 0
++}
index 25d74b8c49939da8a643e60a2da1e4b264440d91,0000000000000000000000000000000000000000..0e8f40e92101a053ab1d5210b761838ca2447b87
mode 100644,000000..100644
--- /dev/null
@@@ -1,1377 -1,0 +1,1377 @@@
-     /// It's faster use symbols directly intead of strings.
 +use clippy_utils::consts::{constant_simple, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::macros::root_macro_call_first_node;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::match_type;
 +use clippy_utils::{
 +    def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path,
 +    method_calls, paths, peel_blocks_with_stmt, SpanlessEq,
 +};
 +use if_chain::if_chain;
 +use rustc_ast as ast;
 +use rustc_ast::ast::{Crate, ItemKind, LitKind, ModKind, NodeId};
 +use rustc_ast::visit::FnKind;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::hir_id::CRATE_HIR_ID;
 +use rustc_hir::intravisit::Visitor;
 +use rustc_hir::{
 +    BinOpKind, Block, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty, TyKind,
 +    UnOp,
 +};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::mir::interpret::ConstValue;
 +use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, FloatTy};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Spanned;
 +use rustc_span::symbol::Symbol;
 +use rustc_span::{sym, BytePos, Span};
 +use rustc_typeck::hir_ty_to_ty;
 +
 +use std::borrow::{Borrow, Cow};
 +
 +#[cfg(feature = "internal")]
 +pub mod metadata_collector;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for various things we like to keep tidy in clippy.
 +    ///
 +    /// ### Why is this bad?
 +    /// We like to pretend we're an example of tidy code.
 +    ///
 +    /// ### Example
 +    /// Wrong ordering of the util::paths constants.
 +    pub CLIPPY_LINTS_INTERNAL,
 +    internal,
 +    "various things that will negatively affect your clippy experience"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Ensures every lint is associated to a `LintPass`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The compiler only knows lints via a `LintPass`. Without
 +    /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
 +    /// know the name of the lint.
 +    ///
 +    /// ### Known problems
 +    /// Only checks for lints associated using the
 +    /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// declare_lint! { pub LINT_1, ... }
 +    /// declare_lint! { pub LINT_2, ... }
 +    /// declare_lint! { pub FORGOTTEN_LINT, ... }
 +    /// // ...
 +    /// declare_lint_pass!(Pass => [LINT_1, LINT_2]);
 +    /// // missing FORGOTTEN_LINT
 +    /// ```
 +    pub LINT_WITHOUT_LINT_PASS,
 +    internal,
 +    "declaring a lint without associating it in a LintPass"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*`
 +    /// variant of the function.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `utils::*` variants also add a link to the Clippy documentation to the
 +    /// warning/error messages.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// cx.span_lint(LINT_NAME, "message");
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// utils::span_lint(cx, LINT_NAME, "message");
 +    /// ```
 +    pub COMPILER_LINT_FUNCTIONS,
 +    internal,
 +    "usage of the lint functions of the compiler instead of the utils::* variant"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `cx.outer().expn_data()` and suggests to use
 +    /// the `cx.outer_expn_data()`
 +    ///
 +    /// ### Why is this bad?
 +    /// `cx.outer_expn_data()` is faster and more concise.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// expr.span.ctxt().outer().expn_data()
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// expr.span.ctxt().outer_expn_data()
 +    /// ```
 +    pub OUTER_EXPN_EXPN_DATA,
 +    internal,
 +    "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Not an actual lint. This lint is only meant for testing our customized internal compiler
 +    /// error message by calling `panic`.
 +    ///
 +    /// ### Why is this bad?
 +    /// ICE in large quantities can damage your teeth
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// 🍦🍦🍦🍦🍦
 +    /// ```
 +    pub PRODUCE_ICE,
 +    internal,
 +    "this message should not appear anywhere as we ICE before and don't emit the lint"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for cases of an auto-generated lint without an updated description,
 +    /// i.e. `default lint description`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Indicates that the lint is not finished.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// declare_lint! { pub COOL_LINT, nursery, "default lint description" }
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" }
 +    /// ```
 +    pub DEFAULT_LINT,
 +    internal,
 +    "found 'default lint description' in a lint declaration"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Lints `span_lint_and_then` function calls, where the
 +    /// closure argument has only one statement and that statement is a method
 +    /// call to `span_suggestion`, `span_help`, `span_note` (using the same
 +    /// span), `help` or `note`.
 +    ///
 +    /// These usages of `span_lint_and_then` should be replaced with one of the
 +    /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or
 +    /// `span_lint_and_note`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the wrapper `span_lint_and_*` functions, is more
 +    /// convenient, readable and less error prone.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.span_suggestion(
 +    ///         expr.span,
 +    ///         help_msg,
 +    ///         sugg.to_string(),
 +    ///         Applicability::MachineApplicable,
 +    ///     );
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.span_help(expr.span, help_msg);
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.help(help_msg);
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.span_note(expr.span, note_msg);
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.note(note_msg);
 +    /// });
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// span_lint_and_sugg(
 +    ///     cx,
 +    ///     TEST_LINT,
 +    ///     expr.span,
 +    ///     lint_msg,
 +    ///     help_msg,
 +    ///     sugg.to_string(),
 +    ///     Applicability::MachineApplicable,
 +    /// );
 +    /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg);
 +    /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg);
 +    /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg);
 +    /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg);
 +    /// ```
 +    pub COLLAPSIBLE_SPAN_LINT_CALLS,
 +    internal,
 +    "found collapsible `span_lint_and_then` calls"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `utils::match_type()` on a type diagnostic item
 +    /// and suggests to use `utils::is_type_diagnostic_item()` instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// `utils::is_type_diagnostic_item()` does not require hardcoded paths.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// utils::match_type(cx, ty, &paths::VEC)
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// utils::is_type_diagnostic_item(cx, ty, sym::Vec)
 +    /// ```
 +    pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +    internal,
 +    "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks the paths module for invalid paths.
 +    ///
 +    /// ### Why is this bad?
 +    /// It indicates a bug in the code.
 +    ///
 +    /// ### Example
 +    /// None.
 +    pub INVALID_PATHS,
 +    internal,
 +    "invalid path"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for interning symbols that have already been pre-interned and defined as constants.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's faster and easier to use the symbol constant.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// let _ = sym!(f32);
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// let _ = sym::f32;
 +    /// ```
 +    pub INTERNING_DEFINED_SYMBOL,
 +    internal,
 +    "interning a symbol that is pre-interned and defined as a constant"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary conversion from Symbol to a string.
 +    ///
 +    /// ### Why is this bad?
-         "this call is collspible",
++    /// It's faster use symbols directly instead of strings.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// symbol.as_str() == "clippy";
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// symbol == sym::clippy;
 +    /// ```
 +    pub UNNECESSARY_SYMBOL_STR,
 +    internal,
 +    "unnecessary conversion between Symbol and string"
 +}
 +
 +declare_clippy_lint! {
 +    /// Finds unidiomatic usage of `if_chain!`
 +    pub IF_CHAIN_STYLE,
 +    internal,
 +    "non-idiomatic `if_chain!` usage"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for invalid `clippy::version` attributes.
 +    ///
 +    /// Valid values are:
 +    /// * "pre 1.29.0"
 +    /// * any valid semantic version
 +    pub INVALID_CLIPPY_VERSION_ATTRIBUTE,
 +    internal,
 +    "found an invalid `clippy::version` attribute"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for declared clippy lints without the `clippy::version` attribute.
 +    ///
 +    pub MISSING_CLIPPY_VERSION_ATTRIBUTE,
 +    internal,
 +    "found clippy lint without `clippy::version` attribute"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV.
 +    ///
 +    pub MISSING_MSRV_ATTR_IMPL,
 +    internal,
 +    "checking if all necessary steps were taken when adding a MSRV to a lint"
 +}
 +
 +declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
 +
 +impl EarlyLintPass for ClippyLintsInternal {
 +    fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
 +        if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") {
 +            if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind {
 +                if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") {
 +                    if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind {
 +                        let mut last_name: Option<&str> = None;
 +                        for item in items {
 +                            let name = item.ident.as_str();
 +                            if let Some(last_name) = last_name {
 +                                if *last_name > *name {
 +                                    span_lint(
 +                                        cx,
 +                                        CLIPPY_LINTS_INTERNAL,
 +                                        item.span,
 +                                        "this constant should be before the previous constant due to lexical \
 +                                         ordering",
 +                                    );
 +                                }
 +                            }
 +                            last_name = Some(name);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Debug, Default)]
 +pub struct LintWithoutLintPass {
 +    declared_lints: FxHashMap<Symbol, Span>,
 +    registered_lints: FxHashSet<Symbol>,
 +}
 +
 +impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) {
 +            return;
 +        }
 +
 +        if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
 +            if is_lint_ref_type(cx, ty) {
 +                check_invalid_clippy_version_attribute(cx, item);
 +
 +                let expr = &cx.tcx.hir().body(body_id).value;
 +                if_chain! {
 +                    if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind;
 +                    if let ExprKind::Struct(_, fields, _) = inner_exp.kind;
 +                    let field = fields
 +                        .iter()
 +                        .find(|f| f.ident.as_str() == "desc")
 +                        .expect("lints must have a description field");
 +                    if let ExprKind::Lit(Spanned {
 +                        node: LitKind::Str(ref sym, _),
 +                        ..
 +                    }) = field.expr.kind;
 +                    if sym.as_str() == "default lint description";
 +
 +                    then {
 +                        span_lint(
 +                            cx,
 +                            DEFAULT_LINT,
 +                            item.span,
 +                            &format!("the lint `{}` has the default lint description", item.ident.name),
 +                        );
 +                    }
 +                }
 +                self.declared_lints.insert(item.ident.name, item.span);
 +            }
 +        } else if let Some(macro_call) = root_macro_call_first_node(cx, item) {
 +            if !matches!(
 +                &*cx.tcx.item_name(macro_call.def_id).as_str(),
 +                "impl_lint_pass" | "declare_lint_pass"
 +            ) {
 +                return;
 +            }
 +            if let hir::ItemKind::Impl(hir::Impl {
 +                of_trait: None,
 +                items: impl_item_refs,
 +                ..
 +            }) = item.kind
 +            {
 +                let mut collector = LintCollector {
 +                    output: &mut self.registered_lints,
 +                    cx,
 +                };
 +                let body_id = cx.tcx.hir().body_owned_by(
 +                    impl_item_refs
 +                        .iter()
 +                        .find(|iiref| iiref.ident.as_str() == "get_lints")
 +                        .expect("LintPass needs to implement get_lints")
 +                        .id
 +                        .hir_id(),
 +                );
 +                collector.visit_expr(&cx.tcx.hir().body(body_id).value);
 +            }
 +        }
 +    }
 +
 +    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
 +        if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) {
 +            return;
 +        }
 +
 +        for (lint_name, &lint_span) in &self.declared_lints {
 +            // When using the `declare_tool_lint!` macro, the original `lint_span`'s
 +            // file points to "<rustc macros>".
 +            // `compiletest-rs` thinks that's an error in a different file and
 +            // just ignores it. This causes the test in compile-fail/lint_pass
 +            // not able to capture the error.
 +            // Therefore, we need to climb the macro expansion tree and find the
 +            // actual span that invoked `declare_tool_lint!`:
 +            let lint_span = lint_span.ctxt().outer_expn_data().call_site;
 +
 +            if !self.registered_lints.contains(lint_name) {
 +                span_lint(
 +                    cx,
 +                    LINT_WITHOUT_LINT_PASS,
 +                    lint_span,
 +                    &format!("the lint `{}` is not added to any `LintPass`", lint_name),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool {
 +    if let TyKind::Rptr(
 +        _,
 +        MutTy {
 +            ty: inner,
 +            mutbl: Mutability::Not,
 +        },
 +    ) = ty.kind
 +    {
 +        if let TyKind::Path(ref path) = inner.kind {
 +            if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) {
 +                return match_def_path(cx, def_id, &paths::LINT);
 +            }
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
 +    if let Some(value) = extract_clippy_version_value(cx, item) {
 +        // The `sym!` macro doesn't work as it only expects a single token.
 +        // It's better to keep it this way and have a direct `Symbol::intern` call here.
 +        if value == Symbol::intern("pre 1.29.0") {
 +            return;
 +        }
 +
 +        if RustcVersion::parse(&*value.as_str()).is_err() {
 +            span_lint_and_help(
 +                cx,
 +                INVALID_CLIPPY_VERSION_ATTRIBUTE,
 +                item.span,
 +                "this item has an invalid `clippy::version` attribute",
 +                None,
 +                "please use a valid sematic version, see `doc/adding_lints.md`",
 +            );
 +        }
 +    } else {
 +        span_lint_and_help(
 +            cx,
 +            MISSING_CLIPPY_VERSION_ATTRIBUTE,
 +            item.span,
 +            "this lint is missing the `clippy::version` attribute or version value",
 +            None,
 +            "please use a `clippy::version` attribute, see `doc/adding_lints.md`",
 +        );
 +    }
 +}
 +
 +/// This function extracts the version value of a `clippy::version` attribute if the given value has
 +/// one
 +fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> {
 +    let attrs = cx.tcx.hir().attrs(item.hir_id());
 +    attrs.iter().find_map(|attr| {
 +        if_chain! {
 +            // Identify attribute
 +            if let ast::AttrKind::Normal(ref attr_kind, _) = &attr.kind;
 +            if let [tool_name, attr_name] = &attr_kind.path.segments[..];
 +            if tool_name.ident.name == sym::clippy;
 +            if attr_name.ident.name == sym::version;
 +            if let Some(version) = attr.value_str();
 +            then {
 +                Some(version)
 +            } else {
 +                None
 +            }
 +        }
 +    })
 +}
 +
 +struct LintCollector<'a, 'tcx> {
 +    output: &'a mut FxHashSet<Symbol>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
 +        if path.segments.len() == 1 {
 +            self.output.insert(path.segments[0].ident.name);
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
 +
 +#[derive(Clone, Default)]
 +pub struct CompilerLintFunctions {
 +    map: FxHashMap<&'static str, &'static str>,
 +}
 +
 +impl CompilerLintFunctions {
 +    #[must_use]
 +    pub fn new() -> Self {
 +        let mut map = FxHashMap::default();
 +        map.insert("span_lint", "utils::span_lint");
 +        map.insert("struct_span_lint", "utils::span_lint");
 +        map.insert("lint", "utils::span_lint");
 +        map.insert("span_lint_note", "utils::span_lint_and_note");
 +        map.insert("span_lint_help", "utils::span_lint_and_help");
 +        Self { map }
 +    }
 +}
 +
 +impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
 +            let fn_name = path.ident;
 +            if let Some(sugg) = self.map.get(&*fn_name.as_str());
 +            let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
 +            if match_type(cx, ty, &paths::EARLY_CONTEXT)
 +                || match_type(cx, ty, &paths::LATE_CONTEXT);
 +            then {
 +                span_lint_and_help(
 +                    cx,
 +                    COMPILER_LINT_FUNCTIONS,
 +                    path.ident.span,
 +                    "usage of a compiler lint function",
 +                    None,
 +                    &format!("please use the Clippy variant of this function: `{}`", sugg),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
 +
 +impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) {
 +            return;
 +        }
 +
 +        let (method_names, arg_lists, spans) = method_calls(expr, 2);
 +        let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect();
 +        if_chain! {
 +            if let ["expn_data", "outer_expn"] = method_names.as_slice();
 +            let args = arg_lists[1];
 +            if args.len() == 1;
 +            let self_arg = &args[0];
 +            let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
 +            if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    OUTER_EXPN_EXPN_DATA,
 +                    spans[1].with_hi(expr.span.hi()),
 +                    "usage of `outer_expn().expn_data()`",
 +                    "try",
 +                    "outer_expn_data()".to_string(),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +declare_lint_pass!(ProduceIce => [PRODUCE_ICE]);
 +
 +impl EarlyLintPass for ProduceIce {
 +    fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
 +        assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?");
 +    }
 +}
 +
 +fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
 +    match fn_kind {
 +        FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy",
 +        FnKind::Closure(..) => false,
 +    }
 +}
 +
 +declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::Call(func, and_then_args) = expr.kind;
 +            if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
 +            if and_then_args.len() == 5;
 +            if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind;
 +            let body = cx.tcx.hir().body(*body_id);
 +            let only_expr = peel_blocks_with_stmt(&body.value);
 +            if let ExprKind::MethodCall(ps, span_call_args, _) = &only_expr.kind;
 +            then {
 +                let and_then_snippets = get_and_then_snippets(cx, and_then_args);
 +                let mut sle = SpanlessEq::new(cx).deny_side_effects();
 +                match &*ps.ident.as_str() {
 +                    "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
 +                        suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args));
 +                    },
 +                    "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
 +                        let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
 +                        suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
 +                    },
 +                    "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
 +                        let note_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
 +                        suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
 +                    },
 +                    "help" => {
 +                        let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
 +                        suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
 +                    }
 +                    "note" => {
 +                        let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
 +                        suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false);
 +                    }
 +                    _  => (),
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +struct AndThenSnippets<'a> {
 +    cx: Cow<'a, str>,
 +    lint: Cow<'a, str>,
 +    span: Cow<'a, str>,
 +    msg: Cow<'a, str>,
 +}
 +
 +fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> {
 +    let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx");
 +    let lint_snippet = snippet(cx, and_then_snippets[1].span, "..");
 +    let span_snippet = snippet(cx, and_then_snippets[2].span, "span");
 +    let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#);
 +
 +    AndThenSnippets {
 +        cx: cx_snippet,
 +        lint: lint_snippet,
 +        span: span_snippet,
 +        msg: msg_snippet,
 +    }
 +}
 +
 +struct SpanSuggestionSnippets<'a> {
 +    help: Cow<'a, str>,
 +    sugg: Cow<'a, str>,
 +    applicability: Cow<'a, str>,
 +}
 +
 +fn span_suggestion_snippets<'a, 'hir>(
 +    cx: &LateContext<'_>,
 +    span_call_args: &'hir [Expr<'hir>],
 +) -> SpanSuggestionSnippets<'a> {
 +    let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
 +    let sugg_snippet = snippet(cx, span_call_args[3].span, "..");
 +    let applicability_snippet = snippet(cx, span_call_args[4].span, "Applicability::MachineApplicable");
 +
 +    SpanSuggestionSnippets {
 +        help: help_snippet,
 +        sugg: sugg_snippet,
 +        applicability: applicability_snippet,
 +    }
 +}
 +
 +fn suggest_suggestion(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    span_suggestion_snippets: &SpanSuggestionSnippets<'_>,
 +) {
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
 +        "this call is collapsible",
 +        "collapse into",
 +        format!(
 +            "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})",
 +            and_then_snippets.cx,
 +            and_then_snippets.lint,
 +            and_then_snippets.span,
 +            and_then_snippets.msg,
 +            span_suggestion_snippets.help,
 +            span_suggestion_snippets.sugg,
 +            span_suggestion_snippets.applicability
 +        ),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn suggest_help(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    help: &str,
 +    with_span: bool,
 +) {
 +    let option_span = if with_span {
 +        format!("Some({})", and_then_snippets.span)
 +    } else {
 +        "None".to_string()
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
 +        "this call is collapsible",
 +        "collapse into",
 +        format!(
 +            "span_lint_and_help({}, {}, {}, {}, {}, {})",
 +            and_then_snippets.cx,
 +            and_then_snippets.lint,
 +            and_then_snippets.span,
 +            and_then_snippets.msg,
 +            &option_span,
 +            help
 +        ),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn suggest_note(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    note: &str,
 +    with_span: bool,
 +) {
 +    let note_span = if with_span {
 +        format!("Some({})", and_then_snippets.span)
 +    } else {
 +        "None".to_string()
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
++        "this call is 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 ca03b8010dd821c2c68da237efae631c279e1711,0000000000000000000000000000000000000000..8c1910b3b2af8d37aab0874ad8ac0b6456fc5ab5
mode 100644,000000..100644
--- /dev/null
@@@ -1,925 -1,0 +1,925 @@@
- const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "internal_metadata_collector"];
 +//! 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::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type};
 +
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
 +use clippy_utils::{last_path_segment, match_def_path, match_function_call, match_path, paths};
 +use if_chain::if_chain;
 +use rustc_ast as ast;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::{
 +    self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath,
 +};
 +use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
 +use rustc_middle::hir::nested_filter;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::Ident;
 +use rustc_span::{sym, Loc, Span, Symbol};
 +use serde::{ser::SerializeStruct, Serialize, Serializer};
 +use std::collections::BinaryHeap;
 +use std::fmt;
 +use std::fs::{self, OpenOptions};
 +use std::io::prelude::*;
 +use std::path::Path;
 +
 +/// This is the output file of the lint collector.
 +const OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
 +/// These lints are excluded from the export.
- const VERION_DEFAULT_STR: &str = "Unknown";
++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"
 +    };
 +}
 +
 +const LINT_EMISSION_FUNCTIONS: [&[&str]; 8] = [
 +    &["clippy_utils", "diagnostics", "span_lint"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_help"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_note"],
 +    &["clippy_utils", "diagnostics", "span_lint_hir"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_sugg"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_then"],
 +    &["clippy_utils", "diagnostics", "span_lint_hir_and_then"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_sugg_for_edges"],
 +];
 +const SUGGESTION_DIAGNOSTIC_BUILDER_METHODS: [(&str, bool); 9] = [
 +    ("span_suggestion", false),
 +    ("span_suggestion_short", false),
 +    ("span_suggestion_verbose", false),
 +    ("span_suggestion_hidden", false),
 +    ("tool_only_span_suggestion", false),
 +    ("multipart_suggestion", true),
 +    ("multipart_suggestions", true),
 +    ("tool_only_multipart_suggestion", true),
 +    ("span_suggestions", true),
 +];
 +const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [
 +    &["clippy_utils", "diagnostics", "multispan_sugg"],
 +    &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"],
 +];
 +const DEPRECATED_LINT_TYPE: [&str; 3] = ["clippy_lints", "deprecated_lints", "ClippyDeprecatedLint"];
 +
 +/// The index of the applicability name of `paths::APPLICABILITY_VALUES`
 +const APPLICABILITY_NAME_INDEX: usize = 2;
 +/// This applicability will be set for unresolved applicability values.
 +const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
 +/// The version that will be displayed if none has been defined
-         || VERION_DEFAULT_STR.to_string(),
++const VERSION_DEFAULT_STR: &str = "Unknown";
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Collects metadata about clippy lints for the website.
 +    ///
 +    /// This lint will be used to report problems of syntax parsing. You should hopefully never
 +    /// see this but never say never I guess ^^
 +    ///
 +    /// ### Why is this bad?
 +    /// This is not a bad thing but definitely a hacky way to do it. See
 +    /// issue [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) for a discussion
 +    /// about the implementation.
 +    ///
 +    /// ### Known problems
 +    /// Hopefully none. It would be pretty uncool to have a problem here :)
 +    ///
 +    /// ### Example output
 +    /// ```json,ignore
 +    /// {
 +    ///     "id": "internal_metadata_collector",
 +    ///     "id_span": {
 +    ///         "path": "clippy_lints/src/utils/internal_lints/metadata_collector.rs",
 +    ///         "line": 1
 +    ///     },
 +    ///     "group": "clippy::internal",
 +    ///     "docs": " ### What it does\nCollects metadata about clippy lints for the website. [...] "
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.56.0"]
 +    pub INTERNAL_METADATA_COLLECTOR,
 +    internal_warn,
 +    "A busy bee collection metadata about lints"
 +}
 +
 +impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]);
 +
 +#[allow(clippy::module_name_repetitions)]
 +#[derive(Debug, Clone)]
 +pub struct MetadataCollector {
 +    /// All collected lints
 +    ///
 +    /// We use a Heap here to have the lints added in alphabetic order in the export
 +    lints: BinaryHeap<LintMetadata>,
 +    applicability_info: FxHashMap<String, ApplicabilityInfo>,
 +    config: Vec<ClippyConfiguration>,
 +}
 +
 +impl MetadataCollector {
 +    pub fn new() -> Self {
 +        Self {
 +            lints: BinaryHeap::<LintMetadata>::default(),
 +            applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(),
 +            config: collect_configs(),
 +        }
 +    }
 +
 +    fn get_lint_configs(&self, lint_name: &str) -> Option<String> {
 +        self.config
 +            .iter()
 +            .filter(|config| config.lints.iter().any(|lint| lint == lint_name))
 +            .map(ToString::to_string)
 +            .reduce(|acc, x| acc + &x)
 +            .map(|configurations| format!(CONFIGURATION_SECTION_TEMPLATE!(), configurations = configurations))
 +    }
 +}
 +
 +impl Drop for MetadataCollector {
 +    /// You might ask: How hacky is this?
 +    /// My answer:     YES
 +    fn drop(&mut self) {
 +        // The metadata collector gets dropped twice, this makes sure that we only write
 +        // when the list is full
 +        if self.lints.is_empty() {
 +            return;
 +        }
 +
 +        let mut applicability_info = std::mem::take(&mut self.applicability_info);
 +
 +        // Mapping the final data
 +        let mut lints = std::mem::take(&mut self.lints).into_sorted_vec();
 +        lints
 +            .iter_mut()
 +            .for_each(|x| x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default()));
 +
 +        // Outputting
 +        if Path::new(OUTPUT_FILE).exists() {
 +            fs::remove_file(OUTPUT_FILE).unwrap();
 +        }
 +        let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap();
 +        writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap();
 +    }
 +}
 +
 +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
 +struct LintMetadata {
 +    id: String,
 +    id_span: SerializableSpan,
 +    group: String,
 +    level: String,
 +    docs: String,
 +    version: String,
 +    /// This field is only used in the output and will only be
 +    /// mapped shortly before the actual output.
 +    applicability: Option<ApplicabilityInfo>,
 +}
 +
 +impl LintMetadata {
 +    fn new(
 +        id: String,
 +        id_span: SerializableSpan,
 +        group: String,
 +        level: &'static str,
 +        version: String,
 +        docs: String,
 +    ) -> Self {
 +        Self {
 +            id,
 +            id_span,
 +            group,
 +            level: level.to_string(),
 +            version,
 +            docs,
 +            applicability: None,
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
 +struct SerializableSpan {
 +    path: String,
 +    line: usize,
 +}
 +
 +impl std::fmt::Display for SerializableSpan {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 +        write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line)
 +    }
 +}
 +
 +impl SerializableSpan {
 +    fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self {
 +        Self::from_span(cx, item.ident.span)
 +    }
 +
 +    fn from_span(cx: &LateContext<'_>, span: Span) -> Self {
 +        let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo());
 +
 +        Self {
 +            path: format!("{}", loc.file.name.prefer_remapped()),
 +            line: loc.line,
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
 +struct ApplicabilityInfo {
 +    /// Indicates if any of the lint emissions uses multiple spans. This is related to
 +    /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can
 +    /// currently not be applied automatically.
 +    is_multi_part_suggestion: bool,
 +    applicability: Option<usize>,
 +}
 +
 +impl Serialize for ApplicabilityInfo {
 +    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 +    where
 +        S: Serializer,
 +    {
 +        let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?;
 +        s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?;
 +        if let Some(index) = self.applicability {
 +            s.serialize_field(
 +                "applicability",
 +                &paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX],
 +            )?;
 +        } else {
 +            s.serialize_field("applicability", APPLICABILITY_UNRESOLVED_STR)?;
 +        }
 +        s.end()
 +    }
 +}
 +
 +// ==================================================================
 +// Configuration
 +// ==================================================================
 +#[derive(Debug, Clone, Default)]
 +pub struct ClippyConfiguration {
 +    name: String,
 +    config_type: &'static str,
 +    default: String,
 +    lints: Vec<String>,
 +    doc: String,
 +    #[allow(dead_code)]
 +    deprecation_reason: Option<&'static str>,
 +}
 +
 +impl ClippyConfiguration {
 +    pub fn new(
 +        name: &'static str,
 +        config_type: &'static str,
 +        default: String,
 +        doc_comment: &'static str,
 +        deprecation_reason: Option<&'static str>,
 +    ) -> Self {
 +        let (lints, doc) = parse_config_field_doc(doc_comment)
 +            .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
 +
 +        Self {
 +            name: to_kebab(name),
 +            lints,
 +            doc,
 +            config_type,
 +            default,
 +            deprecation_reason,
 +        }
 +    }
 +}
 +
 +fn collect_configs() -> Vec<ClippyConfiguration> {
 +    crate::utils::conf::metadata::get_configuration_metadata()
 +}
 +
 +/// This parses the field documentation of the config struct.
 +///
 +/// ```rust, ignore
 +/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
 +/// ```
 +///
 +/// Would yield:
 +/// ```rust, ignore
 +/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
 +/// ```
 +fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
 +    const DOC_START: &str = " Lint: ";
 +    if_chain! {
 +        if doc_comment.starts_with(DOC_START);
 +        if let Some(split_pos) = doc_comment.find('.');
 +        then {
 +            let mut doc_comment = doc_comment.to_string();
 +            let mut documentation = doc_comment.split_off(split_pos);
 +
 +            // Extract lints
 +            doc_comment.make_ascii_lowercase();
 +            let lints: Vec<String> = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect();
 +
 +            // Format documentation correctly
 +            // split off leading `.` from lint name list and indent for correct formatting
 +            documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n    ");
 +
 +            Some((lints, documentation))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
 +fn to_kebab(config_name: &str) -> String {
 +    config_name.replace('_', "-")
 +}
 +
 +impl fmt::Display for ClippyConfiguration {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
 +        write!(
 +            f,
 +            CONFIGURATION_VALUE_TEMPLATE!(),
 +            name = self.name,
 +            ty = self.config_type,
 +            doc = self.doc,
 +            default = self.default
 +        )
 +    }
 +}
 +
 +// ==================================================================
 +// Lint pass
 +// ==================================================================
 +impl<'hir> LateLintPass<'hir> for MetadataCollector {
 +    /// Collecting lint declarations like:
 +    /// ```rust, ignore
 +    /// declare_clippy_lint! {
 +    ///     /// ### What it does
 +    ///     /// Something IDK.
 +    ///     pub SOME_LINT,
 +    ///     internal,
 +    ///     "Who am I?"
 +    /// }
 +    /// ```
 +    fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) {
 +        if let ItemKind::Static(ty, Mutability::Not, _) = item.kind {
 +            // Normal lint
 +            if_chain! {
 +                // item validation
 +                if is_lint_ref_type(cx, ty);
 +                // blacklist check
 +                let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
 +                if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
 +                // metadata extraction
 +                if let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item);
 +                if let Some(mut docs) = extract_attr_docs_or_lint(cx, item);
 +                then {
 +                    if let Some(configuration_section) = self.get_lint_configs(&lint_name) {
 +                        docs.push_str(&configuration_section);
 +                    }
 +                    let version = get_lint_version(cx, item);
 +
 +                    self.lints.push(LintMetadata::new(
 +                        lint_name,
 +                        SerializableSpan::from_item(cx, item),
 +                        group,
 +                        level,
 +                        version,
 +                        docs,
 +                    ));
 +                }
 +            }
 +
 +            if_chain! {
 +                if is_deprecated_lint(cx, ty);
 +                // blacklist check
 +                let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
 +                if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
 +                // Metadata the little we can get from a deprecated lint
 +                if let Some(docs) = extract_attr_docs_or_lint(cx, item);
 +                then {
 +                    let version = get_lint_version(cx, item);
 +
 +                    self.lints.push(LintMetadata::new(
 +                        lint_name,
 +                        SerializableSpan::from_item(cx, item),
 +                        DEPRECATED_LINT_GROUP_STR.to_string(),
 +                        DEPRECATED_LINT_LEVEL,
 +                        version,
 +                        docs,
 +                    ));
 +                }
 +            }
 +        }
 +    }
 +
 +    /// Collecting constant applicability from the actual lint emissions
 +    ///
 +    /// Example:
 +    /// ```rust, ignore
 +    /// span_lint_and_sugg(
 +    ///     cx,
 +    ///     SOME_LINT,
 +    ///     item.span,
 +    ///     "Le lint message",
 +    ///     "Here comes help:",
 +    ///     "#![allow(clippy::all)]",
 +    ///     Applicability::MachineApplicable, // <-- Extracts this constant value
 +    /// );
 +    /// ```
 +    fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) {
 +        if let Some(args) = match_lint_emission(cx, expr) {
 +            let emission_info = extract_emission_info(cx, args);
 +            if emission_info.is_empty() {
 +                // See:
 +                // - src/misc.rs:734:9
 +                // - src/methods/mod.rs:3545:13
 +                // - src/methods/mod.rs:3496:13
 +                // We are basically unable to resolve the lint name itself.
 +                return;
 +            }
 +
 +            for (lint_name, applicability, is_multi_part) in emission_info {
 +                let app_info = self.applicability_info.entry(lint_name).or_default();
 +                app_info.applicability = applicability;
 +                app_info.is_multi_part_suggestion = is_multi_part;
 +            }
 +        }
 +    }
 +}
 +
 +// ==================================================================
 +// Lint definition extraction
 +// ==================================================================
 +fn sym_to_string(sym: Symbol) -> String {
 +    sym.as_str().to_string()
 +}
 +
 +fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
 +    extract_attr_docs(cx, item).or_else(|| {
 +        lint_collection_error_item(cx, item, "could not collect the lint documentation");
 +        None
 +    })
 +}
 +
 +/// This function collects all documentation that has been added to an item using
 +/// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks
 +///
 +/// ```ignore
 +/// #[doc = r"Hello world!"]
 +/// #[doc = r"=^.^="]
 +/// struct SomeItem {}
 +/// ```
 +///
 +/// Would result in `Hello world!\n=^.^=\n`
 +///
 +/// ---
 +///
 +/// This function may modify the doc comment to ensure that the string can be displayed using a
 +/// markdown viewer in Clippy's lint list. The following modifications could be applied:
 +/// * Removal of leading space after a new line. (Important to display tables)
 +/// * Ensures that code blocks only contain language information
 +fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
 +    let attrs = cx.tcx.hir().attrs(item.hir_id());
 +    let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str);
 +    let mut docs = String::from(&*lines.next()?.as_str());
 +    let mut in_code_block = false;
 +    let mut is_code_block_rust = false;
 +    for line in lines {
 +        let line = line.as_str();
 +        let line = &*line;
 +
 +        // Rustdoc hides code lines starting with `# ` and this removes them from Clippy's lint list :)
 +        if is_code_block_rust && line.trim_start().starts_with("# ") {
 +            continue;
 +        }
 +
 +        // The line should be represented in the lint list, even if it's just an empty line
 +        docs.push('\n');
 +        if let Some(info) = line.trim_start().strip_prefix("```") {
 +            in_code_block = !in_code_block;
 +            is_code_block_rust = false;
 +            if in_code_block {
 +                let lang = info
 +                    .trim()
 +                    .split(',')
 +                    // remove rustdoc directives
 +                    .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic"))
 +                    // if no language is present, fill in "rust"
 +                    .unwrap_or("rust");
 +                docs.push_str("```");
 +                docs.push_str(lang);
 +
 +                is_code_block_rust = lang == "rust";
 +                continue;
 +            }
 +        }
 +        // This removes the leading space that the macro translation introduces
 +        if let Some(stripped_doc) = line.strip_prefix(' ') {
 +            docs.push_str(stripped_doc);
 +        } else if !line.is_empty() {
 +            docs.push_str(line);
 +        }
 +    }
 +    Some(docs)
 +}
 +
 +fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
 +    extract_clippy_version_value(cx, item).map_or_else(
-     /// Checks if the suggestions include multiple spanns
++        || VERSION_DEFAULT_STR.to_string(),
 +        |version| version.as_str().to_string(),
 +    )
 +}
 +
 +fn get_lint_group_and_level_or_lint(
 +    cx: &LateContext<'_>,
 +    lint_name: &str,
 +    item: &Item<'_>,
 +) -> Option<(String, &'static str)> {
 +    let result = cx.lint_store.check_lint_name(
 +        lint_name,
 +        Some(sym::clippy),
 +        &[Ident::with_dummy_span(sym::clippy)].into_iter().collect(),
 +    );
 +    if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
 +        if let Some(group) = get_lint_group(cx, lint_lst[0]) {
 +            if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
 +                return None;
 +            }
 +
 +            if let Some(level) = get_lint_level_from_group(&group) {
 +                Some((group, level))
 +            } else {
 +                lint_collection_error_item(
 +                    cx,
 +                    item,
 +                    &format!("Unable to determine lint level for found group `{}`", group),
 +                );
 +                None
 +            }
 +        } else {
 +            lint_collection_error_item(cx, item, "Unable to determine lint group");
 +            None
 +        }
 +    } else {
 +        lint_collection_error_item(cx, item, "Unable to find lint in lint_store");
 +        None
 +    }
 +}
 +
 +fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
 +    for (group_name, lints, _) in cx.lint_store.get_lint_groups() {
 +        if IGNORED_LINT_GROUPS.contains(&group_name) {
 +            continue;
 +        }
 +
 +        if lints.iter().any(|group_lint| *group_lint == lint_id) {
 +            let group = group_name.strip_prefix(CLIPPY_LINT_GROUP_PREFIX).unwrap_or(group_name);
 +            return Some((*group).to_string());
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
 +    DEFAULT_LINT_LEVELS
 +        .iter()
 +        .find_map(|(group_name, group_level)| (*group_name == lint_group).then(|| *group_level))
 +}
 +
 +fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
 +    if let hir::TyKind::Path(ref path) = ty.kind {
 +        if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) {
 +            return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE);
 +        }
 +    }
 +
 +    false
 +}
 +
 +// ==================================================================
 +// Lint emission
 +// ==================================================================
 +fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) {
 +    span_lint(
 +        cx,
 +        INTERNAL_METADATA_COLLECTOR,
 +        item.ident.span,
 +        &format!("metadata collection error for `{}`: {}", item.ident.name, message),
 +    );
 +}
 +
 +// ==================================================================
 +// Applicability
 +// ==================================================================
 +/// This function checks if a given expression is equal to a simple lint emission function call.
 +/// It will return the function arguments if the emission matched any function.
 +fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) -> Option<&'hir [hir::Expr<'hir>]> {
 +    LINT_EMISSION_FUNCTIONS
 +        .iter()
 +        .find_map(|emission_fn| match_function_call(cx, expr, emission_fn))
 +}
 +
 +fn take_higher_applicability(a: Option<usize>, b: Option<usize>) -> Option<usize> {
 +    a.map_or(b, |a| a.max(b.unwrap_or_default()).into())
 +}
 +
 +fn extract_emission_info<'hir>(
 +    cx: &LateContext<'hir>,
 +    args: &'hir [hir::Expr<'hir>],
 +) -> Vec<(String, Option<usize>, bool)> {
 +    let mut lints = Vec::new();
 +    let mut applicability = None;
 +    let mut multi_part = false;
 +
 +    for arg in args {
 +        let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(arg));
 +
 +        if match_type(cx, arg_ty, &paths::LINT) {
 +            // If we found the lint arg, extract the lint name
 +            let mut resolved_lints = resolve_lints(cx, arg);
 +            lints.append(&mut resolved_lints);
 +        } else if match_type(cx, arg_ty, &paths::APPLICABILITY) {
 +            applicability = resolve_applicability(cx, arg);
 +        } else if arg_ty.is_closure() {
 +            multi_part |= check_is_multi_part(cx, arg);
 +            applicability = applicability.or_else(|| resolve_applicability(cx, arg));
 +        }
 +    }
 +
 +    lints
 +        .into_iter()
 +        .map(|lint_name| (lint_name, applicability, multi_part))
 +        .collect()
 +}
 +
 +/// Resolves the possible lints that this expression could reference
 +fn resolve_lints<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> {
 +    let mut resolver = LintResolver::new(cx);
 +    resolver.visit_expr(expr);
 +    resolver.lints
 +}
 +
 +/// This function tries to resolve the linked applicability to the given expression.
 +fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> {
 +    let mut resolver = ApplicabilityResolver::new(cx);
 +    resolver.visit_expr(expr);
 +    resolver.complete()
 +}
 +
 +fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
 +    if let ExprKind::Closure(_, _, body_id, _, _) = closure_expr.kind {
 +        let mut scanner = IsMultiSpanScanner::new(cx);
 +        intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body_id));
 +        return scanner.is_multi_part();
 +    } else if let Some(local) = get_parent_local(cx, closure_expr) {
 +        if let Some(local_init) = local.init {
 +            return check_is_multi_part(cx, local_init);
 +        }
 +    }
 +
 +    false
 +}
 +
 +struct LintResolver<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    lints: Vec<String>,
 +}
 +
 +impl<'a, 'hir> LintResolver<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            lints: Vec::<String>::default(),
 +        }
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        if_chain! {
 +            if let ExprKind::Path(qpath) = &expr.kind;
 +            if let QPath::Resolved(_, path) = qpath;
 +
 +            let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
 +            if match_type(self.cx, expr_ty, &paths::LINT);
 +            then {
 +                if let hir::def::Res::Def(DefKind::Static(..), _) = path.res {
 +                    let lint_name = last_path_segment(qpath).ident.name;
 +                    self.lints.push(sym_to_string(lint_name).to_ascii_lowercase());
 +                } else if let Some(local) = get_parent_local(self.cx, expr) {
 +                    if let Some(local_init) = local.init {
 +                        intravisit::walk_expr(self, local_init);
 +                    }
 +                }
 +            }
 +        }
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
 +
 +/// This visitor finds the highest applicability value in the visited expressions
 +struct ApplicabilityResolver<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    /// This is the index of hightest `Applicability` for `paths::APPLICABILITY_VALUES`
 +    applicability_index: Option<usize>,
 +}
 +
 +impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            applicability_index: None,
 +        }
 +    }
 +
 +    fn add_new_index(&mut self, new_index: usize) {
 +        self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index));
 +    }
 +
 +    fn complete(self) -> Option<usize> {
 +        self.applicability_index
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_path(&mut self, path: &'hir hir::Path<'hir>, _id: hir::HirId) {
 +        for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() {
 +            if match_path(path, enum_value) {
 +                self.add_new_index(index);
 +                return;
 +            }
 +        }
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
 +
 +        if_chain! {
 +            if match_type(self.cx, expr_ty, &paths::APPLICABILITY);
 +            if let Some(local) = get_parent_local(self.cx, expr);
 +            if let Some(local_init) = local.init;
 +            then {
 +                intravisit::walk_expr(self, local_init);
 +            }
 +        };
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
 +
 +/// This returns the parent local node if the expression is a reference one
 +fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> {
 +    if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
 +        if let hir::def::Res::Local(local_hir) = path.res {
 +            return get_parent_local_hir_id(cx, local_hir);
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> {
 +    let map = cx.tcx.hir();
 +
 +    match map.find(map.get_parent_node(hir_id)) {
 +        Some(hir::Node::Local(local)) => Some(local),
 +        Some(hir::Node::Pat(pattern)) => get_parent_local_hir_id(cx, pattern.hir_id),
 +        _ => None,
 +    }
 +}
 +
 +/// This visitor finds the highest applicability value in the visited expressions
 +struct IsMultiSpanScanner<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    suggestion_count: usize,
 +}
 +
 +impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            suggestion_count: 0,
 +        }
 +    }
 +
 +    /// Add a new single expression suggestion to the counter
 +    fn add_single_span_suggestion(&mut self) {
 +        self.suggestion_count += 1;
 +    }
 +
 +    /// Signals that a suggestion with possible multiple spans was found
 +    fn add_multi_part_suggestion(&mut self) {
 +        self.suggestion_count += 2;
 +    }
 +
++    /// Checks if the suggestions include multiple spans
 +    fn is_multi_part(&self) -> bool {
 +        self.suggestion_count > 1
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        // Early return if the lint is already multi span
 +        if self.is_multi_part() {
 +            return;
 +        }
 +
 +        match &expr.kind {
 +            ExprKind::Call(fn_expr, _args) => {
 +                let found_function = SUGGESTION_FUNCTIONS
 +                    .iter()
 +                    .any(|func_path| match_function_call(self.cx, fn_expr, func_path).is_some());
 +                if found_function {
 +                    // These functions are all multi part suggestions
 +                    self.add_single_span_suggestion();
 +                }
 +            },
 +            ExprKind::MethodCall(path, arg, _arg_span) => {
 +                let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&arg[0]));
 +                if match_type(self.cx, self_ty, &paths::DIAGNOSTIC_BUILDER) {
 +                    let called_method = path.ident.name.as_str().to_string();
 +                    for (method_name, is_multi_part) in &SUGGESTION_DIAGNOSTIC_BUILDER_METHODS {
 +                        if *method_name == called_method {
 +                            if *is_multi_part {
 +                                self.add_multi_part_suggestion();
 +                            } else {
 +                                self.add_single_span_suggestion();
 +                            }
 +                            break;
 +                        }
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
index dc385ebacba664db68ea0ed564508f9ef7a312cb,0000000000000000000000000000000000000000..787e9fd982c89c40ab67b3f1f997aa643bd406e8
mode 100644,000000..100644
--- /dev/null
@@@ -1,5 -1,0 +1,5 @@@
- pub mod inspector;
 +pub mod author;
 +pub mod conf;
++pub mod dump_hir;
 +#[cfg(feature = "internal")]
 +pub mod internal_lints;
index be46b791aa4b656decb9406aeb5ee275136e8ce1,0000000000000000000000000000000000000000..fdb822c3e5b6d6f5908312885dbff2455323c455
mode 100644,000000..100644
--- /dev/null
@@@ -1,646 -1,0 +1,662 @@@
- use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp};
 +#![allow(clippy::float_cmp)]
 +
 +use crate::{clip, is_direct_expn_of, sext, unsext};
 +use if_chain::if_chain;
 +use rustc_ast::ast::{self, LitFloatType, LitKind};
 +use rustc_data_structures::sync::Lrc;
 +use rustc_hir::def::{DefKind, Res};
++use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp};
 +use rustc_lint::LateContext;
 +use rustc_middle::mir::interpret::Scalar;
 +use rustc_middle::ty::subst::{Subst, SubstsRef};
 +use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty, TyCtxt};
 +use rustc_middle::{bug, span_bug};
 +use rustc_span::symbol::Symbol;
 +use std::cmp::Ordering::{self, Equal};
 +use std::convert::TryInto;
 +use std::hash::{Hash, Hasher};
 +use std::iter;
 +
 +/// A `LitKind`-like enum to fold constant `Expr`s into.
 +#[derive(Debug, Clone)]
 +pub enum Constant {
 +    /// A `String` (e.g., "abc").
 +    Str(String),
 +    /// A binary string (e.g., `b"abc"`).
 +    Binary(Lrc<[u8]>),
 +    /// A single `char` (e.g., `'a'`).
 +    Char(char),
 +    /// An integer's bit representation.
 +    Int(u128),
 +    /// An `f32`.
 +    F32(f32),
 +    /// An `f64`.
 +    F64(f64),
 +    /// `true` or `false`.
 +    Bool(bool),
 +    /// An array of constants.
 +    Vec(Vec<Constant>),
 +    /// Also an array, but with only one constant, repeated N times.
 +    Repeat(Box<Constant>, u64),
 +    /// A tuple of constants.
 +    Tuple(Vec<Constant>),
 +    /// A raw pointer.
 +    RawPtr(u128),
 +    /// A reference
 +    Ref(Box<Constant>),
 +    /// A literal with syntax error.
 +    Err(Symbol),
 +}
 +
 +impl PartialEq for Constant {
 +    fn eq(&self, other: &Self) -> bool {
 +        match (self, other) {
 +            (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs,
 +            (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r,
 +            (&Self::Char(l), &Self::Char(r)) => l == r,
 +            (&Self::Int(l), &Self::Int(r)) => l == r,
 +            (&Self::F64(l), &Self::F64(r)) => {
 +                // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
 +                // `Fw32 == Fw64`, so don’t compare them.
 +                // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
 +                l.to_bits() == r.to_bits()
 +            },
 +            (&Self::F32(l), &Self::F32(r)) => {
 +                // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
 +                // `Fw32 == Fw64`, so don’t compare them.
 +                // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
 +                f64::from(l).to_bits() == f64::from(r).to_bits()
 +            },
 +            (&Self::Bool(l), &Self::Bool(r)) => l == r,
 +            (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r,
 +            (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
 +            (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb,
 +            // TODO: are there inter-type equalities?
 +            _ => false,
 +        }
 +    }
 +}
 +
 +impl Hash for Constant {
 +    fn hash<H>(&self, state: &mut H)
 +    where
 +        H: Hasher,
 +    {
 +        std::mem::discriminant(self).hash(state);
 +        match *self {
 +            Self::Str(ref s) => {
 +                s.hash(state);
 +            },
 +            Self::Binary(ref b) => {
 +                b.hash(state);
 +            },
 +            Self::Char(c) => {
 +                c.hash(state);
 +            },
 +            Self::Int(i) => {
 +                i.hash(state);
 +            },
 +            Self::F32(f) => {
 +                f64::from(f).to_bits().hash(state);
 +            },
 +            Self::F64(f) => {
 +                f.to_bits().hash(state);
 +            },
 +            Self::Bool(b) => {
 +                b.hash(state);
 +            },
 +            Self::Vec(ref v) | Self::Tuple(ref v) => {
 +                v.hash(state);
 +            },
 +            Self::Repeat(ref c, l) => {
 +                c.hash(state);
 +                l.hash(state);
 +            },
 +            Self::RawPtr(u) => {
 +                u.hash(state);
 +            },
 +            Self::Ref(ref r) => {
 +                r.hash(state);
 +            },
 +            Self::Err(ref s) => {
 +                s.hash(state);
 +            },
 +        }
 +    }
 +}
 +
 +impl Constant {
 +    pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
 +        match (left, right) {
 +            (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
 +            (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
 +            (&Self::Int(l), &Self::Int(r)) => {
 +                if let ty::Int(int_ty) = *cmp_type.kind() {
 +                    Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty)))
 +                } else {
 +                    Some(l.cmp(&r))
 +                }
 +            },
 +            (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
 +            (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
 +            (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)),
 +            (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => iter::zip(l, r)
 +                .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri))
 +                .find(|r| r.map_or(true, |o| o != Ordering::Equal))
 +                .unwrap_or_else(|| Some(l.len().cmp(&r.len()))),
 +            (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => {
 +                match Self::partial_cmp(tcx, cmp_type, lv, rv) {
 +                    Some(Equal) => Some(ls.cmp(rs)),
 +                    x => x,
 +                }
 +            },
 +            (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb),
 +            // TODO: are there any useful inter-type orderings?
 +            _ => None,
 +        }
 +    }
 +
 +    /// Returns the integer value or `None` if `self` or `val_type` is not integer type.
 +    pub fn int_value(&self, cx: &LateContext<'_>, val_type: Ty<'_>) -> Option<FullInt> {
 +        if let Constant::Int(const_int) = *self {
 +            match *val_type.kind() {
 +                ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))),
 +                ty::Uint(_) => Some(FullInt::U(const_int)),
 +                _ => None,
 +            }
 +        } else {
 +            None
 +        }
 +    }
 +
 +    #[must_use]
 +    pub fn peel_refs(mut self) -> Self {
 +        while let Constant::Ref(r) = self {
 +            self = *r;
 +        }
 +        self
 +    }
 +}
 +
 +/// Parses a `LitKind` to a `Constant`.
 +pub fn lit_to_mir_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
 +    match *lit {
 +        LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
 +        LitKind::Byte(b) => Constant::Int(u128::from(b)),
 +        LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)),
 +        LitKind::Char(c) => Constant::Char(c),
 +        LitKind::Int(n, _) => Constant::Int(n),
 +        LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty {
 +            ast::FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()),
 +            ast::FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()),
 +        },
 +        LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() {
 +            ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()),
 +            ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()),
 +            _ => bug!(),
 +        },
 +        LitKind::Bool(b) => Constant::Bool(b),
 +        LitKind::Err(s) => Constant::Err(s),
 +    }
 +}
 +
 +pub fn constant<'tcx>(
 +    lcx: &LateContext<'tcx>,
 +    typeck_results: &ty::TypeckResults<'tcx>,
 +    e: &Expr<'_>,
 +) -> Option<(Constant, bool)> {
 +    let mut cx = ConstEvalLateContext {
 +        lcx,
 +        typeck_results,
 +        param_env: lcx.param_env,
 +        needed_resolution: false,
 +        substs: lcx.tcx.intern_substs(&[]),
 +    };
 +    cx.expr(e).map(|cst| (cst, cx.needed_resolution))
 +}
 +
 +pub fn constant_simple<'tcx>(
 +    lcx: &LateContext<'tcx>,
 +    typeck_results: &ty::TypeckResults<'tcx>,
 +    e: &Expr<'_>,
 +) -> Option<Constant> {
 +    constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
 +}
 +
 +pub fn constant_full_int<'tcx>(
 +    lcx: &LateContext<'tcx>,
 +    typeck_results: &ty::TypeckResults<'tcx>,
 +    e: &Expr<'_>,
 +) -> Option<FullInt> {
 +    constant_simple(lcx, typeck_results, e)?.int_value(lcx, typeck_results.expr_ty(e))
 +}
 +
 +#[derive(Copy, Clone, Debug, Eq)]
 +pub enum FullInt {
 +    S(i128),
 +    U(u128),
 +}
 +
 +impl PartialEq for FullInt {
 +    #[must_use]
 +    fn eq(&self, other: &Self) -> bool {
 +        self.cmp(other) == Ordering::Equal
 +    }
 +}
 +
 +impl PartialOrd for FullInt {
 +    #[must_use]
 +    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
 +        Some(self.cmp(other))
 +    }
 +}
 +
 +impl Ord for FullInt {
 +    #[must_use]
 +    fn cmp(&self, other: &Self) -> Ordering {
 +        use FullInt::{S, U};
 +
 +        fn cmp_s_u(s: i128, u: u128) -> Ordering {
 +            u128::try_from(s).map_or(Ordering::Less, |x| x.cmp(&u))
 +        }
 +
 +        match (*self, *other) {
 +            (S(s), S(o)) => s.cmp(&o),
 +            (U(s), U(o)) => s.cmp(&o),
 +            (S(s), U(o)) => cmp_s_u(s, o),
 +            (U(s), S(o)) => cmp_s_u(o, s).reverse(),
 +        }
 +    }
 +}
 +
 +/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`.
 +pub fn constant_context<'a, 'tcx>(
 +    lcx: &'a LateContext<'tcx>,
 +    typeck_results: &'a ty::TypeckResults<'tcx>,
 +) -> ConstEvalLateContext<'a, 'tcx> {
 +    ConstEvalLateContext {
 +        lcx,
 +        typeck_results,
 +        param_env: lcx.param_env,
 +        needed_resolution: false,
 +        substs: lcx.tcx.intern_substs(&[]),
 +    }
 +}
 +
 +pub struct ConstEvalLateContext<'a, 'tcx> {
 +    lcx: &'a LateContext<'tcx>,
 +    typeck_results: &'a ty::TypeckResults<'tcx>,
 +    param_env: ty::ParamEnv<'tcx>,
 +    needed_resolution: bool,
 +    substs: SubstsRef<'tcx>,
 +}
 +
 +impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
 +    /// Simple constant folding: Insert an expression, get a constant or none.
 +    pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> {
 +        match e.kind {
 +            ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
 +            ExprKind::Block(block, _) => self.block(block),
 +            ExprKind::Lit(ref lit) => {
 +                if is_direct_expn_of(e.span, "cfg").is_some() {
 +                    None
 +                } else {
 +                    Some(lit_to_mir_constant(&lit.node, self.typeck_results.expr_ty_opt(e)))
 +                }
 +            },
 +            ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec),
 +            ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple),
 +            ExprKind::Repeat(value, _) => {
 +                let n = match self.typeck_results.expr_ty(e).kind() {
 +                    ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?,
 +                    _ => span_bug!(e.span, "typeck error"),
 +                };
 +                self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
 +            },
 +            ExprKind::Unary(op, operand) => self.expr(operand).and_then(|o| match op {
 +                UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)),
 +                UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)),
 +                UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
 +            }),
 +            ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
 +            ExprKind::Binary(op, left, right) => self.binop(op, left, right),
 +            ExprKind::Call(callee, args) => {
 +                // We only handle a few const functions for now.
 +                if_chain! {
 +                    if args.is_empty();
 +                    if let ExprKind::Path(qpath) = &callee.kind;
 +                    let res = self.typeck_results.qpath_res(qpath, callee.hir_id);
 +                    if let Some(def_id) = res.opt_def_id();
 +                    let def_path = self.lcx.get_def_path(def_id);
 +                    let def_path: Vec<&str> = def_path.iter().take(4).map(Symbol::as_str).collect();
 +                    if let ["core", "num", int_impl, "max_value"] = *def_path;
 +                    then {
 +                        let value = match int_impl {
 +                            "<impl i8>" => i8::MAX as u128,
 +                            "<impl i16>" => i16::MAX as u128,
 +                            "<impl i32>" => i32::MAX as u128,
 +                            "<impl i64>" => i64::MAX as u128,
 +                            "<impl i128>" => i128::MAX as u128,
 +                            _ => return None,
 +                        };
 +                        Some(Constant::Int(value))
 +                    } else {
 +                        None
 +                    }
 +                }
 +            },
 +            ExprKind::Index(arr, index) => self.index(arr, index),
 +            ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))),
 +            // TODO: add other expressions.
 +            _ => None,
 +        }
 +    }
 +
 +    #[allow(clippy::cast_possible_wrap)]
 +    fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
 +        use self::Constant::{Bool, Int};
 +        match *o {
 +            Bool(b) => Some(Bool(!b)),
 +            Int(value) => {
 +                let value = !value;
 +                match *ty.kind() {
 +                    ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))),
 +                    ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))),
 +                    _ => None,
 +                }
 +            },
 +            _ => None,
 +        }
 +    }
 +
 +    fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
 +        use self::Constant::{Int, F32, F64};
 +        match *o {
 +            Int(value) => {
 +                let ity = match *ty.kind() {
 +                    ty::Int(ity) => ity,
 +                    _ => return None,
 +                };
 +                // sign extend
 +                let value = sext(self.lcx.tcx, value, ity);
 +                let value = value.checked_neg()?;
 +                // clear unused bits
 +                Some(Int(unsext(self.lcx.tcx, value, ity)))
 +            },
 +            F32(f) => Some(F32(-f)),
 +            F64(f) => Some(F64(-f)),
 +            _ => None,
 +        }
 +    }
 +
 +    /// Create `Some(Vec![..])` of all constants, unless there is any
 +    /// non-constant part.
 +    fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant>> {
 +        vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
 +    }
 +
 +    /// Lookup a possibly constant expression from an `ExprKind::Path`.
 +    fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<Constant> {
 +        let res = self.typeck_results.qpath_res(qpath, id);
 +        match res {
 +            Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
++                // Check if this constant is based on `cfg!(..)`,
++                // which is NOT constant for our purposes.
++                if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id) &&
++                let Node::Item(&Item {
++                    kind: ItemKind::Const(_, body_id),
++                    ..
++                }) = node &&
++                let Node::Expr(&Expr {
++                    kind: ExprKind::Lit(_),
++                    span,
++                    ..
++                }) = self.lcx.tcx.hir().get(body_id.hir_id) &&
++                is_direct_expn_of(span, "cfg").is_some() {
++                    return None;
++                }
++
 +                let substs = self.typeck_results.node_substs(id);
 +                let substs = if self.substs.is_empty() {
 +                    substs
 +                } else {
 +                    substs.subst(self.lcx.tcx, self.substs)
 +                };
 +
 +                let result = self
 +                    .lcx
 +                    .tcx
 +                    .const_eval_resolve(
 +                        self.param_env,
 +                        ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs),
 +                        None,
 +                    )
 +                    .ok()
 +                    .map(|val| rustc_middle::ty::Const::from_value(self.lcx.tcx, val, ty))?;
 +                let result = miri_to_const(result);
 +                if result.is_some() {
 +                    self.needed_resolution = true;
 +                }
 +                result
 +            },
 +            // FIXME: cover all usable cases.
 +            _ => None,
 +        }
 +    }
 +
 +    fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant> {
 +        let lhs = self.expr(lhs);
 +        let index = self.expr(index);
 +
 +        match (lhs, index) {
 +            (Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) {
 +                Some(Constant::F32(x)) => Some(Constant::F32(*x)),
 +                Some(Constant::F64(x)) => Some(Constant::F64(*x)),
 +                _ => None,
 +            },
 +            (Some(Constant::Vec(vec)), _) => {
 +                if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) {
 +                    match vec.get(0) {
 +                        Some(Constant::F32(x)) => Some(Constant::F32(*x)),
 +                        Some(Constant::F64(x)) => Some(Constant::F64(*x)),
 +                        _ => None,
 +                    }
 +                } else {
 +                    None
 +                }
 +            },
 +            _ => None,
 +        }
 +    }
 +
 +    /// A block can only yield a constant if it only has one constant expression.
 +    fn block(&mut self, block: &Block<'_>) -> Option<Constant> {
 +        if block.stmts.is_empty() {
 +            block.expr.as_ref().and_then(|b| self.expr(b))
 +        } else {
 +            None
 +        }
 +    }
 +
 +    fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant> {
 +        if let Some(Constant::Bool(b)) = self.expr(cond) {
 +            if b {
 +                self.expr(&*then)
 +            } else {
 +                otherwise.as_ref().and_then(|expr| self.expr(expr))
 +            }
 +        } else {
 +            None
 +        }
 +    }
 +
 +    fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant> {
 +        let l = self.expr(left)?;
 +        let r = self.expr(right);
 +        match (l, r) {
 +            (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() {
 +                ty::Int(ity) => {
 +                    let l = sext(self.lcx.tcx, l, ity);
 +                    let r = sext(self.lcx.tcx, r, ity);
 +                    let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity));
 +                    match op.node {
 +                        BinOpKind::Add => l.checked_add(r).map(zext),
 +                        BinOpKind::Sub => l.checked_sub(r).map(zext),
 +                        BinOpKind::Mul => l.checked_mul(r).map(zext),
 +                        BinOpKind::Div if r != 0 => l.checked_div(r).map(zext),
 +                        BinOpKind::Rem if r != 0 => l.checked_rem(r).map(zext),
 +                        BinOpKind::Shr => l.checked_shr(r.try_into().expect("invalid shift")).map(zext),
 +                        BinOpKind::Shl => l.checked_shl(r.try_into().expect("invalid shift")).map(zext),
 +                        BinOpKind::BitXor => Some(zext(l ^ r)),
 +                        BinOpKind::BitOr => Some(zext(l | r)),
 +                        BinOpKind::BitAnd => Some(zext(l & r)),
 +                        BinOpKind::Eq => Some(Constant::Bool(l == r)),
 +                        BinOpKind::Ne => Some(Constant::Bool(l != r)),
 +                        BinOpKind::Lt => Some(Constant::Bool(l < r)),
 +                        BinOpKind::Le => Some(Constant::Bool(l <= r)),
 +                        BinOpKind::Ge => Some(Constant::Bool(l >= r)),
 +                        BinOpKind::Gt => Some(Constant::Bool(l > r)),
 +                        _ => None,
 +                    }
 +                },
 +                ty::Uint(_) => match op.node {
 +                    BinOpKind::Add => l.checked_add(r).map(Constant::Int),
 +                    BinOpKind::Sub => l.checked_sub(r).map(Constant::Int),
 +                    BinOpKind::Mul => l.checked_mul(r).map(Constant::Int),
 +                    BinOpKind::Div => l.checked_div(r).map(Constant::Int),
 +                    BinOpKind::Rem => l.checked_rem(r).map(Constant::Int),
 +                    BinOpKind::Shr => l.checked_shr(r.try_into().expect("shift too large")).map(Constant::Int),
 +                    BinOpKind::Shl => l.checked_shl(r.try_into().expect("shift too large")).map(Constant::Int),
 +                    BinOpKind::BitXor => Some(Constant::Int(l ^ r)),
 +                    BinOpKind::BitOr => Some(Constant::Int(l | r)),
 +                    BinOpKind::BitAnd => Some(Constant::Int(l & r)),
 +                    BinOpKind::Eq => Some(Constant::Bool(l == r)),
 +                    BinOpKind::Ne => Some(Constant::Bool(l != r)),
 +                    BinOpKind::Lt => Some(Constant::Bool(l < r)),
 +                    BinOpKind::Le => Some(Constant::Bool(l <= r)),
 +                    BinOpKind::Ge => Some(Constant::Bool(l >= r)),
 +                    BinOpKind::Gt => Some(Constant::Bool(l > r)),
 +                    _ => None,
 +                },
 +                _ => None,
 +            },
 +            (Constant::F32(l), Some(Constant::F32(r))) => match op.node {
 +                BinOpKind::Add => Some(Constant::F32(l + r)),
 +                BinOpKind::Sub => Some(Constant::F32(l - r)),
 +                BinOpKind::Mul => Some(Constant::F32(l * r)),
 +                BinOpKind::Div => Some(Constant::F32(l / r)),
 +                BinOpKind::Rem => Some(Constant::F32(l % r)),
 +                BinOpKind::Eq => Some(Constant::Bool(l == r)),
 +                BinOpKind::Ne => Some(Constant::Bool(l != r)),
 +                BinOpKind::Lt => Some(Constant::Bool(l < r)),
 +                BinOpKind::Le => Some(Constant::Bool(l <= r)),
 +                BinOpKind::Ge => Some(Constant::Bool(l >= r)),
 +                BinOpKind::Gt => Some(Constant::Bool(l > r)),
 +                _ => None,
 +            },
 +            (Constant::F64(l), Some(Constant::F64(r))) => match op.node {
 +                BinOpKind::Add => Some(Constant::F64(l + r)),
 +                BinOpKind::Sub => Some(Constant::F64(l - r)),
 +                BinOpKind::Mul => Some(Constant::F64(l * r)),
 +                BinOpKind::Div => Some(Constant::F64(l / r)),
 +                BinOpKind::Rem => Some(Constant::F64(l % r)),
 +                BinOpKind::Eq => Some(Constant::Bool(l == r)),
 +                BinOpKind::Ne => Some(Constant::Bool(l != r)),
 +                BinOpKind::Lt => Some(Constant::Bool(l < r)),
 +                BinOpKind::Le => Some(Constant::Bool(l <= r)),
 +                BinOpKind::Ge => Some(Constant::Bool(l >= r)),
 +                BinOpKind::Gt => Some(Constant::Bool(l > r)),
 +                _ => None,
 +            },
 +            (l, r) => match (op.node, l, r) {
 +                (BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)),
 +                (BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)),
 +                (BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => {
 +                    Some(r)
 +                },
 +                (BinOpKind::BitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)),
 +                (BinOpKind::BitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)),
 +                (BinOpKind::BitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)),
 +                _ => None,
 +            },
 +        }
 +    }
 +}
 +
 +pub fn miri_to_const(result: ty::Const<'_>) -> Option<Constant> {
 +    use rustc_middle::mir::interpret::ConstValue;
 +    match result.val() {
 +        ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => {
 +            match result.ty().kind() {
 +                ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
 +                ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
 +                ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
 +                    int.try_into().expect("invalid f32 bit representation"),
 +                ))),
 +                ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
 +                    int.try_into().expect("invalid f64 bit representation"),
 +                ))),
 +                ty::RawPtr(type_and_mut) => {
 +                    if let ty::Uint(_) = type_and_mut.ty.kind() {
 +                        return Some(Constant::RawPtr(int.assert_bits(int.size())));
 +                    }
 +                    None
 +                },
 +                // FIXME: implement other conversions.
 +                _ => None,
 +            }
 +        },
 +        ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty().kind() {
 +            ty::Ref(_, tam, _) => match tam.kind() {
 +                ty::Str => String::from_utf8(
 +                    data.inner()
 +                        .inspect_with_uninit_and_ptr_outside_interpreter(start..end)
 +                        .to_owned(),
 +                )
 +                .ok()
 +                .map(Constant::Str),
 +                _ => None,
 +            },
 +            _ => None,
 +        },
 +        ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty().kind() {
 +            ty::Array(sub_type, len) => match sub_type.kind() {
 +                ty::Float(FloatTy::F32) => match miri_to_const(*len) {
 +                    Some(Constant::Int(len)) => alloc
 +                        .inner()
 +                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize))
 +                        .to_owned()
 +                        .chunks(4)
 +                        .map(|chunk| {
 +                            Some(Constant::F32(f32::from_le_bytes(
 +                                chunk.try_into().expect("this shouldn't happen"),
 +                            )))
 +                        })
 +                        .collect::<Option<Vec<Constant>>>()
 +                        .map(Constant::Vec),
 +                    _ => None,
 +                },
 +                ty::Float(FloatTy::F64) => match miri_to_const(*len) {
 +                    Some(Constant::Int(len)) => alloc
 +                        .inner()
 +                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize))
 +                        .to_owned()
 +                        .chunks(8)
 +                        .map(|chunk| {
 +                            Some(Constant::F64(f64::from_le_bytes(
 +                                chunk.try_into().expect("this shouldn't happen"),
 +                            )))
 +                        })
 +                        .collect::<Option<Vec<Constant>>>()
 +                        .map(Constant::Vec),
 +                    _ => None,
 +                },
 +                // FIXME: implement other array type conversions.
 +                _ => None,
 +            },
 +            _ => None,
 +        },
 +        // FIXME: implement other conversions.
 +        _ => None,
 +    }
 +}
index c05317f59b716ad60f0fdd92689ee1167fbe914e,0000000000000000000000000000000000000000..f4da625f1e306a0e083716316ede4a5f07ced8d7
mode 100644,000000..100644
--- /dev/null
@@@ -1,992 -1,0 +1,1000 @@@
- use crate::consts::{constant_context, constant_simple};
++use crate::consts::constant_simple;
 +use crate::source::snippet_opt;
 +use rustc_ast::ast::InlineAsmTemplatePiece;
 +use rustc_data_structures::fx::FxHasher;
 +use rustc_hir::def::Res;
 +use rustc_hir::HirIdMap;
 +use rustc_hir::{
 +    ArrayLen, BinOpKind, Block, BodyId, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId,
 +    InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, PathSegment, QPath, Stmt,
 +    StmtKind, Ty, TyKind, TypeBinding,
 +};
 +use rustc_lexer::{tokenize, TokenKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::TypeckResults;
 +use rustc_span::Symbol;
 +use std::hash::{Hash, Hasher};
 +
 +/// Type used to check whether two ast are the same. This is different from the
- /// operator
- /// `==` on ast types as this operator would compare true equality with ID and
- /// span.
++/// operator `==` on ast types as this operator would compare true equality with
++/// ID and span.
 +///
 +/// Note that some expressions kinds are not considered but could be added.
 +pub struct SpanlessEq<'a, 'tcx> {
 +    /// Context used to evaluate constant expressions.
 +    cx: &'a LateContext<'tcx>,
-     maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
++    maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>,
 +    allow_side_effects: bool,
 +    expr_fallback: Option<Box<dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a>>,
 +}
 +
 +impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
 +    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            cx,
-             maybe_typeck_results: cx.maybe_typeck_results(),
++            maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)),
 +            allow_side_effects: true,
 +            expr_fallback: None,
 +        }
 +    }
 +
 +    /// Consider expressions containing potential side effects as not equal.
 +    #[must_use]
 +    pub fn deny_side_effects(self) -> Self {
 +        Self {
 +            allow_side_effects: false,
 +            ..self
 +        }
 +    }
 +
 +    #[must_use]
 +    pub fn expr_fallback(self, expr_fallback: impl FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self {
 +        Self {
 +            expr_fallback: Some(Box::new(expr_fallback)),
 +            ..self
 +        }
 +    }
 +
 +    /// Use this method to wrap comparisons that may involve inter-expression context.
 +    /// See `self.locals`.
 +    pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> {
 +        HirEqInterExpr {
 +            inner: self,
 +            locals: HirIdMap::default(),
 +        }
 +    }
 +
 +    #[allow(dead_code)]
 +    pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
 +        self.inter_expr().eq_block(left, right)
 +    }
 +
 +    pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +        self.inter_expr().eq_expr(left, right)
 +    }
 +
 +    pub fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
 +        self.inter_expr().eq_path(left, right)
 +    }
 +
 +    pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
 +        self.inter_expr().eq_path_segment(left, right)
 +    }
 +
 +    pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
 +        self.inter_expr().eq_path_segments(left, right)
 +    }
 +}
 +
 +pub struct HirEqInterExpr<'a, 'b, 'tcx> {
 +    inner: &'a mut SpanlessEq<'b, 'tcx>,
 +
 +    // When binding are declared, the binding ID in the left expression is mapped to the one on the
 +    // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`,
 +    // these blocks are considered equal since `x` is mapped to `y`.
 +    locals: HirIdMap<HirId>,
 +}
 +
 +impl HirEqInterExpr<'_, '_, '_> {
 +    pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
 +        match (&left.kind, &right.kind) {
 +            (&StmtKind::Local(l), &StmtKind::Local(r)) => {
 +                // This additional check ensures that the type of the locals are equivalent even if the init
 +                // expression or type have some inferred parts.
-                 if let Some(typeck) = self.inner.maybe_typeck_results {
-                     let l_ty = typeck.pat_ty(l.pat);
-                     let r_ty = typeck.pat_ty(r.pat);
++                if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
++                    let l_ty = typeck_lhs.pat_ty(l.pat);
++                    let r_ty = typeck_rhs.pat_ty(r.pat);
 +                    if l_ty != r_ty {
 +                        return false;
 +                    }
 +                }
 +
 +                // eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that
 +                // these only get added if the init and type is equal.
 +                both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
 +                    && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r))
 +                    && self.eq_pat(l.pat, r.pat)
 +            },
 +            (&StmtKind::Expr(l), &StmtKind::Expr(r)) | (&StmtKind::Semi(l), &StmtKind::Semi(r)) => self.eq_expr(l, r),
 +            _ => false,
 +        }
 +    }
 +
 +    /// Checks whether two blocks are the same.
 +    fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
 +        match (left.stmts, left.expr, right.stmts, right.expr) {
 +            ([], None, [], None) => {
 +                // For empty blocks, check to see if the tokens are equal. This will catch the case where a macro
 +                // expanded to nothing, or the cfg attribute was used.
 +                let (left, right) = match (
 +                    snippet_opt(self.inner.cx, left.span),
 +                    snippet_opt(self.inner.cx, right.span),
 +                ) {
 +                    (Some(left), Some(right)) => (left, right),
 +                    _ => return true,
 +                };
 +                let mut left_pos = 0;
 +                let left = tokenize(&left)
 +                    .map(|t| {
 +                        let end = left_pos + t.len;
 +                        let s = &left[left_pos..end];
 +                        left_pos = end;
 +                        (t, s)
 +                    })
 +                    .filter(|(t, _)| {
 +                        !matches!(
 +                            t.kind,
 +                            TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
 +                        )
 +                    })
 +                    .map(|(_, s)| s);
 +                let mut right_pos = 0;
 +                let right = tokenize(&right)
 +                    .map(|t| {
 +                        let end = right_pos + t.len;
 +                        let s = &right[right_pos..end];
 +                        right_pos = end;
 +                        (t, s)
 +                    })
 +                    .filter(|(t, _)| {
 +                        !matches!(
 +                            t.kind,
 +                            TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
 +                        )
 +                    })
 +                    .map(|(_, s)| s);
 +                left.eq(right)
 +            },
 +            _ => {
 +                over(left.stmts, right.stmts, |l, r| self.eq_stmt(l, r))
 +                    && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r))
 +            },
 +        }
 +    }
 +
 +    pub fn eq_array_length(&mut self, left: ArrayLen, right: ArrayLen) -> bool {
 +        match (left, right) {
 +            (ArrayLen::Infer(..), ArrayLen::Infer(..)) => true,
 +            (ArrayLen::Body(l_ct), ArrayLen::Body(r_ct)) => self.eq_body(l_ct.body, r_ct.body),
 +            (_, _) => false,
 +        }
 +    }
 +
 +    pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool {
-         let cx = self.inner.cx;
-         let eval_const = |body| constant_context(cx, cx.tcx.typeck_body(body)).expr(&cx.tcx.hir().body(body).value);
-         eval_const(left) == eval_const(right)
++        // swap out TypeckResults when hashing a body
++        let old_maybe_typeck_results = self.inner.maybe_typeck_results.replace((
++            self.inner.cx.tcx.typeck_body(left),
++            self.inner.cx.tcx.typeck_body(right),
++        ));
++        let res = self.eq_expr(
++            &self.inner.cx.tcx.hir().body(left).value,
++            &self.inner.cx.tcx.hir().body(right).value,
++        );
++        self.inner.maybe_typeck_results = old_maybe_typeck_results;
++        res
 +    }
 +
 +    #[allow(clippy::similar_names)]
 +    pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +        if !self.inner.allow_side_effects && left.span.ctxt() != right.span.ctxt() {
 +            return false;
 +        }
 +
-         if let Some(typeck_results) = self.inner.maybe_typeck_results {
++        if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
 +            if let (Some(l), Some(r)) = (
-                 constant_simple(self.inner.cx, typeck_results, left),
-                 constant_simple(self.inner.cx, typeck_results, right),
++                constant_simple(self.inner.cx, typeck_lhs, left),
++                constant_simple(self.inner.cx, typeck_rhs, right),
 +            ) {
 +                if l == r {
 +                    return true;
 +                }
 +            }
 +        }
 +
 +        let is_eq = match (
 +            reduce_exprkind(self.inner.cx, &left.kind),
 +            reduce_exprkind(self.inner.cx, &right.kind),
 +        ) {
 +            (&ExprKind::AddrOf(lb, l_mut, le), &ExprKind::AddrOf(rb, r_mut, re)) => {
 +                lb == rb && l_mut == r_mut && self.eq_expr(le, re)
 +            },
 +            (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => {
 +                both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
 +            },
 +            (&ExprKind::Assign(ll, lr, _), &ExprKind::Assign(rl, rr, _)) => {
 +                self.inner.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +            },
 +            (&ExprKind::AssignOp(ref lo, ll, lr), &ExprKind::AssignOp(ref ro, rl, rr)) => {
 +                self.inner.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +            },
 +            (&ExprKind::Block(l, _), &ExprKind::Block(r, _)) => self.eq_block(l, r),
 +            (&ExprKind::Binary(l_op, ll, lr), &ExprKind::Binary(r_op, rl, rr)) => {
 +                l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +                    || swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| {
 +                        l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +                    })
 +            },
 +            (&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => {
 +                both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
 +                    && both(le, re, |l, r| self.eq_expr(l, r))
 +            },
 +            (&ExprKind::Box(l), &ExprKind::Box(r)) => self.eq_expr(l, r),
 +            (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => {
 +                self.inner.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
 +            },
 +            (&ExprKind::Cast(lx, lt), &ExprKind::Cast(rx, rt)) | (&ExprKind::Type(lx, lt), &ExprKind::Type(rx, rt)) => {
 +                self.eq_expr(lx, rx) && self.eq_ty(lt, rt)
 +            },
 +            (&ExprKind::Field(l_f_exp, ref l_f_ident), &ExprKind::Field(r_f_exp, ref r_f_ident)) => {
 +                l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp)
 +            },
 +            (&ExprKind::Index(la, li), &ExprKind::Index(ra, ri)) => self.eq_expr(la, ra) && self.eq_expr(li, ri),
 +            (&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => {
 +                self.eq_expr(lc, rc) && self.eq_expr(lt, rt) && both(le, re, |l, r| self.eq_expr(l, r))
 +            },
 +            (&ExprKind::Let(l), &ExprKind::Let(r)) => {
 +                self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init)
 +            },
 +            (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node,
 +            (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => {
 +                lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name)
 +            },
 +            (&ExprKind::Match(le, la, ref ls), &ExprKind::Match(re, ra, ref rs)) => {
 +                ls == rs
 +                    && self.eq_expr(le, re)
 +                    && over(la, ra, |l, r| {
 +                        self.eq_pat(l.pat, r.pat)
 +                            && both(&l.guard, &r.guard, |l, r| self.eq_guard(l, r))
 +                            && self.eq_expr(l.body, r.body)
 +                    })
 +            },
 +            (&ExprKind::MethodCall(l_path, l_args, _), &ExprKind::MethodCall(r_path, r_args, _)) => {
 +                self.inner.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args)
 +            },
 +            (&ExprKind::Repeat(le, ll), &ExprKind::Repeat(re, rl)) => {
 +                self.eq_expr(le, re) && self.eq_array_length(ll, rl)
 +            },
 +            (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)),
 +            (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r),
 +            (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => {
 +                self.eq_qpath(l_path, r_path)
 +                    && both(lo, ro, |l, r| self.eq_expr(l, r))
 +                    && over(lf, rf, |l, r| self.eq_expr_field(l, r))
 +            },
 +            (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
 +            (&ExprKind::Unary(l_op, le), &ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re),
 +            (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
 +            (&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re),
 +            _ => false,
 +        };
 +        is_eq || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right))
 +    }
 +
 +    fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
 +        over(left, right, |l, r| self.eq_expr(l, r))
 +    }
 +
 +    fn eq_expr_field(&mut self, left: &ExprField<'_>, right: &ExprField<'_>) -> bool {
 +        left.ident.name == right.ident.name && self.eq_expr(left.expr, right.expr)
 +    }
 +
 +    fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool {
 +        match (left, right) {
 +            (Guard::If(l), Guard::If(r)) => self.eq_expr(l, r),
 +            (Guard::IfLet(lp, le), Guard::IfLet(rp, re)) => self.eq_pat(lp, rp) && self.eq_expr(le, re),
 +            _ => false,
 +        }
 +    }
 +
 +    fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool {
 +        match (left, right) {
 +            (GenericArg::Const(l), GenericArg::Const(r)) => self.eq_body(l.value.body, r.value.body),
 +            (GenericArg::Lifetime(l_lt), GenericArg::Lifetime(r_lt)) => Self::eq_lifetime(l_lt, r_lt),
 +            (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty, r_ty),
 +            (GenericArg::Infer(l_inf), GenericArg::Infer(r_inf)) => self.eq_ty(&l_inf.to_ty(), &r_inf.to_ty()),
 +            _ => false,
 +        }
 +    }
 +
 +    fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool {
 +        left.name == right.name
 +    }
 +
 +    fn eq_pat_field(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool {
 +        let (PatField { ident: li, pat: lp, .. }, PatField { ident: ri, pat: rp, .. }) = (&left, &right);
 +        li.name == ri.name && self.eq_pat(lp, rp)
 +    }
 +
 +    /// Checks whether two patterns are the same.
 +    fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
 +        match (&left.kind, &right.kind) {
 +            (&PatKind::Box(l), &PatKind::Box(r)) => self.eq_pat(l, r),
 +            (&PatKind::Struct(ref lp, la, ..), &PatKind::Struct(ref rp, ra, ..)) => {
 +                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r))
 +            },
 +            (&PatKind::TupleStruct(ref lp, la, ls), &PatKind::TupleStruct(ref rp, ra, rs)) => {
 +                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
 +            },
 +            (&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => {
 +                let eq = lb == rb && both(lp, rp, |l, r| self.eq_pat(l, r));
 +                if eq {
 +                    self.locals.insert(li, ri);
 +                }
 +                eq
 +            },
 +            (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r),
 +            (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r),
 +            (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)),
 +            (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => {
 +                both(ls, rs, |a, b| self.eq_expr(a, b)) && both(le, re, |a, b| self.eq_expr(a, b)) && (li == ri)
 +            },
 +            (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re),
 +            (&PatKind::Slice(ls, ref li, le), &PatKind::Slice(rs, ref ri, re)) => {
 +                over(ls, rs, |l, r| self.eq_pat(l, r))
 +                    && over(le, re, |l, r| self.eq_pat(l, r))
 +                    && both(li, ri, |l, r| self.eq_pat(l, r))
 +            },
 +            (&PatKind::Wild, &PatKind::Wild) => true,
 +            _ => false,
 +        }
 +    }
 +
 +    #[allow(clippy::similar_names)]
 +    fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool {
 +        match (left, right) {
 +            (&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => {
 +                both(lty, rty, |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath)
 +            },
 +            (&QPath::TypeRelative(lty, lseg), &QPath::TypeRelative(rty, rseg)) => {
 +                self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
 +            },
 +            (&QPath::LangItem(llang_item, ..), &QPath::LangItem(rlang_item, ..)) => llang_item == rlang_item,
 +            _ => false,
 +        }
 +    }
 +
 +    pub fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
 +        match (left.res, right.res) {
 +            (Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r),
 +            (Res::Local(_), _) | (_, Res::Local(_)) => false,
 +            _ => over(left.segments, right.segments, |l, r| self.eq_path_segment(l, r)),
 +        }
 +    }
 +
 +    fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool {
 +        if !(left.parenthesized || right.parenthesized) {
 +            over(left.args, right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work
 +                && over(left.bindings, right.bindings, |l, r| self.eq_type_binding(l, r))
 +        } else if left.parenthesized && right.parenthesized {
 +            over(left.inputs(), right.inputs(), |l, r| self.eq_ty(l, r))
 +                && both(&Some(&left.bindings[0].ty()), &Some(&right.bindings[0].ty()), |l, r| {
 +                    self.eq_ty(l, r)
 +                })
 +        } else {
 +            false
 +        }
 +    }
 +
 +    pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
 +        left.len() == right.len() && left.iter().zip(right).all(|(l, r)| self.eq_path_segment(l, r))
 +    }
 +
 +    pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
 +        // The == of idents doesn't work with different contexts,
 +        // we have to be explicit about hygiene
 +        left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
 +    }
 +
 +    #[allow(clippy::similar_names)]
 +    pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
 +        match (&left.kind, &right.kind) {
 +            (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec),
 +            (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl),
 +            (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => {
 +                l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty)
 +            },
 +            (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => {
 +                l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(&*l_rmut.ty, &*r_rmut.ty)
 +            },
 +            (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r),
 +            (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)),
 +            (&TyKind::Infer, &TyKind::Infer) => true,
 +            _ => false,
 +        }
 +    }
 +
 +    fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool {
 +        left.ident.name == right.ident.name && self.eq_ty(left.ty(), right.ty())
 +    }
 +}
 +
 +/// Some simple reductions like `{ return }` => `return`
 +fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'hir ExprKind<'hir> {
 +    if let ExprKind::Block(block, _) = kind {
 +        match (block.stmts, block.expr) {
 +            // From an `if let` expression without an `else` block. The arm for the implicit wild pattern is an empty
 +            // block with an empty span.
 +            ([], None) if block.span.is_empty() => &ExprKind::Tup(&[]),
 +            // `{}` => `()`
 +            ([], None) => match snippet_opt(cx, block.span) {
 +                // Don't reduce if there are any tokens contained in the braces
 +                Some(snip)
 +                    if tokenize(&snip)
 +                        .map(|t| t.kind)
 +                        .filter(|t| {
 +                            !matches!(
 +                                t,
 +                                TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
 +                            )
 +                        })
 +                        .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) =>
 +                {
 +                    kind
 +                },
 +                _ => &ExprKind::Tup(&[]),
 +            },
 +            ([], Some(expr)) => match expr.kind {
 +                // `{ return .. }` => `return ..`
 +                ExprKind::Ret(..) => &expr.kind,
 +                _ => kind,
 +            },
 +            ([stmt], None) => match stmt.kind {
 +                StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind {
 +                    // `{ return ..; }` => `return ..`
 +                    ExprKind::Ret(..) => &expr.kind,
 +                    _ => kind,
 +                },
 +                _ => kind,
 +            },
 +            _ => kind,
 +        }
 +    } else {
 +        kind
 +    }
 +}
 +
 +fn swap_binop<'a>(
 +    binop: BinOpKind,
 +    lhs: &'a Expr<'a>,
 +    rhs: &'a Expr<'a>,
 +) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> {
 +    match binop {
 +        BinOpKind::Add | BinOpKind::Eq | BinOpKind::Ne | BinOpKind::BitAnd | BinOpKind::BitXor | BinOpKind::BitOr => {
 +            Some((binop, rhs, lhs))
 +        },
 +        BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)),
 +        BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)),
 +        BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)),
 +        BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)),
 +        BinOpKind::Mul // Not always commutative, e.g. with matrices. See issue #5698
 +        | BinOpKind::Shl
 +        | BinOpKind::Shr
 +        | BinOpKind::Rem
 +        | BinOpKind::Sub
 +        | BinOpKind::Div
 +        | BinOpKind::And
 +        | BinOpKind::Or => None,
 +    }
 +}
 +
 +/// Checks if the two `Option`s are both `None` or some equal values as per
 +/// `eq_fn`.
 +pub fn both<X>(l: &Option<X>, r: &Option<X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
 +    l.as_ref()
 +        .map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y)))
 +}
 +
 +/// Checks if two slices are equal as per `eq_fn`.
 +pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
 +    left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
 +}
 +
 +/// Counts how many elements of the slices are equal as per `eq_fn`.
 +pub fn count_eq<X: Sized>(
 +    left: &mut dyn Iterator<Item = X>,
 +    right: &mut dyn Iterator<Item = X>,
 +    mut eq_fn: impl FnMut(&X, &X) -> bool,
 +) -> usize {
 +    left.zip(right).take_while(|(l, r)| eq_fn(l, r)).count()
 +}
 +
 +/// Checks if two expressions evaluate to the same value, and don't contain any side effects.
 +pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +    SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right)
 +}
 +
 +/// Type used to hash an ast element. This is different from the `Hash` trait
 +/// on ast types as this
 +/// trait would consider IDs and spans.
 +///
 +/// All expressions kind are hashed, but some might have a weaker hash.
 +pub struct SpanlessHash<'a, 'tcx> {
 +    /// Context used to evaluate constant expressions.
 +    cx: &'a LateContext<'tcx>,
 +    maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
 +    s: FxHasher,
 +}
 +
 +impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
 +    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            cx,
 +            maybe_typeck_results: cx.maybe_typeck_results(),
 +            s: FxHasher::default(),
 +        }
 +    }
 +
 +    pub fn finish(self) -> u64 {
 +        self.s.finish()
 +    }
 +
 +    pub fn hash_block(&mut self, b: &Block<'_>) {
 +        for s in b.stmts {
 +            self.hash_stmt(s);
 +        }
 +
 +        if let Some(e) = b.expr {
 +            self.hash_expr(e);
 +        }
 +
 +        std::mem::discriminant(&b.rules).hash(&mut self.s);
 +    }
 +
 +    #[allow(clippy::too_many_lines)]
 +    pub fn hash_expr(&mut self, e: &Expr<'_>) {
 +        let simple_const = self
 +            .maybe_typeck_results
 +            .and_then(|typeck_results| constant_simple(self.cx, typeck_results, e));
 +
 +        // const hashing may result in the same hash as some unrelated node, so add a sort of
 +        // discriminant depending on which path we're choosing next
 +        simple_const.hash(&mut self.s);
 +        if simple_const.is_some() {
 +            return;
 +        }
 +
 +        std::mem::discriminant(&e.kind).hash(&mut self.s);
 +
 +        match e.kind {
 +            ExprKind::AddrOf(kind, m, e) => {
 +                std::mem::discriminant(&kind).hash(&mut self.s);
 +                m.hash(&mut self.s);
 +                self.hash_expr(e);
 +            },
 +            ExprKind::Continue(i) => {
 +                if let Some(i) = i.label {
 +                    self.hash_name(i.ident.name);
 +                }
 +            },
 +            ExprKind::Assign(l, r, _) => {
 +                self.hash_expr(l);
 +                self.hash_expr(r);
 +            },
 +            ExprKind::AssignOp(ref o, l, r) => {
 +                std::mem::discriminant(&o.node).hash(&mut self.s);
 +                self.hash_expr(l);
 +                self.hash_expr(r);
 +            },
 +            ExprKind::Block(b, _) => {
 +                self.hash_block(b);
 +            },
 +            ExprKind::Binary(op, l, r) => {
 +                std::mem::discriminant(&op.node).hash(&mut self.s);
 +                self.hash_expr(l);
 +                self.hash_expr(r);
 +            },
 +            ExprKind::Break(i, ref j) => {
 +                if let Some(i) = i.label {
 +                    self.hash_name(i.ident.name);
 +                }
 +                if let Some(j) = *j {
 +                    self.hash_expr(&*j);
 +                }
 +            },
 +            ExprKind::Box(e) | ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => {
 +                self.hash_expr(e);
 +            },
 +            ExprKind::Call(fun, args) => {
 +                self.hash_expr(fun);
 +                self.hash_exprs(args);
 +            },
 +            ExprKind::Cast(e, ty) | ExprKind::Type(e, ty) => {
 +                self.hash_expr(e);
 +                self.hash_ty(ty);
 +            },
 +            ExprKind::Closure(cap, _, eid, _, _) => {
 +                std::mem::discriminant(&cap).hash(&mut self.s);
 +                // closures inherit TypeckResults
 +                self.hash_expr(&self.cx.tcx.hir().body(eid).value);
 +            },
 +            ExprKind::Field(e, ref f) => {
 +                self.hash_expr(e);
 +                self.hash_name(f.name);
 +            },
 +            ExprKind::Index(a, i) => {
 +                self.hash_expr(a);
 +                self.hash_expr(i);
 +            },
 +            ExprKind::InlineAsm(asm) => {
 +                for piece in asm.template {
 +                    match piece {
 +                        InlineAsmTemplatePiece::String(s) => s.hash(&mut self.s),
 +                        InlineAsmTemplatePiece::Placeholder {
 +                            operand_idx,
 +                            modifier,
 +                            span: _,
 +                        } => {
 +                            operand_idx.hash(&mut self.s);
 +                            modifier.hash(&mut self.s);
 +                        },
 +                    }
 +                }
 +                asm.options.hash(&mut self.s);
 +                for (op, _op_sp) in asm.operands {
 +                    match op {
 +                        InlineAsmOperand::In { reg, expr } => {
 +                            reg.hash(&mut self.s);
 +                            self.hash_expr(expr);
 +                        },
 +                        InlineAsmOperand::Out { reg, late, expr } => {
 +                            reg.hash(&mut self.s);
 +                            late.hash(&mut self.s);
 +                            if let Some(expr) = expr {
 +                                self.hash_expr(expr);
 +                            }
 +                        },
 +                        InlineAsmOperand::InOut { reg, late, expr } => {
 +                            reg.hash(&mut self.s);
 +                            late.hash(&mut self.s);
 +                            self.hash_expr(expr);
 +                        },
 +                        InlineAsmOperand::SplitInOut {
 +                            reg,
 +                            late,
 +                            in_expr,
 +                            out_expr,
 +                        } => {
 +                            reg.hash(&mut self.s);
 +                            late.hash(&mut self.s);
 +                            self.hash_expr(in_expr);
 +                            if let Some(out_expr) = out_expr {
 +                                self.hash_expr(out_expr);
 +                            }
 +                        },
-                         InlineAsmOperand::Const { anon_const } => self.hash_body(anon_const.body),
-                         InlineAsmOperand::SymFn { anon_const } => self.hash_body(anon_const.body),
++                        InlineAsmOperand::Const { anon_const } | InlineAsmOperand::SymFn { anon_const } => {
++                            self.hash_body(anon_const.body);
++                        },
 +                        InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path),
 +                    }
 +                }
 +            },
 +            ExprKind::Let(Let { pat, init, ty, .. }) => {
 +                self.hash_expr(init);
 +                if let Some(ty) = ty {
 +                    self.hash_ty(ty);
 +                }
 +                self.hash_pat(pat);
 +            },
 +            ExprKind::Err => {},
 +            ExprKind::Lit(ref l) => {
 +                l.node.hash(&mut self.s);
 +            },
 +            ExprKind::Loop(b, ref i, ..) => {
 +                self.hash_block(b);
 +                if let Some(i) = *i {
 +                    self.hash_name(i.ident.name);
 +                }
 +            },
 +            ExprKind::If(cond, then, ref else_opt) => {
 +                self.hash_expr(cond);
 +                self.hash_expr(then);
 +                if let Some(e) = *else_opt {
 +                    self.hash_expr(e);
 +                }
 +            },
 +            ExprKind::Match(e, arms, ref s) => {
 +                self.hash_expr(e);
 +
 +                for arm in arms {
 +                    self.hash_pat(arm.pat);
 +                    if let Some(ref e) = arm.guard {
 +                        self.hash_guard(e);
 +                    }
 +                    self.hash_expr(arm.body);
 +                }
 +
 +                s.hash(&mut self.s);
 +            },
 +            ExprKind::MethodCall(path, args, ref _fn_span) => {
 +                self.hash_name(path.ident.name);
 +                self.hash_exprs(args);
 +            },
 +            ExprKind::ConstBlock(ref l_id) => {
 +                self.hash_body(l_id.body);
 +            },
 +            ExprKind::Repeat(e, len) => {
 +                self.hash_expr(e);
 +                self.hash_array_length(len);
 +            },
 +            ExprKind::Ret(ref e) => {
 +                if let Some(e) = *e {
 +                    self.hash_expr(e);
 +                }
 +            },
 +            ExprKind::Path(ref qpath) => {
 +                self.hash_qpath(qpath);
 +            },
 +            ExprKind::Struct(path, fields, ref expr) => {
 +                self.hash_qpath(path);
 +
 +                for f in fields {
 +                    self.hash_name(f.ident.name);
 +                    self.hash_expr(f.expr);
 +                }
 +
 +                if let Some(e) = *expr {
 +                    self.hash_expr(e);
 +                }
 +            },
 +            ExprKind::Tup(tup) => {
 +                self.hash_exprs(tup);
 +            },
 +            ExprKind::Array(v) => {
 +                self.hash_exprs(v);
 +            },
 +            ExprKind::Unary(lop, le) => {
 +                std::mem::discriminant(&lop).hash(&mut self.s);
 +                self.hash_expr(le);
 +            },
 +        }
 +    }
 +
 +    pub fn hash_exprs(&mut self, e: &[Expr<'_>]) {
 +        for e in e {
 +            self.hash_expr(e);
 +        }
 +    }
 +
 +    pub fn hash_name(&mut self, n: Symbol) {
 +        n.hash(&mut self.s);
 +    }
 +
 +    pub fn hash_qpath(&mut self, p: &QPath<'_>) {
 +        match *p {
 +            QPath::Resolved(_, path) => {
 +                self.hash_path(path);
 +            },
 +            QPath::TypeRelative(_, path) => {
 +                self.hash_name(path.ident.name);
 +            },
 +            QPath::LangItem(lang_item, ..) => {
 +                std::mem::discriminant(&lang_item).hash(&mut self.s);
 +            },
 +        }
 +        // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
 +    }
 +
 +    pub fn hash_pat(&mut self, pat: &Pat<'_>) {
 +        std::mem::discriminant(&pat.kind).hash(&mut self.s);
 +        match pat.kind {
 +            PatKind::Binding(ann, _, _, pat) => {
 +                std::mem::discriminant(&ann).hash(&mut self.s);
 +                if let Some(pat) = pat {
 +                    self.hash_pat(pat);
 +                }
 +            },
 +            PatKind::Box(pat) => self.hash_pat(pat),
 +            PatKind::Lit(expr) => self.hash_expr(expr),
 +            PatKind::Or(pats) => {
 +                for pat in pats {
 +                    self.hash_pat(pat);
 +                }
 +            },
 +            PatKind::Path(ref qpath) => self.hash_qpath(qpath),
 +            PatKind::Range(s, e, i) => {
 +                if let Some(s) = s {
 +                    self.hash_expr(s);
 +                }
 +                if let Some(e) = e {
 +                    self.hash_expr(e);
 +                }
 +                std::mem::discriminant(&i).hash(&mut self.s);
 +            },
 +            PatKind::Ref(pat, mu) => {
 +                self.hash_pat(pat);
 +                std::mem::discriminant(&mu).hash(&mut self.s);
 +            },
 +            PatKind::Slice(l, m, r) => {
 +                for pat in l {
 +                    self.hash_pat(pat);
 +                }
 +                if let Some(pat) = m {
 +                    self.hash_pat(pat);
 +                }
 +                for pat in r {
 +                    self.hash_pat(pat);
 +                }
 +            },
 +            PatKind::Struct(ref qpath, fields, e) => {
 +                self.hash_qpath(qpath);
 +                for f in fields {
 +                    self.hash_name(f.ident.name);
 +                    self.hash_pat(f.pat);
 +                }
 +                e.hash(&mut self.s);
 +            },
 +            PatKind::Tuple(pats, e) => {
 +                for pat in pats {
 +                    self.hash_pat(pat);
 +                }
 +                e.hash(&mut self.s);
 +            },
 +            PatKind::TupleStruct(ref qpath, pats, e) => {
 +                self.hash_qpath(qpath);
 +                for pat in pats {
 +                    self.hash_pat(pat);
 +                }
 +                e.hash(&mut self.s);
 +            },
 +            PatKind::Wild => {},
 +        }
 +    }
 +
 +    pub fn hash_path(&mut self, path: &Path<'_>) {
 +        match path.res {
 +            // constant hash since equality is dependant on inter-expression context
 +            // e.g. The expressions `if let Some(x) = foo() {}` and `if let Some(y) = foo() {}` are considered equal
 +            // even though the binding names are different and they have different `HirId`s.
 +            Res::Local(_) => 1_usize.hash(&mut self.s),
 +            _ => {
 +                for seg in path.segments {
 +                    self.hash_name(seg.ident.name);
 +                    self.hash_generic_args(seg.args().args);
 +                }
 +            },
 +        }
 +    }
 +
 +    pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
 +        std::mem::discriminant(&b.kind).hash(&mut self.s);
 +
 +        match &b.kind {
 +            StmtKind::Local(local) => {
 +                self.hash_pat(local.pat);
 +                if let Some(init) = local.init {
 +                    self.hash_expr(init);
 +                }
 +            },
 +            StmtKind::Item(..) => {},
 +            StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
 +                self.hash_expr(expr);
 +            },
 +        }
 +    }
 +
 +    pub fn hash_guard(&mut self, g: &Guard<'_>) {
 +        match g {
 +            Guard::If(expr) | Guard::IfLet(_, expr) => {
 +                self.hash_expr(expr);
 +            },
 +        }
 +    }
 +
 +    pub fn hash_lifetime(&mut self, lifetime: Lifetime) {
 +        std::mem::discriminant(&lifetime.name).hash(&mut self.s);
 +        if let LifetimeName::Param(ref name) = lifetime.name {
 +            std::mem::discriminant(name).hash(&mut self.s);
 +            match name {
 +                ParamName::Plain(ref ident) => {
 +                    ident.name.hash(&mut self.s);
 +                },
 +                ParamName::Fresh(ref size) => {
 +                    size.hash(&mut self.s);
 +                },
 +                ParamName::Error => {},
 +            }
 +        }
 +    }
 +
 +    pub fn hash_ty(&mut self, ty: &Ty<'_>) {
 +        std::mem::discriminant(&ty.kind).hash(&mut self.s);
 +        self.hash_tykind(&ty.kind);
 +    }
 +
 +    pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
 +        match ty {
 +            TyKind::Slice(ty) => {
 +                self.hash_ty(ty);
 +            },
 +            &TyKind::Array(ty, len) => {
 +                self.hash_ty(ty);
 +                self.hash_array_length(len);
 +            },
 +            TyKind::Ptr(ref mut_ty) => {
 +                self.hash_ty(mut_ty.ty);
 +                mut_ty.mutbl.hash(&mut self.s);
 +            },
 +            TyKind::Rptr(lifetime, ref mut_ty) => {
 +                self.hash_lifetime(*lifetime);
 +                self.hash_ty(mut_ty.ty);
 +                mut_ty.mutbl.hash(&mut self.s);
 +            },
 +            TyKind::BareFn(bfn) => {
 +                bfn.unsafety.hash(&mut self.s);
 +                bfn.abi.hash(&mut self.s);
 +                for arg in bfn.decl.inputs {
 +                    self.hash_ty(arg);
 +                }
 +                std::mem::discriminant(&bfn.decl.output).hash(&mut self.s);
 +                match bfn.decl.output {
 +                    FnRetTy::DefaultReturn(_) => {},
 +                    FnRetTy::Return(ty) => {
 +                        self.hash_ty(ty);
 +                    },
 +                }
 +                bfn.decl.c_variadic.hash(&mut self.s);
 +            },
 +            TyKind::Tup(ty_list) => {
 +                for ty in *ty_list {
 +                    self.hash_ty(ty);
 +                }
 +            },
 +            TyKind::Path(ref qpath) => self.hash_qpath(qpath),
 +            TyKind::OpaqueDef(_, arg_list) => {
 +                self.hash_generic_args(arg_list);
 +            },
 +            TyKind::TraitObject(_, lifetime, _) => {
 +                self.hash_lifetime(*lifetime);
 +            },
 +            TyKind::Typeof(anon_const) => {
 +                self.hash_body(anon_const.body);
 +            },
 +            TyKind::Err | TyKind::Infer | TyKind::Never => {},
 +        }
 +    }
 +
 +    pub fn hash_array_length(&mut self, length: ArrayLen) {
 +        match length {
 +            ArrayLen::Infer(..) => {},
 +            ArrayLen::Body(anon_const) => self.hash_body(anon_const.body),
 +        }
 +    }
 +
 +    pub fn hash_body(&mut self, body_id: BodyId) {
 +        // swap out TypeckResults when hashing a body
 +        let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body_id));
 +        self.hash_expr(&self.cx.tcx.hir().body(body_id).value);
 +        self.maybe_typeck_results = old_maybe_typeck_results;
 +    }
 +
 +    fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) {
 +        for arg in arg_list {
 +            match *arg {
 +                GenericArg::Lifetime(l) => self.hash_lifetime(l),
 +                GenericArg::Type(ref ty) => self.hash_ty(ty),
 +                GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
 +                GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()),
 +            }
 +        }
 +    }
 +}
index e7d4c5a49521d9ae21cec7b51e7512a5606630c0,0000000000000000000000000000000000000000..a268e339bb130df99138102b739e6def1d44c640
mode 100644,000000..100644
--- /dev/null
@@@ -1,583 -1,0 +1,583 @@@
-                 // ArgumnetV1::new_<format_trait>(<value>)
 +#![allow(clippy::similar_names)] // `expr` and `expn`
 +
 +use crate::visitors::expr_visitor_no_bodies;
 +
 +use arrayvec::ArrayVec;
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
 +use rustc_hir::intravisit::Visitor;
 +use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
 +use rustc_lint::LateContext;
 +use rustc_span::def_id::DefId;
 +use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
 +use rustc_span::{sym, ExpnData, ExpnId, ExpnKind, Span, Symbol};
 +use std::ops::ControlFlow;
 +
 +const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
 +    sym::assert_eq_macro,
 +    sym::assert_macro,
 +    sym::assert_ne_macro,
 +    sym::debug_assert_eq_macro,
 +    sym::debug_assert_macro,
 +    sym::debug_assert_ne_macro,
 +    sym::eprint_macro,
 +    sym::eprintln_macro,
 +    sym::format_args_macro,
 +    sym::format_macro,
 +    sym::print_macro,
 +    sym::println_macro,
 +    sym::std_panic_macro,
 +    sym::write_macro,
 +    sym::writeln_macro,
 +];
 +
 +/// Returns true if a given Macro `DefId` is a format macro (e.g. `println!`)
 +pub fn is_format_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool {
 +    if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) {
 +        FORMAT_MACRO_DIAG_ITEMS.contains(&name)
 +    } else {
 +        false
 +    }
 +}
 +
 +/// A macro call, like `vec![1, 2, 3]`.
 +///
 +/// Use `tcx.item_name(macro_call.def_id)` to get the macro name.
 +/// Even better is to check if it is a diagnostic item.
 +///
 +/// This structure is similar to `ExpnData` but it precludes desugaring expansions.
 +#[derive(Debug)]
 +pub struct MacroCall {
 +    /// Macro `DefId`
 +    pub def_id: DefId,
 +    /// Kind of macro
 +    pub kind: MacroKind,
 +    /// The expansion produced by the macro call
 +    pub expn: ExpnId,
 +    /// Span of the macro call site
 +    pub span: Span,
 +}
 +
 +impl MacroCall {
 +    pub fn is_local(&self) -> bool {
 +        span_is_local(self.span)
 +    }
 +}
 +
 +/// Returns an iterator of expansions that created the given span
 +pub fn expn_backtrace(mut span: Span) -> impl Iterator<Item = (ExpnId, ExpnData)> {
 +    std::iter::from_fn(move || {
 +        let ctxt = span.ctxt();
 +        if ctxt == SyntaxContext::root() {
 +            return None;
 +        }
 +        let expn = ctxt.outer_expn();
 +        let data = expn.expn_data();
 +        span = data.call_site;
 +        Some((expn, data))
 +    })
 +}
 +
 +/// Checks whether the span is from the root expansion or a locally defined macro
 +pub fn span_is_local(span: Span) -> bool {
 +    !span.from_expansion() || expn_is_local(span.ctxt().outer_expn())
 +}
 +
 +/// Checks whether the expansion is the root expansion or a locally defined macro
 +pub fn expn_is_local(expn: ExpnId) -> bool {
 +    if expn == ExpnId::root() {
 +        return true;
 +    }
 +    let data = expn.expn_data();
 +    let backtrace = expn_backtrace(data.call_site);
 +    std::iter::once((expn, data))
 +        .chain(backtrace)
 +        .find_map(|(_, data)| data.macro_def_id)
 +        .map_or(true, DefId::is_local)
 +}
 +
 +/// Returns an iterator of macro expansions that created the given span.
 +/// Note that desugaring expansions are skipped.
 +pub fn macro_backtrace(span: Span) -> impl Iterator<Item = MacroCall> {
 +    expn_backtrace(span).filter_map(|(expn, data)| match data {
 +        ExpnData {
 +            kind: ExpnKind::Macro(kind, _),
 +            macro_def_id: Some(def_id),
 +            call_site: span,
 +            ..
 +        } => Some(MacroCall {
 +            def_id,
 +            kind,
 +            expn,
 +            span,
 +        }),
 +        _ => None,
 +    })
 +}
 +
 +/// If the macro backtrace of `span` has a macro call at the root expansion
 +/// (i.e. not a nested macro call), returns `Some` with the `MacroCall`
 +pub fn root_macro_call(span: Span) -> Option<MacroCall> {
 +    macro_backtrace(span).last()
 +}
 +
 +/// Like [`root_macro_call`], but only returns `Some` if `node` is the "first node"
 +/// produced by the macro call, as in [`first_node_in_macro`].
 +pub fn root_macro_call_first_node(cx: &LateContext<'_>, node: &impl HirNode) -> Option<MacroCall> {
 +    if first_node_in_macro(cx, node) != Some(ExpnId::root()) {
 +        return None;
 +    }
 +    root_macro_call(node.span())
 +}
 +
 +/// Like [`macro_backtrace`], but only returns macro calls where `node` is the "first node" of the
 +/// macro call, as in [`first_node_in_macro`].
 +pub fn first_node_macro_backtrace(cx: &LateContext<'_>, node: &impl HirNode) -> impl Iterator<Item = MacroCall> {
 +    let span = node.span();
 +    first_node_in_macro(cx, node)
 +        .into_iter()
 +        .flat_map(move |expn| macro_backtrace(span).take_while(move |macro_call| macro_call.expn != expn))
 +}
 +
 +/// If `node` is the "first node" in a macro expansion, returns `Some` with the `ExpnId` of the
 +/// macro call site (i.e. the parent of the macro expansion). This generally means that `node`
 +/// is the outermost node of an entire macro expansion, but there are some caveats noted below.
 +/// This is useful for finding macro calls while visiting the HIR without processing the macro call
 +/// at every node within its expansion.
 +///
 +/// If you already have immediate access to the parent node, it is simpler to
 +/// just check the context of that span directly (e.g. `parent.span.from_expansion()`).
 +///
 +/// If a macro call is in statement position, it expands to one or more statements.
 +/// In that case, each statement *and* their immediate descendants will all yield `Some`
 +/// with the `ExpnId` of the containing block.
 +///
 +/// A node may be the "first node" of multiple macro calls in a macro backtrace.
 +/// The expansion of the outermost macro call site is returned in such cases.
 +pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option<ExpnId> {
 +    // get the macro expansion or return `None` if not found
 +    // `macro_backtrace` importantly ignores desugaring expansions
 +    let expn = macro_backtrace(node.span()).next()?.expn;
 +
 +    // get the parent node, possibly skipping over a statement
 +    // if the parent is not found, it is sensible to return `Some(root)`
 +    let hir = cx.tcx.hir();
 +    let mut parent_iter = hir.parent_iter(node.hir_id());
 +    let (parent_id, _) = match parent_iter.next() {
 +        None => return Some(ExpnId::root()),
 +        Some((_, Node::Stmt(_))) => match parent_iter.next() {
 +            None => return Some(ExpnId::root()),
 +            Some(next) => next,
 +        },
 +        Some(next) => next,
 +    };
 +
 +    // get the macro expansion of the parent node
 +    let parent_span = hir.span(parent_id);
 +    let Some(parent_macro_call) = macro_backtrace(parent_span).next() else {
 +        // the parent node is not in a macro
 +        return Some(ExpnId::root());
 +    };
 +
 +    if parent_macro_call.expn.is_descendant_of(expn) {
 +        // `node` is input to a macro call
 +        return None;
 +    }
 +
 +    Some(parent_macro_call.expn)
 +}
 +
 +/* Specific Macro Utils */
 +
 +/// Is `def_id` of `std::panic`, `core::panic` or any inner implementation macros
 +pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false };
 +    matches!(
 +        name.as_str(),
 +        "core_panic_macro"
 +            | "std_panic_macro"
 +            | "core_panic_2015_macro"
 +            | "std_panic_2015_macro"
 +            | "core_panic_2021_macro"
 +    )
 +}
 +
 +pub enum PanicExpn<'a> {
 +    /// No arguments - `panic!()`
 +    Empty,
 +    /// A string literal or any `&str` - `panic!("message")` or `panic!(message)`
 +    Str(&'a Expr<'a>),
 +    /// A single argument that implements `Display` - `panic!("{}", object)`
 +    Display(&'a Expr<'a>),
 +    /// Anything else - `panic!("error {}: {}", a, b)`
 +    Format(FormatArgsExpn<'a>),
 +}
 +
 +impl<'a> PanicExpn<'a> {
 +    pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Self> {
 +        if !macro_backtrace(expr.span).any(|macro_call| is_panic(cx, macro_call.def_id)) {
 +            return None;
 +        }
 +        let ExprKind::Call(callee, [arg]) = &expr.kind else { return None };
 +        let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None };
 +        let result = match path.segments.last().unwrap().ident.as_str() {
 +            "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
 +            "panic" | "panic_str" => Self::Str(arg),
 +            "panic_display" => {
 +                let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None };
 +                Self::Display(e)
 +            },
 +            "panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?),
 +            _ => return None,
 +        };
 +        Some(result)
 +    }
 +}
 +
 +/// Finds the arguments of an `assert!` or `debug_assert!` macro call within the macro expansion
 +pub fn find_assert_args<'a>(
 +    cx: &LateContext<'_>,
 +    expr: &'a Expr<'a>,
 +    expn: ExpnId,
 +) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> {
 +    find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p))
 +}
 +
 +/// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro
 +/// expansion
 +pub fn find_assert_eq_args<'a>(
 +    cx: &LateContext<'_>,
 +    expr: &'a Expr<'a>,
 +    expn: ExpnId,
 +) -> Option<(&'a Expr<'a>, &'a Expr<'a>, PanicExpn<'a>)> {
 +    find_assert_args_inner(cx, expr, expn).map(|([a, b], p)| (a, b, p))
 +}
 +
 +fn find_assert_args_inner<'a, const N: usize>(
 +    cx: &LateContext<'_>,
 +    expr: &'a Expr<'a>,
 +    expn: ExpnId,
 +) -> Option<([&'a Expr<'a>; N], PanicExpn<'a>)> {
 +    let macro_id = expn.expn_data().macro_def_id?;
 +    let (expr, expn) = match cx.tcx.item_name(macro_id).as_str().strip_prefix("debug_") {
 +        None => (expr, expn),
 +        Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?,
 +    };
 +    let mut args = ArrayVec::new();
 +    let mut panic_expn = None;
 +    expr_visitor_no_bodies(|e| {
 +        if args.is_full() {
 +            if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() {
 +                panic_expn = PanicExpn::parse(cx, e);
 +            }
 +            panic_expn.is_none()
 +        } else if is_assert_arg(cx, e, expn) {
 +            args.push(e);
 +            false
 +        } else {
 +            true
 +        }
 +    })
 +    .visit_expr(expr);
 +    let args = args.into_inner().ok()?;
 +    // if no `panic!(..)` is found, use `PanicExpn::Empty`
 +    // to indicate that the default assertion message is used
 +    let panic_expn = panic_expn.unwrap_or(PanicExpn::Empty);
 +    Some((args, panic_expn))
 +}
 +
 +fn find_assert_within_debug_assert<'a>(
 +    cx: &LateContext<'_>,
 +    expr: &'a Expr<'a>,
 +    expn: ExpnId,
 +    assert_name: Symbol,
 +) -> Option<(&'a Expr<'a>, ExpnId)> {
 +    let mut found = None;
 +    expr_visitor_no_bodies(|e| {
 +        if found.is_some() || !e.span.from_expansion() {
 +            return false;
 +        }
 +        let e_expn = e.span.ctxt().outer_expn();
 +        if e_expn == expn {
 +            return true;
 +        }
 +        if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) {
 +            found = Some((e, e_expn));
 +        }
 +        false
 +    })
 +    .visit_expr(expr);
 +    found
 +}
 +
 +fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> bool {
 +    if !expr.span.from_expansion() {
 +        return true;
 +    }
 +    let result = macro_backtrace(expr.span).try_for_each(|macro_call| {
 +        if macro_call.expn == assert_expn {
 +            ControlFlow::Break(false)
 +        } else {
 +            match cx.tcx.item_name(macro_call.def_id) {
 +                // `cfg!(debug_assertions)` in `debug_assert!`
 +                sym::cfg => ControlFlow::CONTINUE,
 +                // assert!(other_macro!(..))
 +                _ => ControlFlow::Break(true),
 +            }
 +        }
 +    });
 +    match result {
 +        ControlFlow::Break(is_assert_arg) => is_assert_arg,
 +        ControlFlow::Continue(()) => true,
 +    }
 +}
 +
 +/// A parsed `format_args!` expansion
 +#[derive(Debug)]
 +pub struct FormatArgsExpn<'tcx> {
 +    /// Span of the first argument, the format string
 +    pub format_string_span: Span,
 +    /// The format string split by formatted args like `{..}`
 +    pub format_string_parts: Vec<Symbol>,
 +    /// Values passed after the format string
 +    pub value_args: Vec<&'tcx Expr<'tcx>>,
 +    /// Each element is a `value_args` index and a formatting trait (e.g. `sym::Debug`)
 +    pub formatters: Vec<(usize, Symbol)>,
 +    /// List of `fmt::v1::Argument { .. }` expressions. If this is empty,
 +    /// then `formatters` represents the format args (`{..}`).
 +    /// If this is non-empty, it represents the format args, and the `position`
 +    /// parameters within the struct expressions are indexes of `formatters`.
 +    pub specs: Vec<&'tcx Expr<'tcx>>,
 +}
 +
 +impl<'tcx> FormatArgsExpn<'tcx> {
 +    /// Parses an expanded `format_args!` or `format_args_nl!` invocation
 +    pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
 +        macro_backtrace(expr.span).find(|macro_call| {
 +            matches!(
 +                cx.tcx.item_name(macro_call.def_id),
 +                sym::const_format_args | sym::format_args | sym::format_args_nl
 +            )
 +        })?;
 +        let mut format_string_span: Option<Span> = None;
 +        let mut format_string_parts: Vec<Symbol> = Vec::new();
 +        let mut value_args: Vec<&Expr<'_>> = Vec::new();
 +        let mut formatters: Vec<(usize, Symbol)> = Vec::new();
 +        let mut specs: Vec<&Expr<'_>> = Vec::new();
 +        expr_visitor_no_bodies(|e| {
 +            // if we're still inside of the macro definition...
 +            if e.span.ctxt() == expr.span.ctxt() {
++                // 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);
 +                    }
 +                }
 +                // walk through the macro expansion
 +                return true;
 +            }
 +            // assume that the first expr with a differing context represents
 +            // (and has the span of) the format string
 +            if format_string_span.is_none() {
 +                format_string_span = Some(e.span);
 +                let span = e.span;
 +                // walk the expr and collect string literals which are format string parts
 +                expr_visitor_no_bodies(|e| {
 +                    if e.span.ctxt() != span.ctxt() {
 +                        // defensive check, probably doesn't happen
 +                        return false;
 +                    }
 +                    if let ExprKind::Lit(lit) = &e.kind {
 +                        if let LitKind::Str(symbol, _s) = lit.node {
 +                            format_string_parts.push(symbol);
 +                        }
 +                    }
 +                    true
 +                })
 +                .visit_expr(e);
 +            } else {
 +                // assume that any further exprs with a differing context are value args
 +                value_args.push(e);
 +            }
 +            // don't walk anything not from the macro expansion (e.a. inputs)
 +            false
 +        })
 +        .visit_expr(expr);
 +        Some(FormatArgsExpn {
 +            format_string_span: format_string_span?,
 +            format_string_parts,
 +            value_args,
 +            formatters,
 +            specs,
 +        })
 +    }
 +
 +    /// Finds a nested call to `format_args!` within a `format!`-like macro call
 +    pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> {
 +        let mut format_args = None;
 +        expr_visitor_no_bodies(|e| {
 +            if format_args.is_some() {
 +                return false;
 +            }
 +            let e_ctxt = e.span.ctxt();
 +            if e_ctxt == expr.span.ctxt() {
 +                return true;
 +            }
 +            if e_ctxt.outer_expn().is_descendant_of(expn_id) {
 +                format_args = FormatArgsExpn::parse(cx, e);
 +            }
 +            false
 +        })
 +        .visit_expr(expr);
 +        format_args
 +    }
 +
 +    /// Returns a vector of `FormatArgsArg`.
 +    pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> {
 +        if self.specs.is_empty() {
 +            let args = std::iter::zip(&self.value_args, &self.formatters)
 +                .map(|(value, &(_, format_trait))| FormatArgsArg {
 +                    value,
 +                    format_trait,
 +                    spec: None,
 +                })
 +                .collect();
 +            return Some(args);
 +        }
 +        self.specs
 +            .iter()
 +            .map(|spec| {
 +                if_chain! {
 +                    // struct `core::fmt::rt::v1::Argument`
 +                    if let ExprKind::Struct(_, fields, _) = spec.kind;
 +                    if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position);
 +                    if let ExprKind::Lit(lit) = &position_field.expr.kind;
 +                    if let LitKind::Int(position, _) = lit.node;
 +                    if let Ok(i) = usize::try_from(position);
 +                    if let Some(&(j, format_trait)) = self.formatters.get(i);
 +                    then {
 +                        Some(FormatArgsArg {
 +                            value: self.value_args[j],
 +                            format_trait,
 +                            spec: Some(spec),
 +                        })
 +                    } else {
 +                        None
 +                    }
 +                }
 +            })
 +            .collect()
 +    }
 +
 +    /// Source callsite span of all inputs
 +    pub fn inputs_span(&self) -> Span {
 +        match *self.value_args {
 +            [] => self.format_string_span,
 +            [.., last] => self
 +                .format_string_span
 +                .to(hygiene::walk_chain(last.span, self.format_string_span.ctxt())),
 +        }
 +    }
 +}
 +
 +/// Type representing a `FormatArgsExpn`'s format arguments
 +pub struct FormatArgsArg<'tcx> {
 +    /// An element of `value_args` according to `position`
 +    pub value: &'tcx Expr<'tcx>,
 +    /// An element of `args` according to `position`
 +    pub format_trait: Symbol,
 +    /// An element of `specs`
 +    pub spec: Option<&'tcx Expr<'tcx>>,
 +}
 +
 +impl<'tcx> FormatArgsArg<'tcx> {
 +    /// Returns true if any formatting parameters are used that would have an effect on strings,
 +    /// like `{:+2}` instead of just `{}`.
 +    pub fn has_string_formatting(&self) -> bool {
 +        self.spec.map_or(false, |spec| {
 +            // `!` because these conditions check that `self` is unformatted.
 +            !if_chain! {
 +                // struct `core::fmt::rt::v1::Argument`
 +                if let ExprKind::Struct(_, fields, _) = spec.kind;
 +                if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
 +                // struct `core::fmt::rt::v1::FormatSpec`
 +                if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind;
 +                if subfields.iter().all(|field| match field.ident.name {
 +                    sym::precision | sym::width => match field.expr.kind {
 +                        ExprKind::Path(QPath::Resolved(_, path)) => {
 +                            path.segments.last().unwrap().ident.name == sym::Implied
 +                        }
 +                        _ => false,
 +                    }
 +                    _ => true,
 +                });
 +                then { true } else { false }
 +            }
 +        })
 +    }
 +}
 +
 +/// A node with a `HirId` and a `Span`
 +pub trait HirNode {
 +    fn hir_id(&self) -> HirId;
 +    fn span(&self) -> Span;
 +}
 +
 +macro_rules! impl_hir_node {
 +    ($($t:ident),*) => {
 +        $(impl HirNode for hir::$t<'_> {
 +            fn hir_id(&self) -> HirId {
 +                self.hir_id
 +            }
 +            fn span(&self) -> Span {
 +                self.span
 +            }
 +        })*
 +    };
 +}
 +
 +impl_hir_node!(Expr, Pat);
 +
 +impl HirNode for hir::Item<'_> {
 +    fn hir_id(&self) -> HirId {
 +        self.hir_id()
 +    }
 +
 +    fn span(&self) -> Span {
 +        self.span
 +    }
 +}
index 0424e06720263e5322b38eabf54d518bb899d0e6,0000000000000000000000000000000000000000..134fd1ce505a092b22022b6c840a8d722cffc2e7
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,36 @@@
 +use rustc_semver::RustcVersion;
 +
 +macro_rules! msrv_aliases {
 +    ($($major:literal,$minor:literal,$patch:literal {
 +        $($name:ident),* $(,)?
 +    })*) => {
 +        $($(
 +        pub const $name: RustcVersion = RustcVersion::new($major, $minor, $patch);
 +        )*)*
 +    };
 +}
 +
 +// names may refer to stabilized feature flags or library items
 +msrv_aliases! {
 +    1,53,0 { OR_PATTERNS, MANUAL_BITS }
 +    1,52,0 { STR_SPLIT_ONCE }
 +    1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS }
 +    1,50,0 { BOOL_THEN }
 +    1,47,0 { TAU }
 +    1,46,0 { CONST_IF_MATCH }
 +    1,45,0 { STR_STRIP_PREFIX }
 +    1,43,0 { LOG2_10, LOG10_2 }
 +    1,42,0 { MATCHES_MACRO, SLICE_PATTERNS, PTR_SLICE_RAW_PARTS }
 +    1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
 +    1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
 +    1,38,0 { POINTER_CAST }
 +    1,37,0 { TYPE_ALIAS_ENUM_VARIANTS }
 +    1,36,0 { ITERATOR_COPIED }
 +    1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
 +    1,34,0 { TRY_FROM }
 +    1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
 +    1,28,0 { FROM_BOOL }
 +    1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
 +    1,16,0 { STR_REPEAT }
++    1,24,0 { IS_ASCII_DIGIT }
 +}
index 908ff822712ffa0a171f9d043a78ed9b7c8fceb2,0000000000000000000000000000000000000000..b92d42e83232ce27501f77e0e904dbe301eca758
mode 100644,000000..100644
--- /dev/null
@@@ -1,248 -1,0 +1,248 @@@
-                 .map_or(false, |c| c.is_digit(10))
 +use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind};
 +use std::iter;
 +
 +#[derive(Debug, PartialEq, Copy, Clone)]
 +pub enum Radix {
 +    Binary,
 +    Octal,
 +    Decimal,
 +    Hexadecimal,
 +}
 +
 +impl Radix {
 +    /// Returns a reasonable digit group size for this radix.
 +    #[must_use]
 +    fn suggest_grouping(self) -> usize {
 +        match self {
 +            Self::Binary | Self::Hexadecimal => 4,
 +            Self::Octal | Self::Decimal => 3,
 +        }
 +    }
 +}
 +
 +/// A helper method to format numeric literals with digit grouping.
 +/// `lit` must be a valid numeric literal without suffix.
 +pub fn format(lit: &str, type_suffix: Option<&str>, float: bool) -> String {
 +    NumericLiteral::new(lit, type_suffix, float).format()
 +}
 +
 +#[derive(Debug)]
 +pub struct NumericLiteral<'a> {
 +    /// Which radix the literal was represented in.
 +    pub radix: Radix,
 +    /// The radix prefix, if present.
 +    pub prefix: Option<&'a str>,
 +
 +    /// The integer part of the number.
 +    pub integer: &'a str,
 +    /// The fraction part of the number.
 +    pub fraction: Option<&'a str>,
 +    /// The exponent separator (b'e' or b'E') including preceding underscore if present
 +    /// and the exponent part.
 +    pub exponent: Option<(&'a str, &'a str)>,
 +
 +    /// The type suffix, including preceding underscore if present.
 +    pub suffix: Option<&'a str>,
 +}
 +
 +impl<'a> NumericLiteral<'a> {
 +    pub fn from_lit(src: &'a str, lit: &Lit) -> Option<NumericLiteral<'a>> {
 +        NumericLiteral::from_lit_kind(src, &lit.kind)
 +    }
 +
 +    pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> {
 +        let unsigned_src = src.strip_prefix('-').map_or(src, |s| s);
 +        if lit_kind.is_numeric()
 +            && unsigned_src
 +                .trim_start()
 +                .chars()
 +                .next()
++                .map_or(false, |c| c.is_ascii_digit())
 +        {
 +            let (unsuffixed, suffix) = split_suffix(src, lit_kind);
 +            let float = matches!(lit_kind, LitKind::Float(..));
 +            Some(NumericLiteral::new(unsuffixed, suffix, float))
 +        } else {
 +            None
 +        }
 +    }
 +
 +    #[must_use]
 +    pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self {
 +        // Determine delimiter for radix prefix, if present, and radix.
 +        let radix = if lit.starts_with("0x") {
 +            Radix::Hexadecimal
 +        } else if lit.starts_with("0b") {
 +            Radix::Binary
 +        } else if lit.starts_with("0o") {
 +            Radix::Octal
 +        } else {
 +            Radix::Decimal
 +        };
 +
 +        // Grab part of the literal after prefix, if present.
 +        let (prefix, mut sans_prefix) = if radix == Radix::Decimal {
 +            (None, lit)
 +        } else {
 +            let (p, s) = lit.split_at(2);
 +            (Some(p), s)
 +        };
 +
 +        if suffix.is_some() && sans_prefix.ends_with('_') {
 +            // The '_' before the suffix isn't part of the digits
 +            sans_prefix = &sans_prefix[..sans_prefix.len() - 1];
 +        }
 +
 +        let (integer, fraction, exponent) = Self::split_digit_parts(sans_prefix, float);
 +
 +        Self {
 +            radix,
 +            prefix,
 +            integer,
 +            fraction,
 +            exponent,
 +            suffix,
 +        }
 +    }
 +
 +    pub fn is_decimal(&self) -> bool {
 +        self.radix == Radix::Decimal
 +    }
 +
 +    pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) {
 +        let mut integer = digits;
 +        let mut fraction = None;
 +        let mut exponent = None;
 +
 +        if float {
 +            for (i, c) in digits.char_indices() {
 +                match c {
 +                    '.' => {
 +                        integer = &digits[..i];
 +                        fraction = Some(&digits[i + 1..]);
 +                    },
 +                    'e' | 'E' => {
 +                        let exp_start = if digits[..i].ends_with('_') { i - 1 } else { i };
 +
 +                        if integer.len() > exp_start {
 +                            integer = &digits[..exp_start];
 +                        } else {
 +                            fraction = Some(&digits[integer.len() + 1..exp_start]);
 +                        };
 +                        exponent = Some((&digits[exp_start..=i], &digits[i + 1..]));
 +                        break;
 +                    },
 +                    _ => {},
 +                }
 +            }
 +        }
 +
 +        (integer, fraction, exponent)
 +    }
 +
 +    /// Returns literal formatted in a sensible way.
 +    pub fn format(&self) -> String {
 +        let mut output = String::new();
 +
 +        if let Some(prefix) = self.prefix {
 +            output.push_str(prefix);
 +        }
 +
 +        let group_size = self.radix.suggest_grouping();
 +
 +        Self::group_digits(
 +            &mut output,
 +            self.integer,
 +            group_size,
 +            true,
 +            self.radix == Radix::Hexadecimal,
 +        );
 +
 +        if let Some(fraction) = self.fraction {
 +            output.push('.');
 +            Self::group_digits(&mut output, fraction, group_size, false, false);
 +        }
 +
 +        if let Some((separator, exponent)) = self.exponent {
 +            if exponent != "0" {
 +                output.push_str(separator);
 +                Self::group_digits(&mut output, exponent, group_size, true, false);
 +            }
 +        }
 +
 +        if let Some(suffix) = self.suffix {
 +            if output.ends_with('.') {
 +                output.push('0');
 +            }
 +            output.push('_');
 +            output.push_str(suffix);
 +        }
 +
 +        output
 +    }
 +
 +    pub fn group_digits(output: &mut String, input: &str, group_size: usize, partial_group_first: bool, pad: bool) {
 +        debug_assert!(group_size > 0);
 +
 +        let mut digits = input.chars().filter(|&c| c != '_');
 +
 +        // The exponent may have a sign, output it early, otherwise it will be
 +        // treated as a digit
 +        if digits.clone().next() == Some('-') {
 +            let _ = digits.next();
 +            output.push('-');
 +        }
 +
 +        let first_group_size;
 +
 +        if partial_group_first {
 +            first_group_size = (digits.clone().count() - 1) % group_size + 1;
 +            if pad {
 +                for _ in 0..group_size - first_group_size {
 +                    output.push('0');
 +                }
 +            }
 +        } else {
 +            first_group_size = group_size;
 +        }
 +
 +        for _ in 0..first_group_size {
 +            if let Some(digit) = digits.next() {
 +                output.push(digit);
 +            }
 +        }
 +
 +        for (c, i) in iter::zip(digits, (0..group_size).cycle()) {
 +            if i == 0 {
 +                output.push('_');
 +            }
 +            output.push(c);
 +        }
 +    }
 +}
 +
 +fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) {
 +    debug_assert!(lit_kind.is_numeric());
 +    lit_suffix_length(lit_kind).map_or((src, None), |suffix_length| {
 +        let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length);
 +        (unsuffixed, Some(suffix))
 +    })
 +}
 +
 +fn lit_suffix_length(lit_kind: &LitKind) -> Option<usize> {
 +    debug_assert!(lit_kind.is_numeric());
 +    let suffix = match lit_kind {
 +        LitKind::Int(_, int_lit_kind) => match int_lit_kind {
 +            LitIntType::Signed(int_ty) => Some(int_ty.name_str()),
 +            LitIntType::Unsigned(uint_ty) => Some(uint_ty.name_str()),
 +            LitIntType::Unsuffixed => None,
 +        },
 +        LitKind::Float(_, float_lit_kind) => match float_lit_kind {
 +            LitFloatType::Suffixed(float_ty) => Some(float_ty.name_str()),
 +            LitFloatType::Unsuffixed => None,
 +        },
 +        _ => None,
 +    };
 +
 +    suffix.map(str::len)
 +}
index e5fa6deefc5de3d85b1e1afcbcc1cb14cececa02,0000000000000000000000000000000000000000..60971fb716dbdc69e364e137e032b2a5baab05cd
mode 100644,000000..100644
--- /dev/null
@@@ -1,181 -1,0 +1,184 @@@
 +//! This module contains paths to types and functions Clippy needs to know
 +//! about.
 +//!
 +//! Whenever possible, please consider diagnostic items over hardcoded paths.
 +//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
 +
 +#[cfg(feature = "internal")]
 +pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
 +#[cfg(feature = "internal")]
 +pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
 +    ["rustc_lint_defs", "Applicability", "Unspecified"],
 +    ["rustc_lint_defs", "Applicability", "HasPlaceholders"],
 +    ["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
 +    ["rustc_lint_defs", "Applicability", "MachineApplicable"],
 +];
 +#[cfg(feature = "internal")]
 +pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];
 +pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
 +pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
 +pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
 +pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
 +pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
 +pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
 +pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
 +pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
 +pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"];
 +pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
 +pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
 +/// Preferably use the diagnostic item `sym::deref_method` where possible
 +pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
 +pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"];
 +pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
 +#[cfg(feature = "internal")]
 +pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
 +#[cfg(feature = "internal")]
 +pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"];
 +pub const EXIT: [&str; 3] = ["std", "process", "exit"];
 +pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
 +pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
 +pub const FILE: [&str; 3] = ["std", "fs", "File"];
 +pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
 +pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
 +pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
 +pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"];
 +pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
 +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"];
 +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"];
 +pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"];
 +pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
 +pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
 +#[cfg(feature = "internal")]
 +pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
 +#[cfg(feature = "internal")]
 +pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
 +pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
 +pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
 +pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
 +pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
 +pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
 +pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
 +pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
++pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
 +pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
 +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
 +#[cfg(feature = "internal")]
 +pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
 +#[cfg(feature = "internal")]
 +pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
 +#[cfg(feature = "internal")]
 +pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"];
 +#[cfg(feature = "internal")]
 +pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
 +pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"];
 +pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
 +/// Preferably use the diagnostic item `sym::Option` where possible
 +pub const OPTION: [&str; 3] = ["core", "option", "Option"];
 +pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"];
 +pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
 +pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
 +pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
 +pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
 +pub const PARKING_LOT_RAWMUTEX: [&str; 3] = ["parking_lot", "raw_mutex", "RawMutex"];
 +pub const PARKING_LOT_RAWRWLOCK: [&str; 3] = ["parking_lot", "raw_rwlock", "RawRwLock"];
 +pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"];
 +pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"];
 +pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"];
 +pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
 +pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
 +pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
 +#[cfg_attr(not(unix), allow(clippy::invalid_paths))]
 +pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
 +pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
 +pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"];
 +pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"];
 +pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"];
 +pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
 +pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
 +pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"];
 +pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"];
 +pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"];
 +pub const PTR_READ: [&str; 3] = ["core", "ptr", "read"];
 +pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"];
 +pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"];
 +pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"];
 +pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"];
 +pub const PTR_UNALIGNED_VOLATILE_LOAD: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_load"];
 +pub const PTR_UNALIGNED_VOLATILE_STORE: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_store"];
 +pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"];
 +pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
 +pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"];
 +pub const PTR_WRITE_VOLATILE: [&str; 3] = ["core", "ptr", "write_volatile"];
 +pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
 +pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
 +pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
 +pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"];
 +pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"];
 +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
 +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
 +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
 +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
 +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
 +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
 +/// Preferably use the diagnostic item `sym::Result` where possible
 +pub const RESULT: [&str; 3] = ["core", "result", "Result"];
 +pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
 +pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
 +#[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_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
 +pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
 +pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
 +pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
 +pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
 +pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
 +pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
 +pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
++pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];
++pub const STR_BYTES: [&str; 4] = ["core", "str", "<impl str>", "bytes"];
 +pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
 +pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
 +pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
 +pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
 +#[cfg(feature = "internal")]
 +pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
 +#[cfg(feature = "internal")]
 +pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
 +pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
 +pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
 +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"];
 +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"];
 +pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
 +pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
 +pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
 +pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
 +pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
 +pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
 +pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
 +pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
 +pub const PTR_NON_NULL: [&str; 4] = ["core", "ptr", "non_null", "NonNull"];
index fe411220484890f33e6abaa68d2a53477eea7f17,0000000000000000000000000000000000000000..75808b1b17461d657f322903733e48fdf467fc2d
mode 100644,000000..100644
--- /dev/null
@@@ -1,372 -1,0 +1,373 @@@
-         StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => 
-             check_place(tcx, **place, span, body),
 +// This code used to be a part of `rustc` but moved to Clippy as a result of
 +// https://github.com/rust-lang/rust/issues/76618. Because of that, it contains unused code and some
 +// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
 +// differ from the time of `rustc` even if the name stays the same.
 +
 +use rustc_hir as hir;
 +use rustc_hir::def_id::DefId;
 +use rustc_middle::mir::{
 +    Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
 +    TerminatorKind,
 +};
 +use rustc_middle::ty::subst::GenericArgKind;
 +use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
 +use rustc_semver::RustcVersion;
 +use rustc_span::symbol::sym;
 +use rustc_span::Span;
 +use rustc_target::spec::abi::Abi::RustIntrinsic;
 +use std::borrow::Cow;
 +
 +type McfResult = Result<(), (Span, Cow<'static, str>)>;
 +
 +pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult {
 +    let def_id = body.source.def_id();
 +    let mut current = def_id;
 +    loop {
 +        let predicates = tcx.predicates_of(current);
 +        for (predicate, _) in predicates.predicates {
 +            match predicate.kind().skip_binder() {
 +                ty::PredicateKind::RegionOutlives(_)
 +                | ty::PredicateKind::TypeOutlives(_)
 +                | ty::PredicateKind::WellFormed(_)
 +                | ty::PredicateKind::Projection(_)
 +                | ty::PredicateKind::ConstEvaluatable(..)
 +                | ty::PredicateKind::ConstEquate(..)
 +                | ty::PredicateKind::Trait(..)
 +                | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
 +                ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
 +                ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate),
 +                ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate),
 +                ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {:#?}", predicate),
 +            }
 +        }
 +        match predicates.parent {
 +            Some(parent) => current = parent,
 +            None => break,
 +        }
 +    }
 +
 +    for local in &body.local_decls {
 +        check_ty(tcx, local.ty, local.source_info.span)?;
 +    }
 +    // impl trait is gone in MIR, so check the return type manually
 +    check_ty(
 +        tcx,
 +        tcx.fn_sig(def_id).output().skip_binder(),
 +        body.local_decls.iter().next().unwrap().source_info.span,
 +    )?;
 +
 +    for bb in body.basic_blocks() {
 +        check_terminator(tcx, body, bb.terminator(), msrv)?;
 +        for stmt in &bb.statements {
 +            check_statement(tcx, body, def_id, stmt)?;
 +        }
 +    }
 +    Ok(())
 +}
 +
 +fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
 +    for arg in ty.walk() {
 +        let ty = match arg.unpack() {
 +            GenericArgKind::Type(ty) => ty,
 +
 +            // No constraints on lifetimes or constants, except potentially
 +            // constants' types, but `walk` will get to them as well.
 +            GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
 +        };
 +
 +        match ty.kind() {
 +            ty::Ref(_, _, hir::Mutability::Mut) => {
 +                return Err((span, "mutable references in const fn are unstable".into()));
 +            },
 +            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
 +            ty::FnPtr(..) => {
 +                return Err((span, "function pointers in const fn are unstable".into()));
 +            },
 +            ty::Dynamic(preds, _) => {
 +                for pred in preds.iter() {
 +                    match pred.skip_binder() {
 +                        ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
 +                            return Err((
 +                                span,
 +                                "trait bounds other than `Sized` \
 +                                 on const fn parameters are unstable"
 +                                    .into(),
 +                            ));
 +                        },
 +                        ty::ExistentialPredicate::Trait(trait_ref) => {
 +                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
 +                                return Err((
 +                                    span,
 +                                    "trait bounds other than `Sized` \
 +                                     on const fn parameters are unstable"
 +                                        .into(),
 +                                ));
 +                            }
 +                        },
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +    Ok(())
 +}
 +
 +fn check_rvalue<'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    body: &Body<'tcx>,
 +    def_id: DefId,
 +    rvalue: &Rvalue<'tcx>,
 +    span: Span,
 +) -> McfResult {
 +    match rvalue {
 +        Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
 +        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
 +        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
 +            check_place(tcx, *place, span, body)
 +        },
 +        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
 +            use rustc_middle::ty::cast::CastTy;
 +            let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
 +            let cast_out = CastTy::from_ty(*cast_ty).expect("bad output type for cast");
 +            match (cast_in, cast_out) {
 +                (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
 +                    Err((span, "casting pointers to ints is unstable in const fn".into()))
 +                },
 +                _ => check_operand(tcx, operand, span, body),
 +            }
 +        },
 +        Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => {
 +            check_operand(tcx, operand, span, body)
 +        },
 +        Rvalue::Cast(
 +            CastKind::Pointer(
 +                PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
 +            ),
 +            _,
 +            _,
 +        ) => Err((span, "function pointer casts are not allowed in const fn".into())),
 +        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
 +            let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
 +                deref_ty.ty
 +            } else {
 +                // We cannot allow this for now.
 +                return Err((span, "unsizing casts are only allowed for references right now".into()));
 +            };
 +            let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
 +            if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
 +                check_operand(tcx, op, span, body)?;
 +                // Casting/coercing things to slices is fine.
 +                Ok(())
 +            } else {
 +                // We just can't allow trait objects until we have figured out trait method calls.
 +                Err((span, "unsizing casts are not allowed in const fn".into()))
 +            }
 +        },
 +        // binops are fine on integers
 +        Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
 +            check_operand(tcx, lhs, span, body)?;
 +            check_operand(tcx, rhs, span, body)?;
 +            let ty = lhs.ty(body, tcx);
 +            if ty.is_integral() || ty.is_bool() || ty.is_char() {
 +                Ok(())
 +            } else {
 +                Err((
 +                    span,
 +                    "only int, `bool` and `char` operations are stable in const fn".into(),
 +                ))
 +            }
 +        },
 +        Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) | Rvalue::ShallowInitBox(_, _) => Ok(()),
 +        Rvalue::UnaryOp(_, operand) => {
 +            let ty = operand.ty(body, tcx);
 +            if ty.is_integral() || ty.is_bool() {
 +                check_operand(tcx, operand, span, body)
 +            } else {
 +                Err((span, "only int and `bool` operations are stable in const fn".into()))
 +            }
 +        },
 +        Rvalue::Aggregate(_, operands) => {
 +            for operand in operands {
 +                check_operand(tcx, operand, span, body)?;
 +            }
 +            Ok(())
 +        },
 +    }
 +}
 +
 +fn check_statement<'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    body: &Body<'tcx>,
 +    def_id: DefId,
 +    statement: &Statement<'tcx>,
 +) -> McfResult {
 +    let span = statement.source_info.span;
 +    match &statement.kind {
 +        StatementKind::Assign(box (place, rval)) => {
 +            check_place(tcx, *place, span, body)?;
 +            check_rvalue(tcx, body, def_id, rval, span)
 +        },
 +
 +        StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body),
 +        // just an assignment
++        StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => {
++            check_place(tcx, **place, span, body)
++        },
 +
 +        StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { dst, src, count }) => {
 +            check_operand(tcx, dst, span, body)?;
 +            check_operand(tcx, src, span, body)?;
 +            check_operand(tcx, count, span, body)
 +        },
 +        // These are all NOPs
 +        StatementKind::StorageLive(_)
 +        | StatementKind::StorageDead(_)
 +        | StatementKind::Retag { .. }
 +        | StatementKind::AscribeUserType(..)
 +        | StatementKind::Coverage(..)
 +        | StatementKind::Nop => Ok(()),
 +    }
 +}
 +
 +fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
 +    match operand {
 +        Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
 +        Operand::Constant(c) => match c.check_static_ptr(tcx) {
 +            Some(_) => Err((span, "cannot access `static` items in const fn".into())),
 +            None => Ok(()),
 +        },
 +    }
 +}
 +
 +fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
 +    let mut cursor = place.projection.as_ref();
 +    while let [ref proj_base @ .., elem] = *cursor {
 +        cursor = proj_base;
 +        match elem {
 +            ProjectionElem::Field(..) => {
 +                let base_ty = Place::ty_from(place.local, proj_base, body, tcx).ty;
 +                if let Some(def) = base_ty.ty_adt_def() {
 +                    // No union field accesses in `const fn`
 +                    if def.is_union() {
 +                        return Err((span, "accessing union fields is unstable".into()));
 +                    }
 +                }
 +            },
 +            ProjectionElem::ConstantIndex { .. }
 +            | ProjectionElem::Downcast(..)
 +            | ProjectionElem::Subslice { .. }
 +            | ProjectionElem::Deref
 +            | ProjectionElem::Index(_) => {},
 +        }
 +    }
 +
 +    Ok(())
 +}
 +
 +fn check_terminator<'a, 'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    body: &'a Body<'tcx>,
 +    terminator: &Terminator<'tcx>,
 +    msrv: Option<&RustcVersion>,
 +) -> McfResult {
 +    let span = terminator.source_info.span;
 +    match &terminator.kind {
 +        TerminatorKind::FalseEdge { .. }
 +        | TerminatorKind::FalseUnwind { .. }
 +        | TerminatorKind::Goto { .. }
 +        | TerminatorKind::Return
 +        | TerminatorKind::Resume
 +        | TerminatorKind::Unreachable => Ok(()),
 +
 +        TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
 +        TerminatorKind::DropAndReplace { place, value, .. } => {
 +            check_place(tcx, *place, span, body)?;
 +            check_operand(tcx, value, span, body)
 +        },
 +
 +        TerminatorKind::SwitchInt {
 +            discr,
 +            switch_ty: _,
 +            targets: _,
 +        } => check_operand(tcx, discr, span, body),
 +
 +        TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
 +        TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
 +            Err((span, "const fn generators are unstable".into()))
 +        },
 +
 +        TerminatorKind::Call {
 +            func,
 +            args,
 +            from_hir_call: _,
 +            destination: _,
 +            cleanup: _,
 +            fn_span: _,
 +        } => {
 +            let fn_ty = func.ty(body, tcx);
 +            if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
 +                if !is_const_fn(tcx, fn_def_id, msrv) {
 +                    return Err((
 +                        span,
 +                        format!(
 +                            "can only call other `const fn` within a `const fn`, \
 +                             but `{:?}` is not stable as `const fn`",
 +                            func,
 +                        )
 +                        .into(),
 +                    ));
 +                }
 +
 +                // HACK: This is to "unstabilize" the `transmute` intrinsic
 +                // within const fns. `transmute` is allowed in all other const contexts.
 +                // This won't really scale to more intrinsics or functions. Let's allow const
 +                // transmutes in const fn before we add more hacks to this.
 +                if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute {
 +                    return Err((
 +                        span,
 +                        "can only call `transmute` from const items, not `const fn`".into(),
 +                    ));
 +                }
 +
 +                check_operand(tcx, func, span, body)?;
 +
 +                for arg in args {
 +                    check_operand(tcx, arg, span, body)?;
 +                }
 +                Ok(())
 +            } else {
 +                Err((span, "can only call other const fns within const fn".into()))
 +            }
 +        },
 +
 +        TerminatorKind::Assert {
 +            cond,
 +            expected: _,
 +            msg: _,
 +            target: _,
 +            cleanup: _,
 +        } => check_operand(tcx, cond, span, body),
 +
 +        TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
 +    }
 +}
 +
 +fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool {
 +    tcx.is_const_fn(def_id)
 +        && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
 +            if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level {
 +                // Checking MSRV is manually necessary because `rustc` has no such concept. This entire
 +                // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`.
 +                // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
 +                crate::meets_msrv(
 +                    msrv,
 +                    &RustcVersion::parse(since.as_str())
 +                        .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"),
 +                )
 +            } else {
 +                // Unstable const fn with the feature enabled.
 +                msrv.is_none()
 +            }
 +        })
 +}
index dbad607c58ea34dfee9b37780b166824cd0f2efc,0000000000000000000000000000000000000000..c69a3d8d2a15ec4d8883fd415ba94eff3496719a
mode 100644,000000..100644
--- /dev/null
@@@ -1,468 -1,0 +1,487 @@@
- /// Returns the positon just before rarrow
 +//! Utils for extracting, inspecting or transforming source code
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use crate::line_span;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_span::hygiene;
++use rustc_span::source_map::SourceMap;
 +use rustc_span::{BytePos, Pos, Span, SyntaxContext};
 +use std::borrow::Cow;
 +
++/// Checks if the span starts with the given text. This will return false if the span crosses
++/// multiple files or if source is not available.
++///
++/// This is used to check for proc macros giving unhelpful spans to things.
++pub fn span_starts_with<T: LintContext>(cx: &T, span: Span, text: &str) -> bool {
++    fn helper(sm: &SourceMap, span: Span, text: &str) -> bool {
++        let pos = sm.lookup_byte_offset(span.lo());
++        let Some(ref src) = pos.sf.src else {
++            return false;
++        };
++        let end = span.hi() - pos.sf.start_pos;
++        src.get(pos.pos.0 as usize..end.0 as usize)
++            // Expression spans can include wrapping parenthesis. Remove them first.
++            .map_or(false, |s| s.trim_start_matches('(').starts_with(text))
++    }
++    helper(cx.sess().source_map(), span, text)
++}
++
 +/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
 +/// Also takes an `Option<String>` which can be put inside the braces.
 +pub fn expr_block<'a, T: LintContext>(
 +    cx: &T,
 +    expr: &Expr<'_>,
 +    option: Option<String>,
 +    default: &'a str,
 +    indent_relative_to: Option<Span>,
 +) -> Cow<'a, str> {
 +    let code = snippet_block(cx, expr.span, default, indent_relative_to);
 +    let string = option.unwrap_or_default();
 +    if expr.span.from_expansion() {
 +        Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default)))
 +    } else if let ExprKind::Block(_, _) = expr.kind {
 +        Cow::Owned(format!("{}{}", code, string))
 +    } else if string.is_empty() {
 +        Cow::Owned(format!("{{ {} }}", code))
 +    } else {
 +        Cow::Owned(format!("{{\n{};\n{}\n}}", code, string))
 +    }
 +}
 +
 +/// Returns a new Span that extends the original Span to the first non-whitespace char of the first
 +/// line.
 +///
 +/// ```rust,ignore
 +///     let x = ();
 +/// //          ^^
 +/// // will be converted to
 +///     let x = ();
 +/// //  ^^^^^^^^^^
 +/// ```
 +pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
 +    first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
 +}
 +
 +fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
 +    let line_span = line_span(cx, span);
 +    snippet_opt(cx, line_span).and_then(|snip| {
 +        snip.find(|c: char| !c.is_whitespace())
 +            .map(|pos| line_span.lo() + BytePos::from_usize(pos))
 +    })
 +}
 +
 +/// Returns the indentation of the line of a span
 +///
 +/// ```rust,ignore
 +/// let x = ();
 +/// //      ^^ -- will return 0
 +///     let x = ();
 +/// //          ^^ -- will return 4
 +/// ```
 +pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
 +    snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
 +}
 +
 +/// Gets a snippet of the indentation of the line of a span
 +pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> {
 +    snippet_opt(cx, line_span(cx, span)).map(|mut s| {
 +        let len = s.len() - s.trim_start().len();
 +        s.truncate(len);
 +        s
 +    })
 +}
 +
 +// If the snippet is empty, it's an attribute that was inserted during macro
 +// expansion and we want to ignore those, because they could come from external
 +// sources that the user has no control over.
 +// For some reason these attributes don't have any expansion info on them, so
 +// we have to check it this way until there is a better way.
 +pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
 +    if let Some(snippet) = snippet_opt(cx, span) {
 +        if snippet.is_empty() {
 +            return false;
 +        }
 +    }
 +    true
 +}
 +
++/// Returns the position just before rarrow
 +///
 +/// ```rust,ignore
 +/// fn into(self) -> () {}
 +///              ^
 +/// // in case of unformatted code
 +/// fn into2(self)-> () {}
 +///               ^
 +/// fn into3(self)   -> () {}
 +///               ^
 +/// ```
 +pub fn position_before_rarrow(s: &str) -> Option<usize> {
 +    s.rfind("->").map(|rpos| {
 +        let mut rpos = rpos;
 +        let chars: Vec<char> = s.chars().collect();
 +        while rpos > 1 {
 +            if let Some(c) = chars.get(rpos - 1) {
 +                if c.is_whitespace() {
 +                    rpos -= 1;
 +                    continue;
 +                }
 +            }
 +            break;
 +        }
 +        rpos
 +    })
 +}
 +
 +/// Reindent a multiline string with possibility of ignoring the first line.
 +#[allow(clippy::needless_pass_by_value)]
 +pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
 +    let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' ');
 +    let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t');
 +    reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into()
 +}
 +
 +fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String {
 +    let x = s
 +        .lines()
 +        .skip(usize::from(ignore_first))
 +        .filter_map(|l| {
 +            if l.is_empty() {
 +                None
 +            } else {
 +                // ignore empty lines
 +                Some(l.char_indices().find(|&(_, x)| x != ch).unwrap_or((l.len(), ch)).0)
 +            }
 +        })
 +        .min()
 +        .unwrap_or(0);
 +    let indent = indent.unwrap_or(0);
 +    s.lines()
 +        .enumerate()
 +        .map(|(i, l)| {
 +            if (ignore_first && i == 0) || l.is_empty() {
 +                l.to_owned()
 +            } else if x > indent {
 +                l.split_at(x - indent).1.to_owned()
 +            } else {
 +                " ".repeat(indent - x) + l
 +            }
 +        })
 +        .collect::<Vec<String>>()
 +        .join("\n")
 +}
 +
 +/// Converts a span to a code snippet if available, otherwise returns the default.
 +///
 +/// This is useful if you want to provide suggestions for your lint or more generally, if you want
 +/// to convert a given `Span` to a `str`. To create suggestions consider using
 +/// [`snippet_with_applicability`] to ensure that the applicability stays correct.
 +///
 +/// # Example
 +/// ```rust,ignore
 +/// // Given two spans one for `value` and one for the `init` expression.
 +/// let value = Vec::new();
 +/// //  ^^^^^   ^^^^^^^^^^
 +/// //  span1   span2
 +///
 +/// // The snipped call would return the corresponding code snippet
 +/// snippet(cx, span1, "..") // -> "value"
 +/// snippet(cx, span2, "..") // -> "Vec::new()"
 +/// ```
 +pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
 +    snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from)
 +}
 +
 +/// Same as [`snippet`], but it adapts the applicability level by following rules:
 +///
 +/// - Applicability level `Unspecified` will never be changed.
 +/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
 +/// - If the default value is used and the applicability level is `MachineApplicable`, change it to
 +/// `HasPlaceholders`
 +pub fn snippet_with_applicability<'a, T: LintContext>(
 +    cx: &T,
 +    span: Span,
 +    default: &'a str,
 +    applicability: &mut Applicability,
 +) -> Cow<'a, str> {
 +    if *applicability != Applicability::Unspecified && span.from_expansion() {
 +        *applicability = Applicability::MaybeIncorrect;
 +    }
 +    snippet_opt(cx, span).map_or_else(
 +        || {
 +            if *applicability == Applicability::MachineApplicable {
 +                *applicability = Applicability::HasPlaceholders;
 +            }
 +            Cow::Borrowed(default)
 +        },
 +        From::from,
 +    )
 +}
 +
 +/// Same as `snippet`, but should only be used when it's clear that the input span is
 +/// not a macro argument.
 +pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
 +    snippet(cx, span.source_callsite(), default)
 +}
 +
 +/// Converts a span to a code snippet. Returns `None` if not available.
 +pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
 +    cx.sess().source_map().span_to_snippet(span).ok()
 +}
 +
 +/// Converts a span (from a block) to a code snippet if available, otherwise use default.
 +///
 +/// This trims the code of indentation, except for the first line. Use it for blocks or block-like
 +/// things which need to be printed as such.
 +///
 +/// The `indent_relative_to` arg can be used, to provide a span, where the indentation of the
 +/// resulting snippet of the given span.
 +///
 +/// # Example
 +///
 +/// ```rust,ignore
 +/// snippet_block(cx, block.span, "..", None)
 +/// // where, `block` is the block of the if expr
 +///     if x {
 +///         y;
 +///     }
 +/// // will return the snippet
 +/// {
 +///     y;
 +/// }
 +/// ```
 +///
 +/// ```rust,ignore
 +/// snippet_block(cx, block.span, "..", Some(if_expr.span))
 +/// // where, `block` is the block of the if expr
 +///     if x {
 +///         y;
 +///     }
 +/// // will return the snippet
 +/// {
 +///         y;
 +///     } // aligned with `if`
 +/// ```
 +/// Note that the first line of the snippet always has 0 indentation.
 +pub fn snippet_block<'a, T: LintContext>(
 +    cx: &T,
 +    span: Span,
 +    default: &'a str,
 +    indent_relative_to: Option<Span>,
 +) -> Cow<'a, str> {
 +    let snip = snippet(cx, span, default);
 +    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
 +    reindent_multiline(snip, true, indent)
 +}
 +
 +/// Same as `snippet_block`, but adapts the applicability level by the rules of
 +/// `snippet_with_applicability`.
 +pub fn snippet_block_with_applicability<'a, T: LintContext>(
 +    cx: &T,
 +    span: Span,
 +    default: &'a str,
 +    indent_relative_to: Option<Span>,
 +    applicability: &mut Applicability,
 +) -> Cow<'a, str> {
 +    let snip = snippet_with_applicability(cx, span, default, applicability);
 +    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
 +    reindent_multiline(snip, true, indent)
 +}
 +
 +/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This
 +/// will result in the macro call, rather then the expansion, if the span is from a child context.
 +/// If the span is not from a child context, it will be used directly instead.
 +///
 +/// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node
 +/// would result in `box []`. If given the context of the address of expression, this function will
 +/// correctly get a snippet of `vec![]`.
 +///
 +/// This will also return whether or not the snippet is a macro call.
 +pub fn snippet_with_context<'a>(
 +    cx: &LateContext<'_>,
 +    span: Span,
 +    outer: SyntaxContext,
 +    default: &'a str,
 +    applicability: &mut Applicability,
 +) -> (Cow<'a, str>, bool) {
 +    let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else(
 +        || {
 +            // The span is from a macro argument, and the outer context is the macro using the argument
 +            if *applicability != Applicability::Unspecified {
 +                *applicability = Applicability::MaybeIncorrect;
 +            }
 +            // TODO: get the argument span.
 +            (span, false)
 +        },
 +        |outer_span| (outer_span, span.ctxt() != outer),
 +    );
 +
 +    (
 +        snippet_with_applicability(cx, span, default, applicability),
 +        is_macro_call,
 +    )
 +}
 +
 +/// Walks the span up to the target context, thereby returning the macro call site if the span is
 +/// inside a macro expansion, or the original span if it is not. Note this will return `None` in the
 +/// case of the span being in a macro expansion, but the target context is from expanding a macro
 +/// argument.
 +///
 +/// Given the following
 +///
 +/// ```rust,ignore
 +/// macro_rules! m { ($e:expr) => { f($e) }; }
 +/// g(m!(0))
 +/// ```
 +///
 +/// If called with a span of the call to `f` and a context of the call to `g` this will return a
 +/// span containing `m!(0)`. However, if called with a span of the literal `0` this will give a span
 +/// containing `0` as the context is the same as the outer context.
 +///
 +/// This will traverse through multiple macro calls. Given the following:
 +///
 +/// ```rust,ignore
 +/// macro_rules! m { ($e:expr) => { n!($e, 0) }; }
 +/// macro_rules! n { ($e:expr, $f:expr) => { f($e, $f) }; }
 +/// g(m!(0))
 +/// ```
 +///
 +/// If called with a span of the call to `f` and a context of the call to `g` this will return a
 +/// span containing `m!(0)`.
 +pub fn walk_span_to_context(span: Span, outer: SyntaxContext) -> Option<Span> {
 +    let outer_span = hygiene::walk_chain(span, outer);
 +    (outer_span.ctxt() == outer).then(|| outer_span)
 +}
 +
 +/// Removes block comments from the given `Vec` of lines.
 +///
 +/// # Examples
 +///
 +/// ```rust,ignore
 +/// without_block_comments(vec!["/*", "foo", "*/"]);
 +/// // => vec![]
 +///
 +/// without_block_comments(vec!["bar", "/*", "foo", "*/"]);
 +/// // => vec!["bar"]
 +/// ```
 +pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> {
 +    let mut without = vec![];
 +
 +    let mut nest_level = 0;
 +
 +    for line in lines {
 +        if line.contains("/*") {
 +            nest_level += 1;
 +            continue;
 +        } else if line.contains("*/") {
 +            nest_level -= 1;
 +            continue;
 +        }
 +
 +        if nest_level == 0 {
 +            without.push(line);
 +        }
 +    }
 +
 +    without
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use super::{reindent_multiline, without_block_comments};
 +
 +    #[test]
 +    fn test_reindent_multiline_single_line() {
 +        assert_eq!("", reindent_multiline("".into(), false, None));
 +        assert_eq!("...", reindent_multiline("...".into(), false, None));
 +        assert_eq!("...", reindent_multiline("    ...".into(), false, None));
 +        assert_eq!("...", reindent_multiline("\t...".into(), false, None));
 +        assert_eq!("...", reindent_multiline("\t\t...".into(), false, None));
 +    }
 +
 +    #[test]
 +    #[rustfmt::skip]
 +    fn test_reindent_multiline_block() {
 +        assert_eq!("\
 +    if x {
 +        y
 +    } else {
 +        z
 +    }", reindent_multiline("    if x {
 +            y
 +        } else {
 +            z
 +        }".into(), false, None));
 +        assert_eq!("\
 +    if x {
 +    \ty
 +    } else {
 +    \tz
 +    }", reindent_multiline("    if x {
 +        \ty
 +        } else {
 +        \tz
 +        }".into(), false, None));
 +    }
 +
 +    #[test]
 +    #[rustfmt::skip]
 +    fn test_reindent_multiline_empty_line() {
 +        assert_eq!("\
 +    if x {
 +        y
 +
 +    } else {
 +        z
 +    }", reindent_multiline("    if x {
 +            y
 +
 +        } else {
 +            z
 +        }".into(), false, None));
 +    }
 +
 +    #[test]
 +    #[rustfmt::skip]
 +    fn test_reindent_multiline_lines_deeper() {
 +        assert_eq!("\
 +        if x {
 +            y
 +        } else {
 +            z
 +        }", reindent_multiline("\
 +    if x {
 +        y
 +    } else {
 +        z
 +    }".into(), true, Some(8)));
 +    }
 +
 +    #[test]
 +    fn test_without_block_comments_lines_without_block_comments() {
 +        let result = without_block_comments(vec!["/*", "", "*/"]);
 +        println!("result: {:?}", result);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]);
 +        assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]);
 +
 +        let result = without_block_comments(vec!["/* rust", "", "*/"]);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["/* one-line comment */"]);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["foo", "bar", "baz"]);
 +        assert_eq!(result, vec!["foo", "bar", "baz"]);
 +    }
 +}
index 794d2e1026f8c7013de5515b02b2610ddace3365,0000000000000000000000000000000000000000..18915553e61c06cdc97ee45265f796215f617874
mode 100644,000000..100644
--- /dev/null
@@@ -1,1090 -1,0 +1,1088 @@@
- use std::fmt::Display;
 +//! Contains utility functions to generate suggestions.
 +#![deny(clippy::missing_docs_in_private_items)]
 +
 +use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite};
 +use crate::{get_parent_expr_for_hir, higher};
 +use rustc_ast::util::parser::AssocOp;
 +use rustc_ast::{ast, token};
 +use rustc_ast_pretty::pprust::token_kind_to_string;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::{ExprKind, HirId, MutTy, TyKind};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::{EarlyContext, LateContext, LintContext};
 +use rustc_middle::hir::place::ProjectionKind;
 +use rustc_middle::mir::{FakeReadCause, Mutability};
 +use rustc_middle::ty;
 +use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext};
 +use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 +use std::borrow::Cow;
 +use std::convert::TryInto;
-                 self.suggestion_start.push_str(&format!("{}&{}", start_snip, ident_str));
++use std::fmt::{Display, Write as _};
 +use std::iter;
 +use std::ops::{Add, Neg, Not, Sub};
 +
 +/// A helper type to build suggestion correctly handling parentheses.
 +#[derive(Clone, PartialEq)]
 +pub enum Sugg<'a> {
 +    /// An expression that never needs parentheses such as `1337` or `[0; 42]`.
 +    NonParen(Cow<'a, str>),
 +    /// An expression that does not fit in other variants.
 +    MaybeParen(Cow<'a, str>),
 +    /// A binary operator expression, including `as`-casts and explicit type
 +    /// coercion.
 +    BinOp(AssocOp, Cow<'a, str>, Cow<'a, str>),
 +}
 +
 +/// Literal constant `0`, for convenience.
 +pub const ZERO: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("0"));
 +/// Literal constant `1`, for convenience.
 +pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
 +/// a constant represents an empty string, for convenience.
 +pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed(""));
 +
 +impl Display for Sugg<'_> {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
 +        match *self {
 +            Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) => s.fmt(f),
 +            Sugg::BinOp(op, ref lhs, ref rhs) => binop_to_string(op, lhs, rhs).fmt(f),
 +        }
 +    }
 +}
 +
 +#[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method
 +impl<'a> Sugg<'a> {
 +    /// Prepare a suggestion from an expression.
 +    pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Self> {
 +        let get_snippet = |span| snippet(cx, span, "");
 +        snippet_opt(cx, expr.span).map(|_| Self::hir_from_snippet(expr, get_snippet))
 +    }
 +
 +    /// Convenience function around `hir_opt` for suggestions with a default
 +    /// text.
 +    pub fn hir(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
 +        Self::hir_opt(cx, expr).unwrap_or(Sugg::NonParen(Cow::Borrowed(default)))
 +    }
 +
 +    /// Same as `hir`, but it adapts the applicability level by following rules:
 +    ///
 +    /// - Applicability level `Unspecified` will never be changed.
 +    /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
 +    /// - If the default value is used and the applicability level is `MachineApplicable`, change it
 +    ///   to
 +    /// `HasPlaceholders`
 +    pub fn hir_with_applicability(
 +        cx: &LateContext<'_>,
 +        expr: &hir::Expr<'_>,
 +        default: &'a str,
 +        applicability: &mut Applicability,
 +    ) -> Self {
 +        if *applicability != Applicability::Unspecified && expr.span.from_expansion() {
 +            *applicability = Applicability::MaybeIncorrect;
 +        }
 +        Self::hir_opt(cx, expr).unwrap_or_else(|| {
 +            if *applicability == Applicability::MachineApplicable {
 +                *applicability = Applicability::HasPlaceholders;
 +            }
 +            Sugg::NonParen(Cow::Borrowed(default))
 +        })
 +    }
 +
 +    /// Same as `hir`, but will use the pre expansion span if the `expr` was in a macro.
 +    pub fn hir_with_macro_callsite(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
 +        let get_snippet = |span| snippet_with_macro_callsite(cx, span, default);
 +        Self::hir_from_snippet(expr, get_snippet)
 +    }
 +
 +    /// Same as `hir`, but first walks the span up to the given context. This will result in the
 +    /// macro call, rather then the expansion, if the span is from a child context. If the span is
 +    /// not from a child context, it will be used directly instead.
 +    ///
 +    /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR
 +    /// node would result in `box []`. If given the context of the address of expression, this
 +    /// function will correctly get a snippet of `vec![]`.
 +    pub fn hir_with_context(
 +        cx: &LateContext<'_>,
 +        expr: &hir::Expr<'_>,
 +        ctxt: SyntaxContext,
 +        default: &'a str,
 +        applicability: &mut Applicability,
 +    ) -> Self {
 +        if expr.span.ctxt() == ctxt {
 +            Self::hir_from_snippet(expr, |span| snippet(cx, span, default))
 +        } else {
 +            let snip = snippet_with_applicability(cx, expr.span, default, applicability);
 +            Sugg::NonParen(snip)
 +        }
 +    }
 +
 +    /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
 +    /// function variants of `Sugg`, since these use different snippet functions.
 +    fn hir_from_snippet(expr: &hir::Expr<'_>, get_snippet: impl Fn(Span) -> Cow<'a, str>) -> Self {
 +        if let Some(range) = higher::Range::hir(expr) {
 +            let op = match range.limits {
 +                ast::RangeLimits::HalfOpen => AssocOp::DotDot,
 +                ast::RangeLimits::Closed => AssocOp::DotDotEq,
 +            };
 +            let start = range.start.map_or("".into(), |expr| get_snippet(expr.span));
 +            let end = range.end.map_or("".into(), |expr| get_snippet(expr.span));
 +
 +            return Sugg::BinOp(op, start, end);
 +        }
 +
 +        match expr.kind {
 +            hir::ExprKind::AddrOf(..)
 +            | hir::ExprKind::Box(..)
 +            | hir::ExprKind::If(..)
 +            | hir::ExprKind::Let(..)
 +            | hir::ExprKind::Closure(..)
 +            | hir::ExprKind::Unary(..)
 +            | hir::ExprKind::Match(..) => Sugg::MaybeParen(get_snippet(expr.span)),
 +            hir::ExprKind::Continue(..)
 +            | hir::ExprKind::Yield(..)
 +            | hir::ExprKind::Array(..)
 +            | hir::ExprKind::Block(..)
 +            | hir::ExprKind::Break(..)
 +            | hir::ExprKind::Call(..)
 +            | hir::ExprKind::Field(..)
 +            | hir::ExprKind::Index(..)
 +            | hir::ExprKind::InlineAsm(..)
 +            | hir::ExprKind::ConstBlock(..)
 +            | hir::ExprKind::Lit(..)
 +            | hir::ExprKind::Loop(..)
 +            | hir::ExprKind::MethodCall(..)
 +            | hir::ExprKind::Path(..)
 +            | hir::ExprKind::Repeat(..)
 +            | hir::ExprKind::Ret(..)
 +            | hir::ExprKind::Struct(..)
 +            | hir::ExprKind::Tup(..)
 +            | hir::ExprKind::DropTemps(_)
 +            | hir::ExprKind::Err => Sugg::NonParen(get_snippet(expr.span)),
 +            hir::ExprKind::Assign(lhs, rhs, _) => {
 +                Sugg::BinOp(AssocOp::Assign, get_snippet(lhs.span), get_snippet(rhs.span))
 +            },
 +            hir::ExprKind::AssignOp(op, lhs, rhs) => {
 +                Sugg::BinOp(hirbinop2assignop(op), get_snippet(lhs.span), get_snippet(rhs.span))
 +            },
 +            hir::ExprKind::Binary(op, lhs, rhs) => Sugg::BinOp(
 +                AssocOp::from_ast_binop(op.node.into()),
 +                get_snippet(lhs.span),
 +                get_snippet(rhs.span),
 +            ),
 +            hir::ExprKind::Cast(lhs, ty) => Sugg::BinOp(AssocOp::As, get_snippet(lhs.span), get_snippet(ty.span)),
 +            hir::ExprKind::Type(lhs, ty) => Sugg::BinOp(AssocOp::Colon, get_snippet(lhs.span), get_snippet(ty.span)),
 +        }
 +    }
 +
 +    /// Prepare a suggestion from an expression.
 +    pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
 +        use rustc_ast::ast::RangeLimits;
 +
 +        let get_whole_snippet = || {
 +            if expr.span.from_expansion() {
 +                snippet_with_macro_callsite(cx, expr.span, default)
 +            } else {
 +                snippet(cx, expr.span, default)
 +            }
 +        };
 +
 +        match expr.kind {
 +            ast::ExprKind::AddrOf(..)
 +            | ast::ExprKind::Box(..)
 +            | ast::ExprKind::Closure(..)
 +            | ast::ExprKind::If(..)
 +            | ast::ExprKind::Let(..)
 +            | ast::ExprKind::Unary(..)
 +            | ast::ExprKind::Match(..) => Sugg::MaybeParen(get_whole_snippet()),
 +            ast::ExprKind::Async(..)
 +            | ast::ExprKind::Block(..)
 +            | ast::ExprKind::Break(..)
 +            | ast::ExprKind::Call(..)
 +            | ast::ExprKind::Continue(..)
 +            | ast::ExprKind::Yield(..)
 +            | ast::ExprKind::Field(..)
 +            | ast::ExprKind::ForLoop(..)
 +            | ast::ExprKind::Index(..)
 +            | ast::ExprKind::InlineAsm(..)
 +            | ast::ExprKind::ConstBlock(..)
 +            | ast::ExprKind::Lit(..)
 +            | ast::ExprKind::Loop(..)
 +            | ast::ExprKind::MacCall(..)
 +            | ast::ExprKind::MethodCall(..)
 +            | ast::ExprKind::Paren(..)
 +            | ast::ExprKind::Underscore
 +            | ast::ExprKind::Path(..)
 +            | ast::ExprKind::Repeat(..)
 +            | ast::ExprKind::Ret(..)
 +            | ast::ExprKind::Yeet(..)
 +            | ast::ExprKind::Struct(..)
 +            | ast::ExprKind::Try(..)
 +            | ast::ExprKind::TryBlock(..)
 +            | ast::ExprKind::Tup(..)
 +            | ast::ExprKind::Array(..)
 +            | ast::ExprKind::While(..)
 +            | ast::ExprKind::Await(..)
 +            | ast::ExprKind::Err => Sugg::NonParen(get_whole_snippet()),
 +            ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
 +                AssocOp::DotDot,
 +                lhs.as_ref().map_or("".into(), |lhs| snippet(cx, lhs.span, default)),
 +                rhs.as_ref().map_or("".into(), |rhs| snippet(cx, rhs.span, default)),
 +            ),
 +            ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp(
 +                AssocOp::DotDotEq,
 +                lhs.as_ref().map_or("".into(), |lhs| snippet(cx, lhs.span, default)),
 +                rhs.as_ref().map_or("".into(), |rhs| snippet(cx, rhs.span, default)),
 +            ),
 +            ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp(
 +                AssocOp::Assign,
 +                snippet(cx, lhs.span, default),
 +                snippet(cx, rhs.span, default),
 +            ),
 +            ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
 +                astbinop2assignop(op),
 +                snippet(cx, lhs.span, default),
 +                snippet(cx, rhs.span, default),
 +            ),
 +            ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
 +                AssocOp::from_ast_binop(op.node),
 +                snippet(cx, lhs.span, default),
 +                snippet(cx, rhs.span, default),
 +            ),
 +            ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp(
 +                AssocOp::As,
 +                snippet(cx, lhs.span, default),
 +                snippet(cx, ty.span, default),
 +            ),
 +            ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
 +                AssocOp::Colon,
 +                snippet(cx, lhs.span, default),
 +                snippet(cx, ty.span, default),
 +            ),
 +        }
 +    }
 +
 +    /// Convenience method to create the `<lhs> && <rhs>` suggestion.
 +    pub fn and(self, rhs: &Self) -> Sugg<'static> {
 +        make_binop(ast::BinOpKind::And, &self, rhs)
 +    }
 +
 +    /// Convenience method to create the `<lhs> & <rhs>` suggestion.
 +    pub fn bit_and(self, rhs: &Self) -> Sugg<'static> {
 +        make_binop(ast::BinOpKind::BitAnd, &self, rhs)
 +    }
 +
 +    /// Convenience method to create the `<lhs> as <rhs>` suggestion.
 +    pub fn as_ty<R: Display>(self, rhs: R) -> Sugg<'static> {
 +        make_assoc(AssocOp::As, &self, &Sugg::NonParen(rhs.to_string().into()))
 +    }
 +
 +    /// Convenience method to create the `&<expr>` suggestion.
 +    pub fn addr(self) -> Sugg<'static> {
 +        make_unop("&", self)
 +    }
 +
 +    /// Convenience method to create the `&mut <expr>` suggestion.
 +    pub fn mut_addr(self) -> Sugg<'static> {
 +        make_unop("&mut ", self)
 +    }
 +
 +    /// Convenience method to create the `*<expr>` suggestion.
 +    pub fn deref(self) -> Sugg<'static> {
 +        make_unop("*", self)
 +    }
 +
 +    /// Convenience method to create the `&*<expr>` suggestion. Currently this
 +    /// is needed because `sugg.deref().addr()` produces an unnecessary set of
 +    /// parentheses around the deref.
 +    pub fn addr_deref(self) -> Sugg<'static> {
 +        make_unop("&*", self)
 +    }
 +
 +    /// Convenience method to create the `&mut *<expr>` suggestion. Currently
 +    /// this is needed because `sugg.deref().mut_addr()` produces an unnecessary
 +    /// set of parentheses around the deref.
 +    pub fn mut_addr_deref(self) -> Sugg<'static> {
 +        make_unop("&mut *", self)
 +    }
 +
 +    /// Convenience method to transform suggestion into a return call
 +    pub fn make_return(self) -> Sugg<'static> {
 +        Sugg::NonParen(Cow::Owned(format!("return {}", self)))
 +    }
 +
 +    /// Convenience method to transform suggestion into a block
 +    /// where the suggestion is a trailing expression
 +    pub fn blockify(self) -> Sugg<'static> {
 +        Sugg::NonParen(Cow::Owned(format!("{{ {} }}", self)))
 +    }
 +
 +    /// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>`
 +    /// suggestion.
 +    #[allow(dead_code)]
 +    pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> {
 +        match limit {
 +            ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, end),
 +            ast::RangeLimits::Closed => make_assoc(AssocOp::DotDotEq, &self, end),
 +        }
 +    }
 +
 +    /// Adds parentheses to any expression that might need them. Suitable to the
 +    /// `self` argument of a method call
 +    /// (e.g., to build `bar.foo()` or `(1 + 2).foo()`).
 +    #[must_use]
 +    pub fn maybe_par(self) -> Self {
 +        match self {
 +            Sugg::NonParen(..) => self,
 +            // `(x)` and `(x).y()` both don't need additional parens.
 +            Sugg::MaybeParen(sugg) => {
 +                if has_enclosing_paren(&sugg) {
 +                    Sugg::MaybeParen(sugg)
 +                } else {
 +                    Sugg::NonParen(format!("({})", sugg).into())
 +                }
 +            },
 +            Sugg::BinOp(op, lhs, rhs) => {
 +                let sugg = binop_to_string(op, &lhs, &rhs);
 +                Sugg::NonParen(format!("({})", sugg).into())
 +            },
 +        }
 +    }
 +}
 +
 +/// Generates a string from the operator and both sides.
 +fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String {
 +    match op {
 +        AssocOp::Add
 +        | AssocOp::Subtract
 +        | AssocOp::Multiply
 +        | AssocOp::Divide
 +        | AssocOp::Modulus
 +        | AssocOp::LAnd
 +        | AssocOp::LOr
 +        | AssocOp::BitXor
 +        | AssocOp::BitAnd
 +        | AssocOp::BitOr
 +        | AssocOp::ShiftLeft
 +        | AssocOp::ShiftRight
 +        | AssocOp::Equal
 +        | AssocOp::Less
 +        | AssocOp::LessEqual
 +        | AssocOp::NotEqual
 +        | AssocOp::Greater
 +        | AssocOp::GreaterEqual => format!(
 +            "{} {} {}",
 +            lhs,
 +            op.to_ast_binop().expect("Those are AST ops").to_string(),
 +            rhs
 +        ),
 +        AssocOp::Assign => format!("{} = {}", lhs, rhs),
 +        AssocOp::AssignOp(op) => {
 +            format!("{} {}= {}", lhs, token_kind_to_string(&token::BinOp(op)), rhs)
 +        },
 +        AssocOp::As => format!("{} as {}", lhs, rhs),
 +        AssocOp::DotDot => format!("{}..{}", lhs, rhs),
 +        AssocOp::DotDotEq => format!("{}..={}", lhs, rhs),
 +        AssocOp::Colon => format!("{}: {}", lhs, rhs),
 +    }
 +}
 +
 +/// Return `true` if `sugg` is enclosed in parenthesis.
 +pub fn has_enclosing_paren(sugg: impl AsRef<str>) -> bool {
 +    let mut chars = sugg.as_ref().chars();
 +    if chars.next() == Some('(') {
 +        let mut depth = 1;
 +        for c in &mut chars {
 +            if c == '(' {
 +                depth += 1;
 +            } else if c == ')' {
 +                depth -= 1;
 +            }
 +            if depth == 0 {
 +                break;
 +            }
 +        }
 +        chars.next().is_none()
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Copied from the rust standard library, and then edited
 +macro_rules! forward_binop_impls_to_ref {
 +    (impl $imp:ident, $method:ident for $t:ty, type Output = $o:ty) => {
 +        impl $imp<$t> for &$t {
 +            type Output = $o;
 +
 +            fn $method(self, other: $t) -> $o {
 +                $imp::$method(self, &other)
 +            }
 +        }
 +
 +        impl $imp<&$t> for $t {
 +            type Output = $o;
 +
 +            fn $method(self, other: &$t) -> $o {
 +                $imp::$method(&self, other)
 +            }
 +        }
 +
 +        impl $imp for $t {
 +            type Output = $o;
 +
 +            fn $method(self, other: $t) -> $o {
 +                $imp::$method(&self, &other)
 +            }
 +        }
 +    };
 +}
 +
 +impl Add for &Sugg<'_> {
 +    type Output = Sugg<'static>;
 +    fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> {
 +        make_binop(ast::BinOpKind::Add, self, rhs)
 +    }
 +}
 +
 +impl Sub for &Sugg<'_> {
 +    type Output = Sugg<'static>;
 +    fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> {
 +        make_binop(ast::BinOpKind::Sub, self, rhs)
 +    }
 +}
 +
 +forward_binop_impls_to_ref!(impl Add, add for Sugg<'_>, type Output = Sugg<'static>);
 +forward_binop_impls_to_ref!(impl Sub, sub for Sugg<'_>, type Output = Sugg<'static>);
 +
 +impl Neg for Sugg<'_> {
 +    type Output = Sugg<'static>;
 +    fn neg(self) -> Sugg<'static> {
 +        make_unop("-", self)
 +    }
 +}
 +
 +impl<'a> Not for Sugg<'a> {
 +    type Output = Sugg<'a>;
 +    fn not(self) -> Sugg<'a> {
 +        use AssocOp::{Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual};
 +
 +        if let Sugg::BinOp(op, lhs, rhs) = self {
 +            let to_op = match op {
 +                Equal => NotEqual,
 +                NotEqual => Equal,
 +                Less => GreaterEqual,
 +                GreaterEqual => Less,
 +                Greater => LessEqual,
 +                LessEqual => Greater,
 +                _ => return make_unop("!", Sugg::BinOp(op, lhs, rhs)),
 +            };
 +            Sugg::BinOp(to_op, lhs, rhs)
 +        } else {
 +            make_unop("!", self)
 +        }
 +    }
 +}
 +
 +/// Helper type to display either `foo` or `(foo)`.
 +struct ParenHelper<T> {
 +    /// `true` if parentheses are needed.
 +    paren: bool,
 +    /// The main thing to display.
 +    wrapped: T,
 +}
 +
 +impl<T> ParenHelper<T> {
 +    /// Builds a `ParenHelper`.
 +    fn new(paren: bool, wrapped: T) -> Self {
 +        Self { paren, wrapped }
 +    }
 +}
 +
 +impl<T: Display> Display for ParenHelper<T> {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
 +        if self.paren {
 +            write!(f, "({})", self.wrapped)
 +        } else {
 +            self.wrapped.fmt(f)
 +        }
 +    }
 +}
 +
 +/// Builds the string for `<op><expr>` adding parenthesis when necessary.
 +///
 +/// For convenience, the operator is taken as a string because all unary
 +/// operators have the same
 +/// precedence.
 +pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> {
 +    Sugg::MaybeParen(format!("{}{}", op, expr.maybe_par()).into())
 +}
 +
 +/// Builds the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
 +///
 +/// Precedence of shift operator relative to other arithmetic operation is
 +/// often confusing so
 +/// parenthesis will always be added for a mix of these.
 +pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
 +    /// Returns `true` if the operator is a shift operator `<<` or `>>`.
 +    fn is_shift(op: AssocOp) -> bool {
 +        matches!(op, AssocOp::ShiftLeft | AssocOp::ShiftRight)
 +    }
 +
 +    /// Returns `true` if the operator is an arithmetic operator
 +    /// (i.e., `+`, `-`, `*`, `/`, `%`).
 +    fn is_arith(op: AssocOp) -> bool {
 +        matches!(
 +            op,
 +            AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus
 +        )
 +    }
 +
 +    /// Returns `true` if the operator `op` needs parenthesis with the operator
 +    /// `other` in the direction `dir`.
 +    fn needs_paren(op: AssocOp, other: AssocOp, dir: Associativity) -> bool {
 +        other.precedence() < op.precedence()
 +            || (other.precedence() == op.precedence()
 +                && ((op != other && associativity(op) != dir)
 +                    || (op == other && associativity(op) != Associativity::Both)))
 +            || is_shift(op) && is_arith(other)
 +            || is_shift(other) && is_arith(op)
 +    }
 +
 +    let lhs_paren = if let Sugg::BinOp(lop, _, _) = *lhs {
 +        needs_paren(op, lop, Associativity::Left)
 +    } else {
 +        false
 +    };
 +
 +    let rhs_paren = if let Sugg::BinOp(rop, _, _) = *rhs {
 +        needs_paren(op, rop, Associativity::Right)
 +    } else {
 +        false
 +    };
 +
 +    let lhs = ParenHelper::new(lhs_paren, lhs).to_string();
 +    let rhs = ParenHelper::new(rhs_paren, rhs).to_string();
 +    Sugg::BinOp(op, lhs.into(), rhs.into())
 +}
 +
 +/// Convenience wrapper around `make_assoc` and `AssocOp::from_ast_binop`.
 +pub fn make_binop(op: ast::BinOpKind, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
 +    make_assoc(AssocOp::from_ast_binop(op), lhs, rhs)
 +}
 +
 +#[derive(PartialEq, Eq, Clone, Copy)]
 +/// Operator associativity.
 +enum Associativity {
 +    /// The operator is both left-associative and right-associative.
 +    Both,
 +    /// The operator is left-associative.
 +    Left,
 +    /// The operator is not associative.
 +    None,
 +    /// The operator is right-associative.
 +    Right,
 +}
 +
 +/// Returns the associativity/fixity of an operator. The difference with
 +/// `AssocOp::fixity` is that an operator can be both left and right associative
 +/// (such as `+`: `a + b + c == (a + b) + c == a + (b + c)`.
 +///
 +/// Chained `as` and explicit `:` type coercion never need inner parenthesis so
 +/// they are considered
 +/// associative.
 +#[must_use]
 +fn associativity(op: AssocOp) -> Associativity {
 +    use rustc_ast::util::parser::AssocOp::{
 +        Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Colon, Divide, DotDot, DotDotEq, Equal, Greater,
 +        GreaterEqual, LAnd, LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract,
 +    };
 +
 +    match op {
 +        Assign | AssignOp(_) => Associativity::Right,
 +        Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As | Colon => Associativity::Both,
 +        Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight
 +        | Subtract => Associativity::Left,
 +        DotDot | DotDotEq => Associativity::None,
 +    }
 +}
 +
 +/// Converts a `hir::BinOp` to the corresponding assigning binary operator.
 +fn hirbinop2assignop(op: hir::BinOp) -> AssocOp {
 +    use rustc_ast::token::BinOpToken::{And, Caret, Minus, Or, Percent, Plus, Shl, Shr, Slash, Star};
 +
 +    AssocOp::AssignOp(match op.node {
 +        hir::BinOpKind::Add => Plus,
 +        hir::BinOpKind::BitAnd => And,
 +        hir::BinOpKind::BitOr => Or,
 +        hir::BinOpKind::BitXor => Caret,
 +        hir::BinOpKind::Div => Slash,
 +        hir::BinOpKind::Mul => Star,
 +        hir::BinOpKind::Rem => Percent,
 +        hir::BinOpKind::Shl => Shl,
 +        hir::BinOpKind::Shr => Shr,
 +        hir::BinOpKind::Sub => Minus,
 +
 +        hir::BinOpKind::And
 +        | hir::BinOpKind::Eq
 +        | hir::BinOpKind::Ge
 +        | hir::BinOpKind::Gt
 +        | hir::BinOpKind::Le
 +        | hir::BinOpKind::Lt
 +        | hir::BinOpKind::Ne
 +        | hir::BinOpKind::Or => panic!("This operator does not exist"),
 +    })
 +}
 +
 +/// Converts an `ast::BinOp` to the corresponding assigning binary operator.
 +fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
 +    use rustc_ast::ast::BinOpKind::{
 +        Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
 +    };
 +    use rustc_ast::token::BinOpToken;
 +
 +    AssocOp::AssignOp(match op.node {
 +        Add => BinOpToken::Plus,
 +        BitAnd => BinOpToken::And,
 +        BitOr => BinOpToken::Or,
 +        BitXor => BinOpToken::Caret,
 +        Div => BinOpToken::Slash,
 +        Mul => BinOpToken::Star,
 +        Rem => BinOpToken::Percent,
 +        Shl => BinOpToken::Shl,
 +        Shr => BinOpToken::Shr,
 +        Sub => BinOpToken::Minus,
 +        And | Eq | Ge | Gt | Le | Lt | Ne | Or => panic!("This operator does not exist"),
 +    })
 +}
 +
 +/// Returns the indentation before `span` if there are nothing but `[ \t]`
 +/// before it on its line.
 +fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> {
 +    let lo = cx.sess().source_map().lookup_char_pos(span.lo());
 +    lo.file
 +        .get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */)
 +        .and_then(|line| {
 +            if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
 +                // We can mix char and byte positions here because we only consider `[ \t]`.
 +                if lo.col == CharPos(pos) {
 +                    Some(line[..pos].into())
 +                } else {
 +                    None
 +                }
 +            } else {
 +                None
 +            }
 +        })
 +}
 +
 +/// Convenience extension trait for `Diagnostic`.
 +pub trait DiagnosticExt<T: LintContext> {
 +    /// Suggests to add an attribute to an item.
 +    ///
 +    /// Correctly handles indentation of the attribute and item.
 +    ///
 +    /// # Example
 +    ///
 +    /// ```rust,ignore
 +    /// diag.suggest_item_with_attr(cx, item, "#[derive(Default)]");
 +    /// ```
 +    fn suggest_item_with_attr<D: Display + ?Sized>(
 +        &mut self,
 +        cx: &T,
 +        item: Span,
 +        msg: &str,
 +        attr: &D,
 +        applicability: Applicability,
 +    );
 +
 +    /// Suggest to add an item before another.
 +    ///
 +    /// The item should not be indented (except for inner indentation).
 +    ///
 +    /// # Example
 +    ///
 +    /// ```rust,ignore
 +    /// diag.suggest_prepend_item(cx, item,
 +    /// "fn foo() {
 +    ///     bar();
 +    /// }");
 +    /// ```
 +    fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability);
 +
 +    /// Suggest to completely remove an item.
 +    ///
 +    /// This will remove an item and all following whitespace until the next non-whitespace
 +    /// character. This should work correctly if item is on the same indentation level as the
 +    /// following item.
 +    ///
 +    /// # Example
 +    ///
 +    /// ```rust,ignore
 +    /// diag.suggest_remove_item(cx, item, "remove this")
 +    /// ```
 +    fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability);
 +}
 +
 +impl<T: LintContext> DiagnosticExt<T> for rustc_errors::Diagnostic {
 +    fn suggest_item_with_attr<D: Display + ?Sized>(
 +        &mut self,
 +        cx: &T,
 +        item: Span,
 +        msg: &str,
 +        attr: &D,
 +        applicability: Applicability,
 +    ) {
 +        if let Some(indent) = indentation(cx, item) {
 +            let span = item.with_hi(item.lo());
 +
 +            self.span_suggestion(span, msg, format!("{}\n{}", attr, indent), applicability);
 +        }
 +    }
 +
 +    fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability) {
 +        if let Some(indent) = indentation(cx, item) {
 +            let span = item.with_hi(item.lo());
 +
 +            let mut first = true;
 +            let new_item = new_item
 +                .lines()
 +                .map(|l| {
 +                    if first {
 +                        first = false;
 +                        format!("{}\n", l)
 +                    } else {
 +                        format!("{}{}\n", indent, l)
 +                    }
 +                })
 +                .collect::<String>();
 +
 +            self.span_suggestion(span, msg, format!("{}\n{}", new_item, indent), applicability);
 +        }
 +    }
 +
 +    fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability) {
 +        let mut remove_span = item;
 +        let hi = cx.sess().source_map().next_point(remove_span).hi();
 +        let fmpos = cx.sess().source_map().lookup_byte_offset(hi);
 +
 +        if let Some(ref src) = fmpos.sf.src {
 +            let non_whitespace_offset = src[fmpos.pos.to_usize()..].find(|c| c != ' ' && c != '\t' && c != '\n');
 +
 +            if let Some(non_whitespace_offset) = non_whitespace_offset {
 +                remove_span = remove_span
 +                    .with_hi(remove_span.hi() + BytePos(non_whitespace_offset.try_into().expect("offset too large")));
 +            }
 +        }
 +
 +        self.span_suggestion(remove_span, msg, String::new(), applicability);
 +    }
 +}
 +
 +/// Suggestion results for handling closure
 +/// args dereferencing and borrowing
 +pub struct DerefClosure {
 +    /// confidence on the built suggestion
 +    pub applicability: Applicability,
 +    /// gradually built suggestion
 +    pub suggestion: String,
 +}
 +
 +/// Build suggestion gradually by handling closure arg specific usages,
 +/// such as explicit deref and borrowing cases.
 +/// Returns `None` if no such use cases have been triggered in closure body
 +///
 +/// note: this only works on single line immutable closures with exactly one input parameter.
 +pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<'_>) -> Option<DerefClosure> {
 +    if let hir::ExprKind::Closure(_, fn_decl, body_id, ..) = closure.kind {
 +        let closure_body = cx.tcx.hir().body(body_id);
 +        // is closure arg a type annotated double reference (i.e.: `|x: &&i32| ...`)
 +        // a type annotation is present if param `kind` is different from `TyKind::Infer`
 +        let closure_arg_is_type_annotated_double_ref = if let TyKind::Rptr(_, MutTy { ty, .. }) = fn_decl.inputs[0].kind
 +        {
 +            matches!(ty.kind, TyKind::Rptr(_, MutTy { .. }))
 +        } else {
 +            false
 +        };
 +
 +        let mut visitor = DerefDelegate {
 +            cx,
 +            closure_span: closure.span,
 +            closure_arg_is_type_annotated_double_ref,
 +            next_pos: closure.span.lo(),
 +            suggestion_start: String::new(),
 +            applicability: Applicability::MachineApplicable,
 +        };
 +
 +        let fn_def_id = cx.tcx.hir().local_def_id(closure.hir_id);
 +        cx.tcx.infer_ctxt().enter(|infcx| {
 +            ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results())
 +                .consume_body(closure_body);
 +        });
 +
 +        if !visitor.suggestion_start.is_empty() {
 +            return Some(DerefClosure {
 +                applicability: visitor.applicability,
 +                suggestion: visitor.finish(),
 +            });
 +        }
 +    }
 +    None
 +}
 +
 +/// Visitor struct used for tracking down
 +/// dereferencing and borrowing of closure's args
 +struct DerefDelegate<'a, 'tcx> {
 +    /// The late context of the lint
 +    cx: &'a LateContext<'tcx>,
 +    /// The span of the input closure to adapt
 +    closure_span: Span,
 +    /// Indicates if the arg of the closure is a type annotated double reference
 +    closure_arg_is_type_annotated_double_ref: bool,
 +    /// last position of the span to gradually build the suggestion
 +    next_pos: BytePos,
 +    /// starting part of the gradually built suggestion
 +    suggestion_start: String,
 +    /// confidence on the built suggestion
 +    applicability: Applicability,
 +}
 +
 +impl<'tcx> DerefDelegate<'_, 'tcx> {
 +    /// build final suggestion:
 +    /// - create the ending part of suggestion
 +    /// - concatenate starting and ending parts
 +    /// - potentially remove needless borrowing
 +    pub fn finish(&mut self) -> String {
 +        let end_span = Span::new(self.next_pos, self.closure_span.hi(), self.closure_span.ctxt(), None);
 +        let end_snip = snippet_with_applicability(self.cx, end_span, "..", &mut self.applicability);
 +        let sugg = format!("{}{}", self.suggestion_start, end_snip);
 +        if self.closure_arg_is_type_annotated_double_ref {
 +            sugg.replacen('&', "", 1)
 +        } else {
 +            sugg
 +        }
 +    }
 +
 +    /// indicates whether the function from `parent_expr` takes its args by double reference
 +    fn func_takes_arg_by_double_ref(&self, parent_expr: &'tcx hir::Expr<'_>, cmt_hir_id: HirId) -> bool {
 +        let (call_args, inputs) = match parent_expr.kind {
 +            ExprKind::MethodCall(_, call_args, _) => {
 +                if let Some(method_did) = self.cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) {
 +                    (call_args, self.cx.tcx.fn_sig(method_did).skip_binder().inputs())
 +                } else {
 +                    return false;
 +                }
 +            },
 +            ExprKind::Call(func, call_args) => {
 +                let typ = self.cx.typeck_results().expr_ty(func);
 +                (call_args, typ.fn_sig(self.cx.tcx).skip_binder().inputs())
 +            },
 +            _ => return false,
 +        };
 +
 +        iter::zip(call_args, inputs)
 +            .any(|(arg, ty)| arg.hir_id == cmt_hir_id && matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref()))
 +    }
 +}
 +
 +impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
 +    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
 +
 +    #[allow(clippy::too_many_lines)]
 +    fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {
 +        if let PlaceBase::Local(id) = cmt.place.base {
 +            let map = self.cx.tcx.hir();
 +            let span = map.span(cmt.hir_id);
 +            let start_span = Span::new(self.next_pos, span.lo(), span.ctxt(), None);
 +            let mut start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability);
 +
 +            // identifier referring to the variable currently triggered (i.e.: `fp`)
 +            let ident_str = map.name(id).to_string();
 +            // full identifier that includes projection (i.e.: `fp.field`)
 +            let ident_str_with_proj = snippet(self.cx, span, "..").to_string();
 +
 +            if cmt.place.projections.is_empty() {
 +                // handle item without any projection, that needs an explicit borrowing
 +                // i.e.: suggest `&x` instead of `x`
-                             self.suggestion_start
-                                 .push_str(&format!("{}{}", start_snip, ident_str_with_proj));
++                let _ = write!(self.suggestion_start, "{}&{}", start_snip, ident_str);
 +            } else {
 +                // cases where a parent `Call` or `MethodCall` is using the item
 +                // i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()`
 +                //
 +                // Note about method calls:
 +                // - compiler automatically dereference references if the target type is a reference (works also for
 +                //   function call)
 +                // - `self` arguments in the case of `x.is_something()` are also automatically (de)referenced, and
 +                //   no projection should be suggested
 +                if let Some(parent_expr) = get_parent_expr_for_hir(self.cx, cmt.hir_id) {
 +                    match &parent_expr.kind {
 +                        // given expression is the self argument and will be handled completely by the compiler
 +                        // i.e.: `|x| x.is_something()`
 +                        ExprKind::MethodCall(_, [self_expr, ..], _) if self_expr.hir_id == cmt.hir_id => {
-                 self.suggestion_start
-                     .push_str(&format!("{}{}", start_snip, replacement_str));
++                            let _ = write!(self.suggestion_start, "{}{}", start_snip, ident_str_with_proj);
 +                            self.next_pos = span.hi();
 +                            return;
 +                        },
 +                        // item is used in a call
 +                        // i.e.: `Call`: `|x| please(x)` or `MethodCall`: `|x| [1, 2, 3].contains(x)`
 +                        ExprKind::Call(_, [call_args @ ..]) | ExprKind::MethodCall(_, [_, call_args @ ..], _) => {
 +                            let expr = self.cx.tcx.hir().expect_expr(cmt.hir_id);
 +                            let arg_ty_kind = self.cx.typeck_results().expr_ty(expr).kind();
 +
 +                            if matches!(arg_ty_kind, ty::Ref(_, _, Mutability::Not)) {
 +                                // suggest ampersand if call function is taking args by double reference
 +                                let takes_arg_by_double_ref =
 +                                    self.func_takes_arg_by_double_ref(parent_expr, cmt.hir_id);
 +
 +                                // compiler will automatically dereference field or index projection, so no need
 +                                // to suggest ampersand, but full identifier that includes projection is required
 +                                let has_field_or_index_projection =
 +                                    cmt.place.projections.iter().any(|proj| {
 +                                        matches!(proj.kind, ProjectionKind::Field(..) | ProjectionKind::Index)
 +                                    });
 +
 +                                // no need to bind again if the function doesn't take arg by double ref
 +                                // and if the item is already a double ref
 +                                let ident_sugg = if !call_args.is_empty()
 +                                    && !takes_arg_by_double_ref
 +                                    && (self.closure_arg_is_type_annotated_double_ref || has_field_or_index_projection)
 +                                {
 +                                    let ident = if has_field_or_index_projection {
 +                                        ident_str_with_proj
 +                                    } else {
 +                                        ident_str
 +                                    };
 +                                    format!("{}{}", start_snip, ident)
 +                                } else {
 +                                    format!("{}&{}", start_snip, ident_str)
 +                                };
 +                                self.suggestion_start.push_str(&ident_sugg);
 +                                self.next_pos = span.hi();
 +                                return;
 +                            }
 +
 +                            self.applicability = Applicability::Unspecified;
 +                        },
 +                        _ => (),
 +                    }
 +                }
 +
 +                let mut replacement_str = ident_str;
 +                let mut projections_handled = false;
 +                cmt.place.projections.iter().enumerate().for_each(|(i, proj)| {
 +                    match proj.kind {
 +                        // Field projection like `|v| v.foo`
 +                        // no adjustment needed here, as field projections are handled by the compiler
 +                        ProjectionKind::Field(..) => match cmt.place.ty_before_projection(i).kind() {
 +                            ty::Adt(..) | ty::Tuple(_) => {
 +                                replacement_str = ident_str_with_proj.clone();
 +                                projections_handled = true;
 +                            },
 +                            _ => (),
 +                        },
 +                        // Index projection like `|x| foo[x]`
 +                        // the index is dropped so we can't get it to build the suggestion,
 +                        // so the span is set-up again to get more code, using `span.hi()` (i.e.: `foo[x]`)
 +                        // instead of `span.lo()` (i.e.: `foo`)
 +                        ProjectionKind::Index => {
 +                            let start_span = Span::new(self.next_pos, span.hi(), span.ctxt(), None);
 +                            start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability);
 +                            replacement_str.clear();
 +                            projections_handled = true;
 +                        },
 +                        // note: unable to trigger `Subslice` kind in tests
 +                        ProjectionKind::Subslice => (),
 +                        ProjectionKind::Deref => {
 +                            // Explicit derefs are typically handled later on, but
 +                            // some items do not need explicit deref, such as array accesses,
 +                            // so we mark them as already processed
 +                            // i.e.: don't suggest `*sub[1..4].len()` for `|sub| sub[1..4].len() == 3`
 +                            if let ty::Ref(_, inner, _) = cmt.place.ty_before_projection(i).kind() {
 +                                if matches!(inner.kind(), ty::Ref(_, innermost, _) if innermost.is_array()) {
 +                                    projections_handled = true;
 +                                }
 +                            }
 +                        },
 +                    }
 +                });
 +
 +                // handle `ProjectionKind::Deref` by removing one explicit deref
 +                // if no special case was detected (i.e.: suggest `*x` instead of `**x`)
 +                if !projections_handled {
 +                    let last_deref = cmt
 +                        .place
 +                        .projections
 +                        .iter()
 +                        .rposition(|proj| proj.kind == ProjectionKind::Deref);
 +
 +                    if let Some(pos) = last_deref {
 +                        let mut projections = cmt.place.projections.clone();
 +                        projections.truncate(pos);
 +
 +                        for item in projections {
 +                            if item.kind == ProjectionKind::Deref {
 +                                replacement_str = format!("*{}", replacement_str);
 +                            }
 +                        }
 +                    }
 +                }
 +
++                let _ = write!(self.suggestion_start, "{}{}", start_snip, replacement_str);
 +            }
 +            self.next_pos = span.hi();
 +        }
 +    }
 +
 +    fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
 +
 +    fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use super::Sugg;
 +
 +    use rustc_ast::util::parser::AssocOp;
 +    use std::borrow::Cow;
 +
 +    const SUGGESTION: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("function_call()"));
 +
 +    #[test]
 +    fn make_return_transform_sugg_into_a_return_call() {
 +        assert_eq!("return function_call()", SUGGESTION.make_return().to_string());
 +    }
 +
 +    #[test]
 +    fn blockify_transforms_sugg_into_a_block() {
 +        assert_eq!("{ function_call() }", SUGGESTION.blockify().to_string());
 +    }
 +
 +    #[test]
 +    fn binop_maybe_par() {
 +        let sugg = Sugg::BinOp(AssocOp::Add, "1".into(), "1".into());
 +        assert_eq!("(1 + 1)", sugg.maybe_par().to_string());
 +
 +        let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1)".into(), "(1 + 1)".into());
 +        assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_par().to_string());
 +    }
 +    #[test]
 +    fn not_op() {
 +        use AssocOp::{Add, Equal, Greater, GreaterEqual, LAnd, LOr, Less, LessEqual, NotEqual};
 +
 +        fn test_not(op: AssocOp, correct: &str) {
 +            let sugg = Sugg::BinOp(op, "x".into(), "y".into());
 +            assert_eq!((!sugg).to_string(), correct);
 +        }
 +
 +        // Invert the comparison operator.
 +        test_not(Equal, "x != y");
 +        test_not(NotEqual, "x == y");
 +        test_not(Less, "x >= y");
 +        test_not(LessEqual, "x > y");
 +        test_not(Greater, "x <= y");
 +        test_not(GreaterEqual, "x < y");
 +
 +        // Other operators are inverted like !(..).
 +        test_not(Add, "!(x + y)");
 +        test_not(LAnd, "!(x && y)");
 +        test_not(LOr, "!(x || y)");
 +    }
 +}
index e3fc76f4e1a26b16754cb1318df3745a8b1989a0,0000000000000000000000000000000000000000..901e3e5390c5dbbe9a4ac6d54a6adaaa0a737ec7
mode 100644,000000..100644
--- /dev/null
@@@ -1,592 -1,0 +1,657 @@@
- use rustc_data_structures::fx::FxHashMap;
 +//! Util methods for [`rustc_middle::ty`]
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use rustc_ast::ast::Mutability;
- use rustc_hir::{Expr, TyKind, Unsafety};
++use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_hir as hir;
 +use rustc_hir::def::{CtorKind, DefKind, Res};
 +use rustc_hir::def_id::DefId;
- use crate::{match_def_path, must_use_attr, path_res};
++use rustc_hir::{Expr, LangItem, TyKind, Unsafety};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::LateContext;
 +use rustc_middle::mir::interpret::{ConstValue, Scalar};
 +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
 +use rustc_middle::ty::{
 +    self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, VariantDiscr,
 +};
 +use rustc_span::symbol::Ident;
 +use rustc_span::{sym, Span, Symbol, DUMMY_SP};
 +use rustc_target::abi::{Size, VariantIdx};
 +use rustc_trait_selection::infer::InferCtxtExt;
 +use rustc_trait_selection::traits::query::normalize::AtExt;
 +use std::iter;
 +
++use crate::{match_def_path, must_use_attr, path_res, paths};
 +
 +// Checks if the given type implements copy.
 +pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env)
 +}
 +
 +/// Checks whether a type can be partially moved.
 +pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    if has_drop(cx, ty) || is_copy(cx, ty) {
 +        return false;
 +    }
 +    match ty.kind() {
 +        ty::Param(_) => false,
 +        ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))),
 +        _ => true,
 +    }
 +}
 +
 +/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
 +pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool {
 +    ty.walk().any(|inner| match inner.unpack() {
 +        GenericArgKind::Type(inner_ty) => other_ty == inner_ty,
 +        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
 +    })
 +}
 +
 +/// Walks into `ty` and returns `true` if any inner type is an instance of the given adt
 +/// constructor.
 +pub fn contains_adt_constructor(ty: Ty<'_>, adt: AdtDef<'_>) -> bool {
 +    ty.walk().any(|inner| match inner.unpack() {
 +        GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt),
 +        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
 +    })
 +}
 +
 +/// Resolves `<T as Iterator>::Item` for `T`
 +/// Do not invoke without first verifying that the type implements `Iterator`
 +pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
 +    cx.tcx
 +        .get_diagnostic_item(sym::Iterator)
 +        .and_then(|iter_did| get_associated_type(cx, ty, iter_did, "Item"))
 +}
 +
 +/// Returns the associated type `name` for `ty` as an implementation of `trait_id`.
 +/// Do not invoke without first verifying that the type implements the trait.
 +pub fn get_associated_type<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: Ty<'tcx>,
 +    trait_id: DefId,
 +    name: &str,
 +) -> Option<Ty<'tcx>> {
 +    cx.tcx
 +        .associated_items(trait_id)
 +        .find_by_name_and_kind(cx.tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id)
 +        .map(|assoc| {
 +            let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[]));
 +            cx.tcx.normalize_erasing_regions(cx.param_env, proj)
 +        })
 +}
 +
++/// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type
++/// implements a trait marked with a diagnostic item use [`implements_trait`].
++///
++/// For a further exploitation what diagnostic items are see [diagnostic items] in
++/// rustc-dev-guide.
++///
++/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
++pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symbol> {
++    match ty.kind() {
++        ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()),
++        _ => None,
++    }
++}
++
 +/// Returns true if ty has `iter` or `iter_mut` methods
 +pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
 +    // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
 +    // exists and has the desired signature. Unfortunately FnCtxt is not exported
 +    // so we can't use its `lookup_method` method.
 +    let into_iter_collections: &[Symbol] = &[
 +        sym::Vec,
 +        sym::Option,
 +        sym::Result,
 +        sym::BTreeMap,
 +        sym::BTreeSet,
 +        sym::VecDeque,
 +        sym::LinkedList,
 +        sym::BinaryHeap,
 +        sym::HashSet,
 +        sym::HashMap,
 +        sym::PathBuf,
 +        sym::Path,
 +        sym::Receiver,
 +    ];
 +
 +    let ty_to_check = match probably_ref_ty.kind() {
 +        ty::Ref(_, ty_to_check, _) => *ty_to_check,
 +        _ => probably_ref_ty,
 +    };
 +
 +    let def_id = match ty_to_check.kind() {
 +        ty::Array(..) => return Some(sym::array),
 +        ty::Slice(..) => return Some(sym::slice),
 +        ty::Adt(adt, _) => adt.did(),
 +        _ => return None,
 +    };
 +
 +    for &name in into_iter_collections {
 +        if cx.tcx.is_diagnostic_item(name, def_id) {
 +            return Some(cx.tcx.item_name(def_id));
 +        }
 +    }
 +    None
 +}
 +
 +/// Checks whether a type implements a trait.
 +/// The function returns false in case the type contains an inference variable.
 +///
 +/// See:
 +/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`].
 +/// * [Common tools for writing lints] for an example how to use this function and other options.
 +///
 +/// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
 +pub fn implements_trait<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: Ty<'tcx>,
 +    trait_id: DefId,
 +    ty_params: &[GenericArg<'tcx>],
 +) -> bool {
 +    // Clippy shouldn't have infer types
 +    assert!(!ty.needs_infer());
 +
 +    let ty = cx.tcx.erase_regions(ty);
 +    if ty.has_escaping_bound_vars() {
 +        return false;
 +    }
 +    let ty_params = cx.tcx.mk_substs(ty_params.iter());
 +    cx.tcx.infer_ctxt().enter(|infcx| {
 +        infcx
 +            .type_implements_trait(trait_id, ty, ty_params, cx.param_env)
 +            .must_apply_modulo_regions()
 +    })
 +}
 +
 +/// Checks whether this type implements `Drop`.
 +pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    match ty.ty_adt_def() {
 +        Some(def) => def.has_dtor(cx.tcx),
 +        None => false,
 +    }
 +}
 +
 +// Returns whether the type has #[must_use] attribute
 +pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => must_use_attr(cx.tcx.get_attrs(adt.did())).is_some(),
 +        ty::Foreign(ref did) => must_use_attr(cx.tcx.get_attrs(*did)).is_some(),
 +        ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => {
 +            // for the Array case we don't need to care for the len == 0 case
 +            // because we don't want to lint functions returning empty arrays
 +            is_must_use_ty(cx, *ty)
 +        },
 +        ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)),
 +        ty::Opaque(ref def_id, _) => {
 +            for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
 +                if let ty::PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() {
 +                    if must_use_attr(cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() {
 +                        return true;
 +                    }
 +                }
 +            }
 +            false
 +        },
 +        ty::Dynamic(binder, _) => {
 +            for predicate in binder.iter() {
 +                if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
 +                    if must_use_attr(cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
 +                        return true;
 +                    }
 +                }
 +            }
 +            false
 +        },
 +        _ => false,
 +    }
 +}
 +
 +// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
 +// this function can be removed once the `normalize` method does not panic when normalization does
 +// not succeed
 +/// Checks if `Ty` is normalizable. This function is useful
 +/// to avoid crashes on `layout_of`.
 +pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
 +    is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default())
 +}
 +
 +fn is_normalizable_helper<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    param_env: ty::ParamEnv<'tcx>,
 +    ty: Ty<'tcx>,
 +    cache: &mut FxHashMap<Ty<'tcx>, bool>,
 +) -> bool {
 +    if let Some(&cached_result) = cache.get(&ty) {
 +        return cached_result;
 +    }
 +    // prevent recursive loops, false-negative is better than endless loop leading to stack overflow
 +    cache.insert(ty, false);
 +    let result = cx.tcx.infer_ctxt().enter(|infcx| {
 +        let cause = rustc_middle::traits::ObligationCause::dummy();
 +        if infcx.at(&cause, param_env).normalize(ty).is_ok() {
 +            match ty.kind() {
 +                ty::Adt(def, substs) => def.variants().iter().all(|variant| {
 +                    variant
 +                        .fields
 +                        .iter()
 +                        .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
 +                }),
 +                _ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
 +                    GenericArgKind::Type(inner_ty) if inner_ty != ty => {
 +                        is_normalizable_helper(cx, param_env, inner_ty, cache)
 +                    },
 +                    _ => true, // if inner_ty == ty, we've already checked it
 +                }),
 +            }
 +        } else {
 +            false
 +        }
 +    });
 +    cache.insert(ty, result);
 +    result
 +}
 +
 +/// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any
 +/// integer or floating-point number type). For checking aggregation of primitive types (e.g.
 +/// tuples and slices of primitive type) see `is_recursively_primitive_type`
 +pub fn is_non_aggregate_primitive_type(ty: Ty<'_>) -> bool {
 +    matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_))
 +}
 +
 +/// Returns `true` if the given type is a primitive (a `bool` or `char`, any integer or
 +/// floating-point number type, a `str`, or an array, slice, or tuple of those types).
 +pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
 +    match *ty.kind() {
 +        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
 +        ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
 +        ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
 +        ty::Tuple(inner_types) => inner_types.iter().all(is_recursively_primitive_type),
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the type is a reference equals to a diagnostic item
 +pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
 +    match ty.kind() {
 +        ty::Ref(_, ref_ty, _) => match ref_ty.kind() {
 +            ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()),
 +            _ => false,
 +        },
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the type is equal to a diagnostic item. To check if a type implements a
 +/// trait marked with a diagnostic item use [`implements_trait`].
 +///
 +/// For a further exploitation what diagnostic items are see [diagnostic items] in
 +/// rustc-dev-guide.
 +///
 +/// ---
 +///
 +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
 +///
 +/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
 +pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()),
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the type is equal to a lang item.
 +///
 +/// Returns `false` if the `LangItem` is not defined.
 +pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => cx
 +            .tcx
 +            .lang_items()
 +            .require(lang_item)
 +            .map_or(false, |li| li == adt.did()),
 +        _ => false,
 +    }
 +}
 +
 +/// Return `true` if the passed `typ` is `isize` or `usize`.
 +pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
 +    matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
 +}
 +
 +/// Checks if type is struct, enum or union type with the given def path.
 +///
 +/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
 +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
 +pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => match_def_path(cx, adt.did(), path),
 +        _ => false,
 +    }
 +}
 +
++/// Checks if the drop order for a type matters. Some std types implement drop solely to
++/// deallocate memory. For these types, and composites containing them, changing the drop order
++/// won't result in any observable side effects.
++pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
++    fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
++        if !seen.insert(ty) {
++            return false;
++        }
++        if !ty.has_significant_drop(cx.tcx, cx.param_env) {
++            false
++        }
++        // Check for std types which implement drop, but only for memory allocation.
++        else if is_type_lang_item(cx, ty, LangItem::OwnedBox)
++            || matches!(
++                get_type_diagnostic_name(cx, ty),
++                Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type)
++            )
++            || match_type(cx, ty, &paths::WEAK_RC)
++            || match_type(cx, ty, &paths::WEAK_ARC)
++        {
++            // Check all of the generic arguments.
++            if let ty::Adt(_, subs) = ty.kind() {
++                subs.types().any(|ty| needs_ordered_drop_inner(cx, ty, seen))
++            } else {
++                true
++            }
++        } else if !cx
++            .tcx
++            .lang_items()
++            .drop_trait()
++            .map_or(false, |id| implements_trait(cx, ty, id, &[]))
++        {
++            // This type doesn't implement drop, so no side effects here.
++            // Check if any component type has any.
++            match ty.kind() {
++                ty::Tuple(fields) => fields.iter().any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
++                ty::Array(ty, _) => needs_ordered_drop_inner(cx, *ty, seen),
++                ty::Adt(adt, subs) => adt
++                    .all_fields()
++                    .map(|f| f.ty(cx.tcx, subs))
++                    .any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
++                _ => true,
++            }
++        } else {
++            true
++        }
++    }
++
++    needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
++}
++
 +/// Peels off all references on the type. Returns the underlying type and the number of references
 +/// removed.
 +pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
 +    fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
 +        if let ty::Ref(_, ty, _) = ty.kind() {
 +            peel(*ty, count + 1)
 +        } else {
 +            (ty, count)
 +        }
 +    }
 +    peel(ty, 0)
 +}
 +
 +/// Peels off all references on the type.Returns the underlying type, the number of references
 +/// removed, and whether the pointer is ultimately mutable or not.
 +pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
 +    fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
 +        match ty.kind() {
 +            ty::Ref(_, ty, Mutability::Mut) => f(*ty, count + 1, mutability),
 +            ty::Ref(_, ty, Mutability::Not) => f(*ty, count + 1, Mutability::Not),
 +            _ => (ty, count, mutability),
 +        }
 +    }
 +    f(ty, 0, Mutability::Mut)
 +}
 +
 +/// Returns `true` if the given type is an `unsafe` function.
 +pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    match ty.kind() {
 +        ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
 +        _ => false,
 +    }
 +}
 +
 +/// Returns the base type for HIR references and pointers.
 +pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
 +    match ty.kind {
 +        TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty),
 +        _ => ty,
 +    }
 +}
 +
 +/// Returns the base type for references and raw pointers, and count reference
 +/// depth.
 +pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
 +    fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
 +        match ty.kind() {
 +            ty::Ref(_, ty, _) => inner(*ty, depth + 1),
 +            _ => (ty, depth),
 +        }
 +    }
 +    inner(ty, 0)
 +}
 +
 +/// Returns `true` if types `a` and `b` are same types having same `Const` generic args,
 +/// otherwise returns `false`
 +pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
 +    match (&a.kind(), &b.kind()) {
 +        (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => {
 +            if did_a != did_b {
 +                return false;
 +            }
 +
 +            substs_a
 +                .iter()
 +                .zip(substs_b.iter())
 +                .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) {
 +                    (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b,
 +                    (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => {
 +                        same_type_and_consts(type_a, type_b)
 +                    },
 +                    _ => true,
 +                })
 +        },
 +        _ => a == b,
 +    }
 +}
 +
 +/// Checks if a given type looks safe to be uninitialized.
 +pub fn is_uninit_value_valid_for_ty(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
 +    match *ty.kind() {
 +        ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component),
 +        ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)),
 +        ty::Adt(adt, _) => cx.tcx.lang_items().maybe_uninit() == Some(adt.did()),
 +        _ => false,
 +    }
 +}
 +
 +/// Gets an iterator over all predicates which apply to the given item.
 +pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(Predicate<'_>, Span)> {
 +    let mut next_id = Some(id);
 +    iter::from_fn(move || {
 +        next_id.take().map(|id| {
 +            let preds = tcx.predicates_of(id);
 +            next_id = preds.parent;
 +            preds.predicates.iter()
 +        })
 +    })
 +    .flatten()
 +}
 +
 +/// A signature for a function like type.
 +#[derive(Clone, Copy)]
 +pub enum ExprFnSig<'tcx> {
 +    Sig(Binder<'tcx, FnSig<'tcx>>),
 +    Closure(Binder<'tcx, FnSig<'tcx>>),
 +    Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>),
 +}
 +impl<'tcx> ExprFnSig<'tcx> {
 +    /// Gets the argument type at the given offset.
 +    pub fn input(self, i: usize) -> Binder<'tcx, Ty<'tcx>> {
 +        match self {
 +            Self::Sig(sig) => sig.input(i),
 +            Self::Closure(sig) => sig.input(0).map_bound(|ty| ty.tuple_fields()[i]),
 +            Self::Trait(inputs, _) => inputs.map_bound(|ty| ty.tuple_fields()[i]),
 +        }
 +    }
 +
 +    /// Gets the result type, if one could be found. Note that the result type of a trait may not be
 +    /// specified.
 +    pub fn output(self) -> Option<Binder<'tcx, Ty<'tcx>>> {
 +        match self {
 +            Self::Sig(sig) | Self::Closure(sig) => Some(sig.output()),
 +            Self::Trait(_, output) => output,
 +        }
 +    }
 +}
 +
 +/// If the expression is function like, get the signature for it.
 +pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
 +    if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
 +        Some(ExprFnSig::Sig(cx.tcx.fn_sig(id)))
 +    } else {
 +        let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs();
 +        match *ty.kind() {
 +            ty::Closure(_, subs) => Some(ExprFnSig::Closure(subs.as_closure().sig())),
 +            ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).subst(cx.tcx, subs))),
 +            ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)),
 +            ty::Dynamic(bounds, _) => {
 +                let lang_items = cx.tcx.lang_items();
 +                match bounds.principal() {
 +                    Some(bound)
 +                        if Some(bound.def_id()) == lang_items.fn_trait()
 +                            || Some(bound.def_id()) == lang_items.fn_once_trait()
 +                            || Some(bound.def_id()) == lang_items.fn_mut_trait() =>
 +                    {
 +                        let output = bounds
 +                            .projection_bounds()
 +                            .find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
 +                            .map(|p| p.map_bound(|p| p.term.ty().expect("return type was a const")));
 +                        Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output))
 +                    },
 +                    _ => None,
 +                }
 +            },
 +            ty::Param(_) | ty::Projection(..) => {
 +                let mut inputs = None;
 +                let mut output = None;
 +                let lang_items = cx.tcx.lang_items();
 +
 +                for (pred, _) in all_predicates_of(cx.tcx, cx.typeck_results().hir_owner.to_def_id()) {
 +                    let mut is_input = false;
 +                    if let Some(ty) = pred
 +                        .kind()
 +                        .map_bound(|pred| match pred {
 +                            PredicateKind::Trait(p)
 +                                if (lang_items.fn_trait() == Some(p.def_id())
 +                                    || lang_items.fn_mut_trait() == Some(p.def_id())
 +                                    || lang_items.fn_once_trait() == Some(p.def_id()))
 +                                    && p.self_ty() == ty =>
 +                            {
 +                                is_input = true;
 +                                Some(p.trait_ref.substs.type_at(1))
 +                            },
 +                            PredicateKind::Projection(p)
 +                                if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
 +                                    && p.projection_ty.self_ty() == ty =>
 +                            {
 +                                is_input = false;
 +                                p.term.ty()
 +                            },
 +                            _ => None,
 +                        })
 +                        .transpose()
 +                    {
 +                        if is_input && inputs.is_none() {
 +                            inputs = Some(ty);
 +                        } else if !is_input && output.is_none() {
 +                            output = Some(ty);
 +                        } else {
 +                            // Multiple different fn trait impls. Is this even allowed?
 +                            return None;
 +                        }
 +                    }
 +                }
 +
 +                inputs.map(|ty| ExprFnSig::Trait(ty, output))
 +            },
 +            _ => None,
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +pub enum EnumValue {
 +    Unsigned(u128),
 +    Signed(i128),
 +}
 +impl core::ops::Add<u32> for EnumValue {
 +    type Output = Self;
 +    fn add(self, n: u32) -> Self::Output {
 +        match self {
 +            Self::Unsigned(x) => Self::Unsigned(x + u128::from(n)),
 +            Self::Signed(x) => Self::Signed(x + i128::from(n)),
 +        }
 +    }
 +}
 +
 +/// Attempts to read the given constant as though it were an an enum value.
 +#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
 +pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option<EnumValue> {
 +    if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) {
 +        match tcx.type_of(id).kind() {
 +            ty::Int(_) => Some(EnumValue::Signed(match value.size().bytes() {
 +                1 => i128::from(value.assert_bits(Size::from_bytes(1)) as u8 as i8),
 +                2 => i128::from(value.assert_bits(Size::from_bytes(2)) as u16 as i16),
 +                4 => i128::from(value.assert_bits(Size::from_bytes(4)) as u32 as i32),
 +                8 => i128::from(value.assert_bits(Size::from_bytes(8)) as u64 as i64),
 +                16 => value.assert_bits(Size::from_bytes(16)) as i128,
 +                _ => return None,
 +            })),
 +            ty::Uint(_) => Some(EnumValue::Unsigned(match value.size().bytes() {
 +                1 => value.assert_bits(Size::from_bytes(1)),
 +                2 => value.assert_bits(Size::from_bytes(2)),
 +                4 => value.assert_bits(Size::from_bytes(4)),
 +                8 => value.assert_bits(Size::from_bytes(8)),
 +                16 => value.assert_bits(Size::from_bytes(16)),
 +                _ => return None,
 +            })),
 +            _ => None,
 +        }
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Gets the value of the given variant.
 +pub fn get_discriminant_value(tcx: TyCtxt<'_>, adt: AdtDef<'_>, i: VariantIdx) -> EnumValue {
 +    let variant = &adt.variant(i);
 +    match variant.discr {
 +        VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap(),
 +        VariantDiscr::Relative(x) => match adt.variant((i.as_usize() - x as usize).into()).discr {
 +            VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap() + x,
 +            VariantDiscr::Relative(_) => EnumValue::Unsigned(x.into()),
 +        },
 +    }
 +}
 +
 +/// Check if the given type is either `core::ffi::c_void`, `std::os::raw::c_void`, or one of the
 +/// platform specific `libc::<platform>::c_void` types in libc.
 +pub fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
 +    if let ty::Adt(adt, _) = ty.kind()
 +        && let &[krate, .., name] = &*cx.get_def_path(adt.did())
 +        && let sym::libc | sym::core | sym::std = krate
 +        && name.as_str() == "c_void"
 +    {
 +        true
 +    } else {
 +        false
 +    }
 +}
index 405e306359bc9b2dfe46278b43c4d5e3c6307a90,0000000000000000000000000000000000000000..4236e3aae2fbde4f811106b27547ba1f8276648b
mode 100644,000000..100644
--- /dev/null
@@@ -1,188 -1,0 +1,217 @@@
- use rustc_hir::{Expr, ExprKind, HirId};
 +use crate as utils;
 +use crate::visitors::{expr_visitor, expr_visitor_no_bodies};
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{self, Visitor};
 +use rustc_hir::HirIdSet;
-         } else if past_expr && utils::path_to_local_id(expr, local_id) {
++use rustc_hir::{Expr, ExprKind, HirId, Node};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::mir::FakeReadCause;
 +use rustc_middle::ty;
 +use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 +
 +/// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined.
 +pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option<HirIdSet> {
 +    let mut delegate = MutVarsDelegate {
 +        used_mutably: HirIdSet::default(),
 +        skip: false,
 +    };
 +    cx.tcx.infer_ctxt().enter(|infcx| {
 +        ExprUseVisitor::new(
 +            &mut delegate,
 +            &infcx,
 +            expr.hir_id.owner,
 +            cx.param_env,
 +            cx.typeck_results(),
 +        )
 +        .walk_expr(expr);
 +    });
 +
 +    if delegate.skip {
 +        return None;
 +    }
 +    Some(delegate.used_mutably)
 +}
 +
 +pub fn is_potentially_mutated<'tcx>(variable: HirId, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
 +    mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&variable))
 +}
 +
 +struct MutVarsDelegate {
 +    used_mutably: HirIdSet,
 +    skip: bool,
 +}
 +
 +impl<'tcx> MutVarsDelegate {
 +    #[allow(clippy::similar_names)]
 +    fn update(&mut self, cat: &PlaceWithHirId<'tcx>) {
 +        match cat.place.base {
 +            PlaceBase::Local(id) => {
 +                self.used_mutably.insert(id);
 +            },
 +            PlaceBase::Upvar(_) => {
 +                //FIXME: This causes false negatives. We can't get the `NodeId` from
 +                //`Categorization::Upvar(_)`. So we search for any `Upvar`s in the
 +                //`while`-body, not just the ones in the condition.
 +                self.skip = true;
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
 +    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
 +
 +    fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, bk: ty::BorrowKind) {
 +        if bk == ty::BorrowKind::MutBorrow {
 +            self.update(cmt);
 +        }
 +    }
 +
 +    fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
 +        self.update(cmt);
 +    }
 +
 +    fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
 +}
 +
 +pub struct ParamBindingIdCollector {
 +    pub binding_hir_ids: Vec<hir::HirId>,
 +}
 +impl<'tcx> ParamBindingIdCollector {
 +    fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec<hir::HirId> {
 +        let mut hir_ids: Vec<hir::HirId> = Vec::new();
 +        for param in body.params.iter() {
 +            let mut finder = ParamBindingIdCollector {
 +                binding_hir_ids: Vec::new(),
 +            };
 +            finder.visit_param(param);
 +            for hir_id in &finder.binding_hir_ids {
 +                hir_ids.push(*hir_id);
 +            }
 +        }
 +        hir_ids
 +    }
 +}
 +impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector {
 +    fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
 +        if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind {
 +            self.binding_hir_ids.push(hir_id);
 +        }
 +        intravisit::walk_pat(self, pat);
 +    }
 +}
 +
 +pub struct BindingUsageFinder<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    binding_ids: Vec<hir::HirId>,
 +    usage_found: bool,
 +}
 +impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> {
 +    pub fn are_params_used(cx: &'a LateContext<'tcx>, body: &'tcx hir::Body<'tcx>) -> bool {
 +        let mut finder = BindingUsageFinder {
 +            cx,
 +            binding_ids: ParamBindingIdCollector::collect_binding_hir_ids(body),
 +            usage_found: false,
 +        };
 +        finder.visit_body(body);
 +        finder.usage_found
 +    }
 +}
 +impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> {
 +    type NestedFilter = nested_filter::OnlyBodies;
 +
 +    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
 +        if !self.usage_found {
 +            intravisit::walk_expr(self, expr);
 +        }
 +    }
 +
 +    fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
 +        if let hir::def::Res::Local(id) = path.res {
 +            if self.binding_ids.contains(&id) {
 +                self.usage_found = true;
 +            }
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
 +
 +pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
 +    let mut seen_return_break_continue = false;
 +    expr_visitor_no_bodies(|ex| {
 +        if seen_return_break_continue {
 +            return false;
 +        }
 +        match &ex.kind {
 +            ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => {
 +                seen_return_break_continue = true;
 +            },
 +            // Something special could be done here to handle while or for loop
 +            // desugaring, as this will detect a break if there's a while loop
 +            // or a for loop inside the expression.
 +            _ => {
 +                if ex.span.from_expansion() {
 +                    seen_return_break_continue = true;
 +                }
 +            },
 +        }
 +        !seen_return_break_continue
 +    })
 +    .visit_expr(expression);
 +    seen_return_break_continue
 +}
 +
 +pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool {
 +    let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false };
++
++    // for _ in 1..3 {
++    //    local
++    // }
++    //
++    // let closure = || local;
++    // closure();
++    // closure();
++    let in_loop_or_closure = cx
++        .tcx
++        .hir()
++        .parent_iter(after.hir_id)
++        .take_while(|&(id, _)| id != block.hir_id)
++        .any(|(_, node)| {
++            matches!(
++                node,
++                Node::Expr(Expr {
++                    kind: ExprKind::Loop(..) | ExprKind::Closure(..),
++                    ..
++                })
++            )
++        });
++    if in_loop_or_closure {
++        return true;
++    }
++
 +    let mut used_after_expr = false;
 +    let mut past_expr = false;
 +    expr_visitor(cx, |expr| {
 +        if used_after_expr {
 +            return false;
 +        }
 +
 +        if expr.hir_id == after.hir_id {
 +            past_expr = true;
++            return false;
++        }
++
++        if past_expr && utils::path_to_local_id(expr, local_id) {
 +            used_after_expr = true;
 +        }
 +        !used_after_expr
 +    })
 +    .visit_block(block);
 +    used_after_expr
 +}
index 40451b17a9c63ce30693fbf2ab7147b2a020f284,0000000000000000000000000000000000000000..c00bc2bd213f9a4fd3c8469fe16f0273167b1191
mode 100644,000000..100644
--- /dev/null
@@@ -1,372 -1,0 +1,438 @@@
-     Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, Unsafety,
 +use crate::path_to_local_id;
++use core::ops::ControlFlow;
 +use rustc_hir as hir;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
 +use rustc_hir::{
++    Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, UnsafeSource,
++    Unsafety,
 +};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::ty;
 +
 +/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
 +/// bodies (i.e. closures) are visited.
 +/// If the callback returns `true`, the expr just provided to the callback is walked.
 +#[must_use]
 +pub fn expr_visitor<'tcx>(cx: &LateContext<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
 +    struct V<'tcx, F> {
 +        hir: Map<'tcx>,
 +        f: F,
 +    }
 +    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<'tcx, F> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.hir
 +        }
 +
 +        fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 +            if (self.f)(expr) {
 +                walk_expr(self, expr);
 +            }
 +        }
 +    }
 +    V { hir: cx.tcx.hir(), f }
 +}
 +
 +/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
 +/// bodies (i.e. closures) are not visited.
 +/// If the callback returns `true`, the expr just provided to the callback is walked.
 +#[must_use]
 +pub fn expr_visitor_no_bodies<'tcx>(f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
 +    struct V<F>(F);
 +    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<F> {
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if (self.0)(e) {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +    V(f)
 +}
 +
 +/// returns `true` if expr contains match expr desugared from try
 +fn contains_try(expr: &hir::Expr<'_>) -> bool {
 +    let mut found = false;
 +    expr_visitor_no_bodies(|e| {
 +        if !found {
 +            found = matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar));
 +        }
 +        !found
 +    })
 +    .visit_expr(expr);
 +    found
 +}
 +
 +pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
 +where
 +    F: FnMut(&'hir hir::Expr<'hir>) -> bool,
 +{
 +    struct RetFinder<F> {
 +        in_stmt: bool,
 +        failed: bool,
 +        cb: F,
 +    }
 +
 +    struct WithStmtGuarg<'a, F> {
 +        val: &'a mut RetFinder<F>,
 +        prev_in_stmt: bool,
 +    }
 +
 +    impl<F> RetFinder<F> {
 +        fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
 +            let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
 +            WithStmtGuarg {
 +                val: self,
 +                prev_in_stmt,
 +            }
 +        }
 +    }
 +
 +    impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
 +        type Target = RetFinder<F>;
 +
 +        fn deref(&self) -> &Self::Target {
 +            self.val
 +        }
 +    }
 +
 +    impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
 +        fn deref_mut(&mut self) -> &mut Self::Target {
 +            self.val
 +        }
 +    }
 +
 +    impl<F> Drop for WithStmtGuarg<'_, F> {
 +        fn drop(&mut self) {
 +            self.val.in_stmt = self.prev_in_stmt;
 +        }
 +    }
 +
 +    impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder<F> {
 +        fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) {
 +            intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt);
 +        }
 +
 +        fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) {
 +            if self.failed {
 +                return;
 +            }
 +            if self.in_stmt {
 +                match expr.kind {
 +                    hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
 +                    _ => intravisit::walk_expr(self, expr),
 +                }
 +            } else {
 +                match expr.kind {
 +                    hir::ExprKind::If(cond, then, else_opt) => {
 +                        self.inside_stmt(true).visit_expr(cond);
 +                        self.visit_expr(then);
 +                        if let Some(el) = else_opt {
 +                            self.visit_expr(el);
 +                        }
 +                    },
 +                    hir::ExprKind::Match(cond, arms, _) => {
 +                        self.inside_stmt(true).visit_expr(cond);
 +                        for arm in arms {
 +                            self.visit_expr(arm.body);
 +                        }
 +                    },
 +                    hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr),
 +                    hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
 +                    _ => self.failed |= !(self.cb)(expr),
 +                }
 +            }
 +        }
 +    }
 +
 +    !contains_try(expr) && {
 +        let mut ret_finder = RetFinder {
 +            in_stmt: false,
 +            failed: false,
 +            cb: callback,
 +        };
 +        ret_finder.visit_expr(expr);
 +        !ret_finder.failed
 +    }
 +}
 +
 +/// A type which can be visited.
 +pub trait Visitable<'tcx> {
 +    /// Calls the corresponding `visit_*` function on the visitor.
 +    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V);
 +}
 +macro_rules! visitable_ref {
 +    ($t:ident, $f:ident) => {
 +        impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
 +            fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
 +                visitor.$f(self);
 +            }
 +        }
 +    };
 +}
 +visitable_ref!(Arm, visit_arm);
 +visitable_ref!(Block, visit_block);
 +visitable_ref!(Body, visit_body);
 +visitable_ref!(Expr, visit_expr);
 +visitable_ref!(Stmt, visit_stmt);
 +
 +// impl<'tcx, I: IntoIterator> Visitable<'tcx> for I
 +// where
 +//     I::Item: Visitable<'tcx>,
 +// {
 +//     fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
 +//         for x in self {
 +//             x.visit(visitor);
 +//         }
 +//     }
 +// }
 +
 +/// Checks if the given resolved path is used in the given body.
 +pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
 +    let mut found = false;
 +    expr_visitor(cx, |e| {
 +        if found {
 +            return false;
 +        }
 +
 +        if let ExprKind::Path(p) = &e.kind {
 +            if cx.qpath_res(p, e.hir_id) == res {
 +                found = true;
 +            }
 +        }
 +        !found
 +    })
 +    .visit_expr(&cx.tcx.hir().body(body).value);
 +    found
 +}
 +
 +/// Checks if the given local is used.
 +pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
 +    let mut is_used = false;
 +    let mut visitor = expr_visitor(cx, |expr| {
 +        if !is_used {
 +            is_used = path_to_local_id(expr, id);
 +        }
 +        !is_used
 +    });
 +    visitable.visit(&mut visitor);
 +    drop(visitor);
 +    is_used
 +}
 +
 +/// Checks if the given expression is a constant.
 +pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
 +    struct V<'a, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        is_const: bool,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if !self.is_const {
 +                return;
 +            }
 +            match e.kind {
 +                ExprKind::ConstBlock(_) => return,
 +                ExprKind::Call(
 +                    &Expr {
 +                        kind: ExprKind::Path(ref p),
 +                        hir_id,
 +                        ..
 +                    },
 +                    _,
 +                ) if self
 +                    .cx
 +                    .qpath_res(p, hir_id)
 +                    .opt_def_id()
 +                    .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
 +                ExprKind::MethodCall(..)
 +                    if self
 +                        .cx
 +                        .typeck_results()
 +                        .type_dependent_def_id(e.hir_id)
 +                        .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
 +                ExprKind::Binary(_, lhs, rhs)
 +                    if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
 +                        && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
 +                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (),
 +                ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (),
 +                ExprKind::Index(base, _)
 +                    if matches!(
 +                        self.cx.typeck_results().expr_ty(base).peel_refs().kind(),
 +                        ty::Slice(_) | ty::Array(..)
 +                    ) => {},
 +                ExprKind::Path(ref p)
 +                    if matches!(
 +                        self.cx.qpath_res(p, e.hir_id),
 +                        Res::Def(
 +                            DefKind::Const
 +                                | DefKind::AssocConst
 +                                | DefKind::AnonConst
 +                                | DefKind::ConstParam
 +                                | DefKind::Ctor(..)
 +                                | DefKind::Fn
 +                                | DefKind::AssocFn,
 +                            _
 +                        ) | Res::SelfCtor(_)
 +                    ) => {},
 +
 +                ExprKind::AddrOf(..)
 +                | ExprKind::Array(_)
 +                | ExprKind::Block(..)
 +                | ExprKind::Cast(..)
 +                | ExprKind::DropTemps(_)
 +                | ExprKind::Field(..)
 +                | ExprKind::If(..)
 +                | ExprKind::Let(..)
 +                | ExprKind::Lit(_)
 +                | ExprKind::Match(..)
 +                | ExprKind::Repeat(..)
 +                | ExprKind::Struct(..)
 +                | ExprKind::Tup(_)
 +                | ExprKind::Type(..) => (),
 +
 +                _ => {
 +                    self.is_const = false;
 +                    return;
 +                },
 +            }
 +            walk_expr(self, e);
 +        }
 +    }
 +
 +    let mut v = V { cx, is_const: true };
 +    v.visit_expr(e);
 +    v.is_const
 +}
 +
 +/// Checks if the given expression performs an unsafe operation outside of an unsafe block.
 +pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
 +    struct V<'a, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        is_unsafe: bool,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.is_unsafe {
 +                return;
 +            }
 +            match e.kind {
 +                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => {
 +                    self.is_unsafe = true;
 +                },
 +                ExprKind::MethodCall(..)
 +                    if self
 +                        .cx
 +                        .typeck_results()
 +                        .type_dependent_def_id(e.hir_id)
 +                        .map_or(false, |id| self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe) =>
 +                {
 +                    self.is_unsafe = true;
 +                },
 +                ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
 +                    ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
 +                    ty::FnPtr(sig) if sig.unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
 +                    _ => walk_expr(self, e),
 +                },
 +                ExprKind::Path(ref p)
 +                    if self
 +                        .cx
 +                        .qpath_res(p, e.hir_id)
 +                        .opt_def_id()
 +                        .map_or(false, |id| self.cx.tcx.is_mutable_static(id)) =>
 +                {
 +                    self.is_unsafe = true;
 +                },
 +                _ => walk_expr(self, e),
 +            }
 +        }
 +        fn visit_block(&mut self, b: &'tcx Block<'_>) {
 +            if !matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
 +                walk_block(self, b);
 +            }
 +        }
 +        fn visit_nested_item(&mut self, id: ItemId) {
 +            if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind {
 +                self.is_unsafe = i.unsafety == Unsafety::Unsafe;
 +            }
 +        }
 +    }
 +    let mut v = V { cx, is_unsafe: false };
 +    v.visit_expr(e);
 +    v.is_unsafe
 +}
++
++/// Checks if the given expression contains an unsafe block
++pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
++    struct V<'cx, 'tcx> {
++        cx: &'cx LateContext<'tcx>,
++        found_unsafe: bool,
++    }
++    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
++        type NestedFilter = nested_filter::OnlyBodies;
++        fn nested_visit_map(&mut self) -> Self::Map {
++            self.cx.tcx.hir()
++        }
++
++        fn visit_block(&mut self, b: &'tcx Block<'_>) {
++            if self.found_unsafe {
++                return;
++            }
++            if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
++                self.found_unsafe = true;
++                return;
++            }
++            walk_block(self, b);
++        }
++    }
++    let mut v = V {
++        cx,
++        found_unsafe: false,
++    };
++    v.visit_expr(e);
++    v.found_unsafe
++}
++
++/// Runs the given function for each sub-expression producing the final value consumed by the parent
++/// of the give expression.
++///
++/// e.g. for the following expression
++/// ```rust,ignore
++/// if foo {
++///     f(0)
++/// } else {
++///     1 + 1
++/// }
++/// ```
++/// this will pass both `f(0)` and `1+1` to the given function.
++pub fn for_each_value_source<'tcx, B>(
++    e: &'tcx Expr<'tcx>,
++    f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
++) -> ControlFlow<B> {
++    match e.kind {
++        ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
++        ExprKind::Match(_, arms, _) => {
++            for arm in arms {
++                for_each_value_source(arm.body, f)?;
++            }
++            ControlFlow::Continue(())
++        },
++        ExprKind::If(_, if_expr, Some(else_expr)) => {
++            for_each_value_source(if_expr, f)?;
++            for_each_value_source(else_expr, f)
++        },
++        ExprKind::DropTemps(e) => for_each_value_source(e, f),
++        _ => f(e),
++    }
++}
index cf16a1d5d3dcf5b09538f3d55da4dd6f3e22341d,0000000000000000000000000000000000000000..307cf2f3a9047896fd3912c029e95745c0b29ff2
mode 100644,000000..100644
--- /dev/null
@@@ -1,682 -1,0 +1,696 @@@
 +# Adding a new lint
 +
 +You are probably here because you want to add a new lint to Clippy. If this is
 +the first time you're contributing to Clippy, this document guides you through
 +creating an example lint from scratch.
 +
 +To get started, we will create a lint that detects functions called `foo`,
 +because that's clearly a non-descriptive name.
 +
 +- [Adding a new lint](#adding-a-new-lint)
 +  - [Setup](#setup)
 +  - [Getting Started](#getting-started)
 +  - [Testing](#testing)
 +    - [Cargo lints](#cargo-lints)
 +  - [Rustfix tests](#rustfix-tests)
 +  - [Edition 2018 tests](#edition-2018-tests)
 +  - [Testing manually](#testing-manually)
 +  - [Lint declaration](#lint-declaration)
 +  - [Lint registration](#lint-registration)
 +  - [Lint passes](#lint-passes)
 +  - [Emitting a lint](#emitting-a-lint)
 +  - [Adding the lint logic](#adding-the-lint-logic)
 +  - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv)
 +  - [Author lint](#author-lint)
++  - [Print HIR lint](#print-hir-lint)
 +  - [Documentation](#documentation)
 +  - [Running rustfmt](#running-rustfmt)
 +  - [Debugging](#debugging)
 +  - [PR Checklist](#pr-checklist)
 +  - [Adding configuration to a lint](#adding-configuration-to-a-lint)
 +  - [Cheatsheet](#cheatsheet)
 +
 +## Setup
 +
 +See the [Basics](basics.md#get-the-code) documentation.
 +
 +## Getting Started
 +
 +There is a bit of boilerplate code that needs to be set up when creating a new
 +lint. Fortunately, you can use the clippy dev tools to handle this for you. We
 +are naming our new lint `foo_functions` (lints are generally written in snake
 +case), and we don't need type information so it will have an early pass type
 +(more on this later on). If you're not sure if the name you chose fits the lint,
 +take a look at our [lint naming guidelines][lint_naming]. To get started on this
 +lint you can run `cargo dev new_lint --name=foo_functions --pass=early
 +--category=pedantic` (category will default to nursery if not provided). This
 +command will create two files: `tests/ui/foo_functions.rs` and
 +`clippy_lints/src/foo_functions.rs`, as well as
 +[registering the lint](#lint-registration). For cargo lints, two project
 +hierarchies (fail/pass) will be created by default under `tests/ui-cargo`.
 +
 +Next, we'll open up these files and add our lint!
 +
 +## Testing
 +
 +Let's write some tests first that we can execute while we iterate on our lint.
 +
 +Clippy uses UI tests for testing. UI tests check that the output of Clippy is
 +exactly as expected. Each test is just a plain Rust file that contains the code
 +we want to check. The output of Clippy is compared against a `.stderr` file.
 +Note that you don't have to create this file yourself, we'll get to
 +generating the `.stderr` files further down.
 +
 +We start by opening the test file created at `tests/ui/foo_functions.rs`.
 +
 +Update the file with some examples to get started:
 +
 +```rust
 +#![warn(clippy::foo_functions)]
 +
 +// Impl methods
 +struct A;
 +impl A {
 +    pub fn fo(&self) {}
 +    pub fn foo(&self) {}
 +    pub fn food(&self) {}
 +}
 +
 +// Default trait methods
 +trait B {
 +    fn fo(&self) {}
 +    fn foo(&self) {}
 +    fn food(&self) {}
 +}
 +
 +// Plain functions
 +fn fo() {}
 +fn foo() {}
 +fn food() {}
 +
 +fn main() {
 +    // We also don't want to lint method calls
 +    foo();
 +    let a = A;
 +    a.foo();
 +}
 +```
 +
 +Now we can run the test with `TESTNAME=foo_functions cargo uitest`,
 +currently this test is meaningless though.
 +
 +While we are working on implementing our lint, we can keep running the UI
 +test. That allows us to check if the output is turning into what we want.
 +
 +Once we are satisfied with the output, we need to run
 +`cargo dev bless` to update the `.stderr` file for our lint.
 +Please note that, we should run `TESTNAME=foo_functions cargo uitest`
 +every time before running `cargo dev bless`.
 +Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit
 +our lint, we need to commit the generated `.stderr` files, too. In general, you
 +should only commit files changed by `cargo dev bless` for the
 +specific lint you are creating/editing. Note that if the generated files are
 +empty, they should be removed.
 +
 +Note that you can run multiple test files by specifying a comma separated list:
 +`TESTNAME=foo_functions,test2,test3`.
 +
 +### Cargo lints
 +
 +For cargo lints, the process of testing differs in that we are interested in
 +the `Cargo.toml` manifest file. We also need a minimal crate associated
 +with that manifest.
 +
 +If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint`
 +we will find by default two new crates, each with its manifest file:
 +
 +* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error.
 +* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger the lint.
 +
 +If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it.
 +
 +The process of generating the `.stderr` file is the same, and prepending the `TESTNAME`
 +variable to `cargo uitest` works too.
 +
 +## Rustfix tests
 +
 +If the lint you are working on is making use of structured suggestions, the
 +test file should include a `// run-rustfix` comment at the top. This will
 +additionally run [rustfix] for that test. Rustfix will apply the suggestions
 +from the lint to the code of the test file and compare that to the contents of
 +a `.fixed` file.
 +
 +Use `cargo dev bless` to automatically generate the
 +`.fixed` file after running the tests.
 +
 +[rustfix]: https://github.com/rust-lang/rustfix
 +
 +## Edition 2018 tests
 +
 +Some features require the 2018 edition to work (e.g. `async_await`), but
 +compile-test tests run on the 2015 edition by default. To change this behavior
 +add `// edition:2018` at the top of the test file (note that it's space-sensitive).
 +
 +## Testing manually
 +
 +Manually testing against an example file can be useful if you have added some
 +`println!`s and the test suite output becomes unreadable. To try Clippy with
 +your local modifications, run
 +
 +```
 +cargo dev lint input.rs
 +```
 +
 +from the working copy root. With tests in place, let's have a look at
 +implementing our lint now.
 +
 +## Lint declaration
 +
 +Let's start by opening the new file created in the `clippy_lints` crate
 +at `clippy_lints/src/foo_functions.rs`. That's the crate where all the
 +lint code is. This file has already imported some initial things we will need:
 +
 +```rust
 +use rustc_lint::{EarlyLintPass, EarlyContext};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_ast::ast::*;
 +```
 +
 +The next step is to update the lint declaration. Lints are declared using the
 +[`declare_clippy_lint!`][declare_clippy_lint] macro, and we just need to update
 +the auto-generated lint declaration to have a real description, something like this:
 +
 +```rust
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // example code
 +    /// ```
 +    #[clippy::version = "1.29.0"]
 +    pub FOO_FUNCTIONS,
 +    pedantic,
 +    "function named `foo`, which is not a descriptive name"
 +}
 +```
 +
 +* The section of lines prefixed with `///` constitutes the lint documentation
 +  section. This is the default documentation style and will be displayed
 +  [like this][example_lint_page]. To render and open this documentation locally
 +  in a browser, run `cargo dev serve`.
 +* The `#[clippy::version]` attribute will be rendered as part of the lint documentation.
 +  The value should be set to the current Rust version that the lint is developed in,
 +  it can be retrieved by running `rustc -vV` in the rust-clippy directory. The version
 +  is listed under *release*. (Use the version without the `-nightly`) suffix.
 +* `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the
 +  [lint naming guidelines][lint_naming] here when naming your lint.
 +  In short, the name should state the thing that is being checked for and
 +  read well when used with `allow`/`warn`/`deny`.
 +* `pedantic` sets the lint level to `Allow`.
 +  The exact mapping can be found [here][category_level_mapping]
 +* The last part should be a text that explains what exactly is wrong with the
 +  code
 +
 +The rest of this file contains an empty implementation for our lint pass,
 +which in this case is `EarlyLintPass` and should look like this:
 +
 +```rust
 +// clippy_lints/src/foo_functions.rs
 +
 +// .. imports and lint declaration ..
 +
 +declare_lint_pass!(FooFunctions => [FOO_FUNCTIONS]);
 +
 +impl EarlyLintPass for FooFunctions {}
 +```
 +
 +[declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60
 +[example_lint_page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
 +[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
 +[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110
 +
 +## Lint registration
 +
 +When using `cargo dev new_lint`, the lint is automatically registered and
 +nothing more has to be done.
 +
 +When declaring a new lint by hand and `cargo dev update_lints` is used, the lint
 +pass may have to be registered manually in the `register_plugins` function in
 +`clippy_lints/src/lib.rs`:
 +
 +```rust
 +store.register_early_pass(|| Box::new(foo_functions::FooFunctions));
 +```
 +
 +As one may expect, there is a corresponding `register_late_pass` method
 +available as well. Without a call to one of `register_early_pass` or
 +`register_late_pass`, the lint pass in question will not be run.
 +
 +One reason that `cargo dev update_lints` does not automate this step is that
 +multiple lints can use the same lint pass, so registering the lint pass may
 +already be done when adding a new lint. Another reason that this step is not
 +automated is that the order that the passes are registered determines the order
 +the passes actually run, which in turn affects the order that any emitted lints
 +are output in.
 +
 +## Lint passes
 +
 +Writing a lint that only checks for the name of a function means that we only
 +have to deal with the AST and don't have to deal with the type system at all.
 +This is good, because it makes writing this particular lint less complicated.
 +
 +We have to make this decision with every new Clippy lint. It boils down to using
 +either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass].
 +
 +In short, the `LateLintPass` has access to type information while the
 +`EarlyLintPass` doesn't. If you don't need access to type information, use the
 +`EarlyLintPass`. The `EarlyLintPass` is also faster. However linting speed
 +hasn't really been a concern with Clippy so far.
 +
 +Since we don't need type information for checking the function name, we used
 +`--pass=early` when running the new lint automation and all the imports were
 +added accordingly.
 +
 +[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
 +[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
 +
 +## Emitting a lint
 +
 +With UI tests and the lint declaration in place, we can start working on the
 +implementation of the lint logic.
 +
 +Let's start by implementing the `EarlyLintPass` for our `FooFunctions`:
 +
 +```rust
 +impl EarlyLintPass for FooFunctions {
 +    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
 +        // TODO: Emit lint here
 +    }
 +}
 +```
 +
 +We implement the [`check_fn`][check_fn] method from the
 +[`EarlyLintPass`][early_lint_pass] trait. This gives us access to various
 +information about the function that is currently being checked. More on that in
 +the next section. Let's worry about the details later and emit our lint for
 +*every* function definition first.
 +
 +Depending on how complex we want our lint message to be, we can choose from a
 +variety of lint emission functions. They can all be found in
 +[`clippy_utils/src/diagnostics.rs`][diagnostics].
 +
 +`span_lint_and_help` seems most appropriate in this case. It allows us to
 +provide an extra help message and we can't really suggest a better name
 +automatically. This is how it looks:
 +
 +```rust
 +impl EarlyLintPass for FooFunctions {
 +    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
 +        span_lint_and_help(
 +            cx,
 +            FOO_FUNCTIONS,
 +            span,
 +            "function named `foo`",
 +            None,
 +            "consider using a more meaningful name"
 +        );
 +    }
 +}
 +```
 +
 +Running our UI test should now produce output that contains the lint message.
 +
 +According to [the rustc-dev-guide], the text should be matter of fact and avoid
 +capitalization and periods, unless multiple sentences are needed.
 +When code or an identifier must appear in a message or label, it should be
 +surrounded with single grave accents \`.
 +
 +[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
 +[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/diagnostics.rs
 +[the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html
 +
 +## Adding the lint logic
 +
 +Writing the logic for your lint will most likely be different from our example,
 +so this section is kept rather short.
 +
 +Using the [`check_fn`][check_fn] method gives us access to [`FnKind`][fn_kind]
 +that has the [`FnKind::Fn`] variant. It provides access to the name of the
 +function/method via an [`Ident`][ident].
 +
 +With that we can expand our `check_fn` method to:
 +
 +```rust
 +impl EarlyLintPass for FooFunctions {
 +    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
 +        if is_foo_fn(fn_kind) {
 +            span_lint_and_help(
 +                cx,
 +                FOO_FUNCTIONS,
 +                span,
 +                "function named `foo`",
 +                None,
 +                "consider using a more meaningful name"
 +            );
 +        }
 +    }
 +}
 +```
 +
 +We separate the lint conditional from the lint emissions because it makes the
 +code a bit easier to read. In some cases this separation would also allow to
 +write some unit tests (as opposed to only UI tests) for the separate function.
 +
 +In our example, `is_foo_fn` looks like:
 +
 +```rust
 +// use statements, impl EarlyLintPass, check_fn, ..
 +
 +fn is_foo_fn(fn_kind: FnKind<'_>) -> bool {
 +    match fn_kind {
 +        FnKind::Fn(_, ident, ..) => {
 +            // check if `fn` name is `foo`
 +            ident.name.as_str() == "foo"
 +        }
 +        // ignore closures
 +        FnKind::Closure(..) => false
 +    }
 +}
 +```
 +
 +Now we should also run the full test suite with `cargo test`. At this point
 +running `cargo test` should produce the expected output. Remember to run
 +`cargo dev bless` to update the `.stderr` file.
 +
 +`cargo test` (as opposed to `cargo uitest`) will also ensure that our lint
 +implementation is not violating any Clippy lints itself.
 +
 +That should be it for the lint implementation. Running `cargo test` should now
 +pass.
 +
 +[fn_kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html
 +[`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn
 +[ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html
 +
 +## Specifying the lint's minimum supported Rust version (MSRV)
 +
 +Sometimes a lint makes suggestions that require a certain version of Rust. For example, the `manual_strip` lint suggests
 +using `str::strip_prefix` and `str::strip_suffix` which is only available after Rust 1.45. In such cases, you need to
 +ensure that the MSRV configured for the project is >= the MSRV of the required Rust feature. If multiple features are
 +required, just use the one with a lower MSRV.
 +
 +First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`](/clippy_utils/src/msrvs.rs). This can be
 +accessed later as `msrvs::STR_STRIP_PREFIX`, for example.
 +
 +```rust
 +msrv_aliases! {
 +    ..
 +    1,45,0 { STR_STRIP_PREFIX }
 +}
 +```
 +
 +In order to access the project-configured MSRV, you need to have an `msrv` field in the LintPass struct, and a
 +constructor to initialize the field. The `msrv` value is passed to the constructor in `clippy_lints/lib.rs`.
 +
 +```rust
 +pub struct ManualStrip {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl ManualStrip {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +```
 +
 +The project's MSRV can then be matched against the feature MSRV in the LintPass
 +using the `meets_msrv` utility function.
 +
 +``` rust
 +if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) {
 +    return;
 +}
 +```
 +
 +The project's MSRV can also be specified as an inner attribute, which overrides
 +the value from `clippy.toml`. This can be accounted for using the
 +`extract_msrv_attr!(LintContext)` macro and passing
 +`LateContext`/`EarlyContext`.
 +
 +```rust
 +impl<'tcx> LateLintPass<'tcx> for ManualStrip {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        ...
 +    }
 +    extract_msrv_attr!(LateContext);
 +}
 +```
 +
 +Once the `msrv` is added to the lint, a relevant test case should be added to
 +`tests/ui/min_rust_version_attr.rs` which verifies that the lint isn't emitted
 +if the project's MSRV is lower.
 +
 +As a last step, the lint should be added to the lint documentation. This is done
 +in `clippy_lints/src/utils/conf.rs`:
 +
 +```rust
 +define_Conf! {
 +    /// Lint: LIST, OF, LINTS, <THE_NEWLY_ADDED_LINT>. The minimum rust version that the project supports
 +    (msrv: Option<String> = None),
 +    ...
 +}
 +```
 +
 +## Author lint
 +
 +If you have trouble implementing your lint, there is also the internal `author`
 +lint to generate Clippy code that detects the offending pattern. It does not
 +work for all of the Rust syntax, but can give a good starting point.
 +
 +The quickest way to use it, is the
 +[Rust playground: play.rust-lang.org][author_example].
 +Put the code you want to lint into the editor and add the `#[clippy::author]`
 +attribute above the item. Then run Clippy via `Tools -> Clippy` and you should
 +see the generated code in the output below.
 +
 +[Here][author_example] is an example on the playground.
 +
 +If the command was executed successfully, you can copy the code over to where
 +you are implementing your lint.
 +
 +[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
 +
++## Print HIR lint
++
++To implement a lint, it's helpful to first understand the internal representation
++that rustc uses. Clippy has the `#[clippy::dump]` attribute that prints the
++[_High-Level Intermediate Representation (HIR)_] of the item, statement, or 
++expression that the attribute is attached to. To attach the attribute to expressions
++you often need to enable `#![feature(stmt_expr_attributes)]`.
++
++[Here][print_hir_example] you can find an example, just select _Tools_ and run _Clippy_.
++
++[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html
++[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb
++
 +## Documentation
 +
 +The final thing before submitting our PR is to add some documentation to our
 +lint declaration.
 +
 +Please document your lint with a doc comment akin to the following:
 +
 +```rust
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for ... (describe what the lint matches).
 +    ///
 +    /// ### Why is this bad?
 +    /// Supply the reason for linting the code.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// Insert a short example of code that triggers the lint
 +    ///
 +    /// // Good
 +    /// Insert a short example of improved code that doesn't trigger the lint
 +    /// ```
 +    #[clippy::version = "1.29.0"]
 +    pub FOO_FUNCTIONS,
 +    pedantic,
 +    "function named `foo`, which is not a descriptive name"
 +}
 +```
 +
 +Once your lint is merged, this documentation will show up in the [lint
 +list][lint_list].
 +
 +[lint_list]: https://rust-lang.github.io/rust-clippy/master/index.html
 +
 +## Running rustfmt
 +
 +[Rustfmt] is a tool for formatting Rust code according to style guidelines.
 +Your code has to be formatted by `rustfmt` before a PR can be merged.
 +Clippy uses nightly `rustfmt` in the CI.
 +
 +It can be installed via `rustup`:
 +
 +```bash
 +rustup component add rustfmt --toolchain=nightly
 +```
 +
 +Use `cargo dev fmt` to format the whole codebase. Make sure that `rustfmt` is
 +installed for the nightly toolchain.
 +
 +[Rustfmt]: https://github.com/rust-lang/rustfmt
 +
 +## Debugging
 +
 +If you want to debug parts of your lint implementation, you can use the [`dbg!`]
 +macro anywhere in your code. Running the tests should then include the debug
 +output in the `stdout` part.
 +
 +[`dbg!`]: https://doc.rust-lang.org/std/macro.dbg.html
 +
 +## PR Checklist
 +
 +Before submitting your PR make sure you followed all of the basic requirements:
 +
 +<!-- Sync this with `.github/PULL_REQUEST_TEMPLATE` -->
 +
 +- \[ ] Followed [lint naming conventions][lint_naming]
 +- \[ ] Added passing UI tests (including committed `.stderr` file)
 +- \[ ] `cargo test` passes locally
 +- \[ ] Executed `cargo dev update_lints`
 +- \[ ] Added lint documentation
 +- \[ ] Run `cargo dev fmt`
 +
 +## Adding configuration to a lint
 +
 +Clippy supports the configuration of lints values using a `clippy.toml` file in the workspace
 +directory. Adding a configuration to a lint can be useful for thresholds or to constrain some
 +behavior that can be seen as a false positive for some users. Adding a configuration is done
 +in the following steps:
 +
 +1. Adding a new configuration entry to [clippy_lints::utils::conf](/clippy_lints/src/utils/conf.rs)
 +    like this:
 +    ```rust
 +    /// Lint: LINT_NAME.
 +    ///
 +    /// <The configuration field doc comment>
 +    (configuration_ident: Type = DefaultValue),
 +    ```
 +    The doc comment is automatically added to the documentation of the listed lints. The default
 +    value will be formatted using the `Debug` implementation of the type.
 +2. Adding the configuration value to the lint impl struct:
 +    1. This first requires the definition of a lint impl struct. Lint impl structs are usually
 +        generated with the `declare_lint_pass!` macro. This struct needs to be defined manually
 +        to add some kind of metadata to it:
 +        ```rust
 +        // Generated struct definition
 +        declare_lint_pass!(StructName => [
 +            LINT_NAME
 +        ]);
 +
 +        // New manual definition struct
 +        #[derive(Copy, Clone)]
 +        pub struct StructName {}
 +
 +        impl_lint_pass!(StructName => [
 +            LINT_NAME
 +        ]);
 +        ```
 +
 +    2. Next add the configuration value and a corresponding creation method like this:
 +        ```rust
 +        #[derive(Copy, Clone)]
 +        pub struct StructName {
 +            configuration_ident: Type,
 +        }
 +
 +        // ...
 +
 +        impl StructName {
 +            pub fn new(configuration_ident: Type) -> Self {
 +                Self {
 +                    configuration_ident,
 +                }
 +            }
 +        }
 +        ```
 +3. Passing the configuration value to the lint impl struct:
 +
 +    First find the struct construction in the [clippy_lints lib file](/clippy_lints/src/lib.rs).
 +    The configuration value is now cloned or copied into a local value that is then passed to the
 +    impl struct like this:
 +    ```rust
 +    // Default generated registration:
 +    store.register_*_pass(|| box module::StructName);
 +
 +    // New registration with configuration value
 +    let configuration_ident = conf.configuration_ident.clone();
 +    store.register_*_pass(move || box module::StructName::new(configuration_ident));
 +    ```
 +
 +    Congratulations the work is almost done. The configuration value can now be accessed
 +    in the linting code via `self.configuration_ident`.
 +
 +4. Adding tests:
 +    1. The default configured value can be tested like any normal lint in [`tests/ui`](/tests/ui).
 +    2. The configuration itself will be tested separately in [`tests/ui-toml`](/tests/ui-toml).
 +        Simply add a new subfolder with a fitting name. This folder contains a `clippy.toml` file
 +        with the configuration value and a rust file that should be linted by Clippy. The test can
 +        otherwise be written as usual.
 +
 +## Cheatsheet
 +
 +Here are some pointers to things you are likely going to need for every lint:
 +
 +* [Clippy utils][utils] - Various helper functions. Maybe the function you need
 +  is already in here ([`is_type_diagnostic_item`], [`implements_trait`], [`snippet`], etc)
 +* [Clippy diagnostics][diagnostics]
 +* [The `if_chain` macro][if_chain]
 +* [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro]
 +* [`Span`][span]
 +* [`Applicability`][applicability]
 +* [Common tools for writing lints](common_tools_writing_lints.md) helps with common operations
 +* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler concepts
 +* [The nightly rustc docs][nightly_docs] which has been linked to throughout
 +  this guide
 +
 +For `EarlyLintPass` lints:
 +
 +* [`EarlyLintPass`][early_lint_pass]
 +* [`rustc_ast::ast`][ast]
 +
 +For `LateLintPass` lints:
 +
 +* [`LateLintPass`][late_lint_pass]
 +* [`Ty::TyKind`][ty]
 +
 +While most of Clippy's lint utils are documented, most of rustc's internals lack
 +documentation currently. This is unfortunate, but in most cases you can probably
 +get away with copying things from existing similar lints. If you are stuck,
 +don't hesitate to ask on [Zulip] or in the issue/PR.
 +
 +[utils]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html
 +[`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html
 +[`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html
 +[`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html
 +[if_chain]: https://docs.rs/if_chain/*/if_chain/
 +[from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
 +[in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html
 +[span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html
 +[applicability]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html
 +[rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/
 +[nightly_docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/
 +[ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html
 +[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/index.html
 +[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy
index 9af8dcc7726f080906adcc1c8ed2b1bc2b711175,0000000000000000000000000000000000000000..816efbdaedf36d770aab843378175781f4274fb6
mode 100644,000000..100644
--- /dev/null
@@@ -1,1039 -1,0 +1,1042 @@@
-             output.push_str(&format!(
 +// Run clippy on a fixed set of crates and collect the warnings.
 +// This helps observing the impact clippy changes have on a set of real-world code (and not just our
 +// testsuite).
 +//
 +// When a new lint is introduced, we can search the results for new warnings and check for false
 +// positives.
 +
 +#![allow(clippy::collapsible_else_if)]
 +
 +use std::ffi::OsStr;
++use std::fmt::Write as _;
 +use std::process::Command;
 +use std::sync::atomic::{AtomicUsize, Ordering};
 +use std::{collections::HashMap, io::ErrorKind};
 +use std::{
 +    env,
 +    fs::write,
 +    path::{Path, PathBuf},
 +    thread,
 +    time::Duration,
 +};
 +
 +use clap::{App, Arg, ArgMatches};
 +use rayon::prelude::*;
 +use serde::{Deserialize, Serialize};
 +use serde_json::Value;
 +use walkdir::{DirEntry, WalkDir};
 +
 +#[cfg(not(windows))]
 +const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver";
 +#[cfg(not(windows))]
 +const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy";
 +
 +#[cfg(windows)]
 +const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver.exe";
 +#[cfg(windows)]
 +const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy.exe";
 +
 +const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads";
 +const LINTCHECK_SOURCES: &str = "target/lintcheck/sources";
 +
 +/// List of sources to check, loaded from a .toml file
 +#[derive(Debug, Serialize, Deserialize)]
 +struct SourceList {
 +    crates: HashMap<String, TomlCrate>,
 +}
 +
 +/// A crate source stored inside the .toml
 +/// will be translated into on one of the `CrateSource` variants
 +#[derive(Debug, Serialize, Deserialize)]
 +struct TomlCrate {
 +    name: String,
 +    versions: Option<Vec<String>>,
 +    git_url: Option<String>,
 +    git_hash: Option<String>,
 +    path: Option<String>,
 +    options: Option<Vec<String>>,
 +}
 +
 +/// Represents an archive we download from crates.io, or a git repo, or a local repo/folder
 +/// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate`
 +#[derive(Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)]
 +enum CrateSource {
 +    CratesIo {
 +        name: String,
 +        version: String,
 +        options: Option<Vec<String>>,
 +    },
 +    Git {
 +        name: String,
 +        url: String,
 +        commit: String,
 +        options: Option<Vec<String>>,
 +    },
 +    Path {
 +        name: String,
 +        path: PathBuf,
 +        options: Option<Vec<String>>,
 +    },
 +}
 +
 +/// Represents the actual source code of a crate that we ran "cargo clippy" on
 +#[derive(Debug)]
 +struct Crate {
 +    version: String,
 +    name: String,
 +    // path to the extracted sources that clippy can check
 +    path: PathBuf,
 +    options: Option<Vec<String>>,
 +}
 +
 +/// A single warning that clippy issued while checking a `Crate`
 +#[derive(Debug)]
 +struct ClippyWarning {
 +    crate_name: String,
 +    crate_version: String,
 +    file: String,
 +    line: String,
 +    column: String,
 +    linttype: String,
 +    message: String,
 +    is_ice: bool,
 +}
 +
 +#[allow(unused)]
 +impl ClippyWarning {
 +    fn to_output(&self, markdown: bool) -> String {
 +        let file = format!("{}-{}/{}", &self.crate_name, &self.crate_version, &self.file);
 +        let file_with_pos = format!("{}:{}:{}", &file, &self.line, &self.column);
 +        if markdown {
 +            let lint = format!("`{}`", self.linttype);
 +
 +            let mut output = String::from("| ");
-             ));
-             output.push_str(&format!(r#" | {:<50} | "{}" |"#, lint, self.message));
++            let _ = write!(
++                output,
 +                "[`{}`](../target/lintcheck/sources/{}#L{})",
 +                file_with_pos, file, self.line
-             vec!["--fix", "--allow-no-vcs", "--"]
++            );
++            let _ = write!(output, r#" | {:<50} | "{}" |"#, lint, self.message);
 +            output.push('\n');
 +            output
 +        } else {
 +            format!(
 +                "target/lintcheck/sources/{} {} \"{}\"\n",
 +                file_with_pos, self.linttype, self.message
 +            )
 +        }
 +    }
 +}
 +
 +fn get(path: &str) -> Result<ureq::Response, ureq::Error> {
 +    const MAX_RETRIES: u8 = 4;
 +    let mut retries = 0;
 +    loop {
 +        match ureq::get(path).call() {
 +            Ok(res) => return Ok(res),
 +            Err(e) if retries >= MAX_RETRIES => return Err(e),
 +            Err(ureq::Error::Transport(e)) => eprintln!("Error: {}", e),
 +            Err(e) => return Err(e),
 +        }
 +        eprintln!("retrying in {} seconds...", retries);
 +        thread::sleep(Duration::from_secs(retries as u64));
 +        retries += 1;
 +    }
 +}
 +
 +impl CrateSource {
 +    /// Makes the sources available on the disk for clippy to check.
 +    /// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or
 +    /// copies a local folder
 +    fn download_and_extract(&self) -> Crate {
 +        match self {
 +            CrateSource::CratesIo { name, version, options } => {
 +                let extract_dir = PathBuf::from(LINTCHECK_SOURCES);
 +                let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS);
 +
 +                // url to download the crate from crates.io
 +                let url = format!("https://crates.io/api/v1/crates/{}/{}/download", name, version);
 +                println!("Downloading and extracting {} {} from {}", name, version, url);
 +                create_dirs(&krate_download_dir, &extract_dir);
 +
 +                let krate_file_path = krate_download_dir.join(format!("{}-{}.crate.tar.gz", name, version));
 +                // don't download/extract if we already have done so
 +                if !krate_file_path.is_file() {
 +                    // create a file path to download and write the crate data into
 +                    let mut krate_dest = std::fs::File::create(&krate_file_path).unwrap();
 +                    let mut krate_req = get(&url).unwrap().into_reader();
 +                    // copy the crate into the file
 +                    std::io::copy(&mut krate_req, &mut krate_dest).unwrap();
 +
 +                    // unzip the tarball
 +                    let ungz_tar = flate2::read::GzDecoder::new(std::fs::File::open(&krate_file_path).unwrap());
 +                    // extract the tar archive
 +                    let mut archive = tar::Archive::new(ungz_tar);
 +                    archive.unpack(&extract_dir).expect("Failed to extract!");
 +                }
 +                // crate is extracted, return a new Krate object which contains the path to the extracted
 +                // sources that clippy can check
 +                Crate {
 +                    version: version.clone(),
 +                    name: name.clone(),
 +                    path: extract_dir.join(format!("{}-{}/", name, version)),
 +                    options: options.clone(),
 +                }
 +            },
 +            CrateSource::Git {
 +                name,
 +                url,
 +                commit,
 +                options,
 +            } => {
 +                let repo_path = {
 +                    let mut repo_path = PathBuf::from(LINTCHECK_SOURCES);
 +                    // add a -git suffix in case we have the same crate from crates.io and a git repo
 +                    repo_path.push(format!("{}-git", name));
 +                    repo_path
 +                };
 +                // clone the repo if we have not done so
 +                if !repo_path.is_dir() {
 +                    println!("Cloning {} and checking out {}", url, commit);
 +                    if !Command::new("git")
 +                        .arg("clone")
 +                        .arg(url)
 +                        .arg(&repo_path)
 +                        .status()
 +                        .expect("Failed to clone git repo!")
 +                        .success()
 +                    {
 +                        eprintln!("Failed to clone {} into {}", url, repo_path.display())
 +                    }
 +                }
 +                // check out the commit/branch/whatever
 +                if !Command::new("git")
 +                    .arg("checkout")
 +                    .arg(commit)
 +                    .current_dir(&repo_path)
 +                    .status()
 +                    .expect("Failed to check out commit")
 +                    .success()
 +                {
 +                    eprintln!("Failed to checkout {} of repo at {}", commit, repo_path.display())
 +                }
 +
 +                Crate {
 +                    version: commit.clone(),
 +                    name: name.clone(),
 +                    path: repo_path,
 +                    options: options.clone(),
 +                }
 +            },
 +            CrateSource::Path { name, path, options } => {
 +                // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file.
 +                // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory
 +                // as a result of this filter.
 +                let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name);
 +                if dest_crate_root.exists() {
 +                    println!("Deleting existing directory at {:?}", dest_crate_root);
 +                    std::fs::remove_dir_all(&dest_crate_root).unwrap();
 +                }
 +
 +                println!("Copying {:?} to {:?}", path, dest_crate_root);
 +
 +                fn is_cache_dir(entry: &DirEntry) -> bool {
 +                    std::fs::read(entry.path().join("CACHEDIR.TAG"))
 +                        .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55"))
 +                        .unwrap_or(false)
 +                }
 +
 +                for entry in WalkDir::new(path).into_iter().filter_entry(|e| !is_cache_dir(e)) {
 +                    let entry = entry.unwrap();
 +                    let entry_path = entry.path();
 +                    let relative_entry_path = entry_path.strip_prefix(path).unwrap();
 +                    let dest_path = dest_crate_root.join(relative_entry_path);
 +                    let metadata = entry_path.symlink_metadata().unwrap();
 +
 +                    if metadata.is_dir() {
 +                        std::fs::create_dir(dest_path).unwrap();
 +                    } else if metadata.is_file() {
 +                        std::fs::copy(entry_path, dest_path).unwrap();
 +                    }
 +                }
 +
 +                Crate {
 +                    version: String::from("local"),
 +                    name: name.clone(),
 +                    path: dest_crate_root,
 +                    options: options.clone(),
 +                }
 +            },
 +        }
 +    }
 +}
 +
 +impl Crate {
 +    /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy
 +    /// issued
 +    fn run_clippy_lints(
 +        &self,
 +        cargo_clippy_path: &Path,
 +        target_dir_index: &AtomicUsize,
 +        thread_limit: usize,
 +        total_crates_to_lint: usize,
 +        fix: bool,
 +        lint_filter: &Vec<String>,
 +    ) -> Vec<ClippyWarning> {
 +        // advance the atomic index by one
 +        let index = target_dir_index.fetch_add(1, Ordering::SeqCst);
 +        // "loop" the index within 0..thread_limit
 +        let thread_index = index % thread_limit;
 +        let perc = (index * 100) / total_crates_to_lint;
 +
 +        if thread_limit == 1 {
 +            println!(
 +                "{}/{} {}% Linting {} {}",
 +                index, total_crates_to_lint, perc, &self.name, &self.version
 +            );
 +        } else {
 +            println!(
 +                "{}/{} {}% Linting {} {} in target dir {:?}",
 +                index, total_crates_to_lint, perc, &self.name, &self.version, thread_index
 +            );
 +        }
 +
 +        let cargo_clippy_path = std::fs::canonicalize(cargo_clippy_path).unwrap();
 +
 +        let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir");
 +
 +        let mut args = if fix {
-     /// A list of lint that this lintcheck run shound focus on
++            vec!["--fix", "--"]
 +        } else {
 +            vec!["--", "--message-format=json", "--"]
 +        };
 +
 +        if let Some(options) = &self.options {
 +            for opt in options {
 +                args.push(opt);
 +            }
 +        } else {
 +            args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"])
 +        }
 +
 +        if lint_filter.is_empty() {
 +            args.push("--cap-lints=warn");
 +        } else {
 +            args.push("--cap-lints=allow");
 +            args.extend(lint_filter.iter().map(|filter| filter.as_str()))
 +        }
 +
 +        let all_output = std::process::Command::new(&cargo_clippy_path)
 +            // use the looping index to create individual target dirs
 +            .env(
 +                "CARGO_TARGET_DIR",
 +                shared_target_dir.join(format!("_{:?}", thread_index)),
 +            )
 +            // lint warnings will look like this:
 +            // src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter`
 +            .args(&args)
 +            .current_dir(&self.path)
 +            .output()
 +            .unwrap_or_else(|error| {
 +                panic!(
 +                    "Encountered error:\n{:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
 +                    error,
 +                    &cargo_clippy_path.display(),
 +                    &self.path.display()
 +                );
 +            });
 +        let stdout = String::from_utf8_lossy(&all_output.stdout);
 +        let stderr = String::from_utf8_lossy(&all_output.stderr);
 +        let status = &all_output.status;
 +
 +        if !status.success() {
 +            eprintln!(
 +                "\nWARNING: bad exit status after checking {} {} \n",
 +                self.name, self.version
 +            );
 +        }
 +
 +        if fix {
 +            if let Some(stderr) = stderr
 +                .lines()
 +                .find(|line| line.contains("failed to automatically apply fixes suggested by rustc to crate"))
 +            {
 +                let subcrate = &stderr[63..];
 +                println!(
 +                    "ERROR: failed to apply some suggetion to {} / to (sub)crate {}",
 +                    self.name, subcrate
 +                );
 +            }
 +            // fast path, we don't need the warnings anyway
 +            return Vec::new();
 +        }
 +
 +        let output_lines = stdout.lines();
 +        let warnings: Vec<ClippyWarning> = output_lines
 +            .into_iter()
 +            // get all clippy warnings and ICEs
 +            .filter(|line| filter_clippy_warnings(&line))
 +            .map(|json_msg| parse_json_message(json_msg, &self))
 +            .collect();
 +
 +        warnings
 +    }
 +}
 +
 +#[derive(Debug)]
 +struct LintcheckConfig {
 +    /// max number of jobs to spawn (default 1)
 +    max_jobs: usize,
 +    /// we read the sources to check from here
 +    sources_toml_path: PathBuf,
 +    /// we save the clippy lint results here
 +    lintcheck_results_path: PathBuf,
 +    /// whether to just run --fix and not collect all the warnings
 +    fix: bool,
-     text.push_str(&format!("{}", all_msgs.join("")));
++    /// A list of lints that this lintcheck run should focus on
 +    lint_filter: Vec<String>,
 +    /// Indicate if the output should support markdown syntax
 +    markdown: bool,
 +}
 +
 +impl LintcheckConfig {
 +    fn from_clap(clap_config: &ArgMatches) -> Self {
 +        // first, check if we got anything passed via the LINTCHECK_TOML env var,
 +        // if not, ask clap if we got any value for --crates-toml  <foo>
 +        // if not, use the default "lintcheck/lintcheck_crates.toml"
 +        let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| {
 +            clap_config
 +                .value_of("crates-toml")
 +                .clone()
 +                .unwrap_or("lintcheck/lintcheck_crates.toml")
 +                .to_string()
 +        });
 +
 +        let markdown = clap_config.is_present("markdown");
 +        let sources_toml_path = PathBuf::from(sources_toml);
 +
 +        // for the path where we save the lint results, get the filename without extension (so for
 +        // wasd.toml, use "wasd"...)
 +        let filename: PathBuf = sources_toml_path.file_stem().unwrap().into();
 +        let lintcheck_results_path = PathBuf::from(format!(
 +            "lintcheck-logs/{}_logs.{}",
 +            filename.display(),
 +            if markdown { "md" } else { "txt" }
 +        ));
 +
 +        // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and
 +        // use half of that for the physical core count
 +        // by default use a single thread
 +        let max_jobs = match clap_config.value_of("threads") {
 +            Some(threads) => {
 +                let threads: usize = threads
 +                    .parse()
 +                    .unwrap_or_else(|_| panic!("Failed to parse '{}' to a digit", threads));
 +                if threads == 0 {
 +                    // automatic choice
 +                    // Rayon seems to return thread count so half that for core count
 +                    (rayon::current_num_threads() / 2) as usize
 +                } else {
 +                    threads
 +                }
 +            },
 +            // no -j passed, use a single thread
 +            None => 1,
 +        };
 +        let fix: bool = clap_config.is_present("fix");
 +        let lint_filter: Vec<String> = clap_config
 +            .values_of("filter")
 +            .map(|iter| {
 +                iter.map(|lint_name| {
 +                    let mut filter = lint_name.replace('_', "-");
 +                    if !filter.starts_with("clippy::") {
 +                        filter.insert_str(0, "clippy::");
 +                    }
 +                    filter
 +                })
 +                .collect()
 +            })
 +            .unwrap_or_default();
 +
 +        LintcheckConfig {
 +            max_jobs,
 +            sources_toml_path,
 +            lintcheck_results_path,
 +            fix,
 +            lint_filter,
 +            markdown,
 +        }
 +    }
 +}
 +
 +/// takes a single json-formatted clippy warnings and returns true (we are interested in that line)
 +/// or false (we aren't)
 +fn filter_clippy_warnings(line: &str) -> bool {
 +    // we want to collect ICEs because clippy might have crashed.
 +    // these are summarized later
 +    if line.contains("internal compiler error: ") {
 +        return true;
 +    }
 +    // in general, we want all clippy warnings
 +    // however due to some kind of bug, sometimes there are absolute paths
 +    // to libcore files inside the message
 +    // or we end up with cargo-metadata output (https://github.com/rust-lang/rust-clippy/issues/6508)
 +
 +    // filter out these message to avoid unnecessary noise in the logs
 +    if line.contains("clippy::")
 +        && !(line.contains("could not read cargo metadata")
 +            || (line.contains(".rustup") && line.contains("toolchains")))
 +    {
 +        return true;
 +    }
 +    false
 +}
 +
 +/// Builds clippy inside the repo to make sure we have a clippy executable we can use.
 +fn build_clippy() {
 +    let status = Command::new("cargo")
 +        .arg("build")
 +        .status()
 +        .expect("Failed to build clippy!");
 +    if !status.success() {
 +        eprintln!("Error: Failed to compile Clippy!");
 +        std::process::exit(1);
 +    }
 +}
 +
 +/// Read a `toml` file and return a list of `CrateSources` that we want to check with clippy
 +fn read_crates(toml_path: &Path) -> Vec<CrateSource> {
 +    let toml_content: String =
 +        std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display()));
 +    let crate_list: SourceList =
 +        toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e));
 +    // parse the hashmap of the toml file into a list of crates
 +    let tomlcrates: Vec<TomlCrate> = crate_list
 +        .crates
 +        .into_iter()
 +        .map(|(_cratename, tomlcrate)| tomlcrate)
 +        .collect();
 +
 +    // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate =>
 +    // multiple Cratesources)
 +    let mut crate_sources = Vec::new();
 +    tomlcrates.into_iter().for_each(|tk| {
 +        if let Some(ref path) = tk.path {
 +            crate_sources.push(CrateSource::Path {
 +                name: tk.name.clone(),
 +                path: PathBuf::from(path),
 +                options: tk.options.clone(),
 +            });
 +        }
 +
 +        // if we have multiple versions, save each one
 +        if let Some(ref versions) = tk.versions {
 +            versions.iter().for_each(|ver| {
 +                crate_sources.push(CrateSource::CratesIo {
 +                    name: tk.name.clone(),
 +                    version: ver.to_string(),
 +                    options: tk.options.clone(),
 +                });
 +            })
 +        }
 +        // otherwise, we should have a git source
 +        if tk.git_url.is_some() && tk.git_hash.is_some() {
 +            crate_sources.push(CrateSource::Git {
 +                name: tk.name.clone(),
 +                url: tk.git_url.clone().unwrap(),
 +                commit: tk.git_hash.clone().unwrap(),
 +                options: tk.options.clone(),
 +            });
 +        }
 +        // if we have a version as well as a git data OR only one git data, something is funky
 +        if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some())
 +            || tk.git_hash.is_some() != tk.git_url.is_some()
 +        {
 +            eprintln!("tomlkrate: {:?}", tk);
 +            if tk.git_hash.is_some() != tk.git_url.is_some() {
 +                panic!("Error: Encountered TomlCrate with only one of git_hash and git_url!");
 +            }
 +            if tk.path.is_some() && (tk.git_hash.is_some() || tk.versions.is_some()) {
 +                panic!("Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields");
 +            }
 +            unreachable!("Failed to translate TomlCrate into CrateSource!");
 +        }
 +    });
 +    // sort the crates
 +    crate_sources.sort();
 +
 +    crate_sources
 +}
 +
 +/// Parse the json output of clippy and return a `ClippyWarning`
 +fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning {
 +    let jmsg: Value = serde_json::from_str(&json_message).unwrap_or_else(|e| panic!("Failed to parse json:\n{:?}", e));
 +
 +    let file: String = jmsg["message"]["spans"][0]["file_name"]
 +        .to_string()
 +        .trim_matches('"')
 +        .into();
 +
 +    let file = if file.contains(".cargo") {
 +        // if we deal with macros, a filename may show the origin of a macro which can be inside a dep from
 +        // the registry.
 +        // don't show the full path in that case.
 +
 +        // /home/matthias/.cargo/registry/src/github.com-1ecc6299db9ec823/syn-1.0.63/src/custom_keyword.rs
 +        let path = PathBuf::from(file);
 +        let mut piter = path.iter();
 +        // consume all elements until we find ".cargo", so that "/home/matthias" is skipped
 +        let _: Option<&OsStr> = piter.find(|x| x == &std::ffi::OsString::from(".cargo"));
 +        // collect the remaining segments
 +        let file = piter.collect::<PathBuf>();
 +        format!("{}", file.display())
 +    } else {
 +        file
 +    };
 +
 +    ClippyWarning {
 +        crate_name: krate.name.to_string(),
 +        crate_version: krate.version.to_string(),
 +        file,
 +        line: jmsg["message"]["spans"][0]["line_start"]
 +            .to_string()
 +            .trim_matches('"')
 +            .into(),
 +        column: jmsg["message"]["spans"][0]["text"][0]["highlight_start"]
 +            .to_string()
 +            .trim_matches('"')
 +            .into(),
 +        linttype: jmsg["message"]["code"]["code"].to_string().trim_matches('"').into(),
 +        message: jmsg["message"]["message"].to_string().trim_matches('"').into(),
 +        is_ice: json_message.contains("internal compiler error: "),
 +    }
 +}
 +
 +/// Generate a short list of occurring lints-types and their count
 +fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) {
 +    // count lint type occurrences
 +    let mut counter: HashMap<&String, usize> = HashMap::new();
 +    clippy_warnings
 +        .iter()
 +        .for_each(|wrn| *counter.entry(&wrn.linttype).or_insert(0) += 1);
 +
 +    // collect into a tupled list for sorting
 +    let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect();
 +    // sort by "000{count} {clippy::lintname}"
 +    // to not have a lint with 200 and 2 warnings take the same spot
 +    stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint));
 +
 +    let mut header = String::from("| lint                                               | count |\n");
 +    header.push_str("| -------------------------------------------------- | ----- |\n");
 +    let stats_string = stats
 +        .iter()
 +        .map(|(lint, count)| format!("| {:<50} |  {:>4} |\n", lint, count))
 +        .fold(header, |mut table, line| {
 +            table.push_str(&line);
 +            table
 +        });
 +
 +    (stats_string, counter)
 +}
 +
 +/// check if the latest modification of the logfile is older than the modification date of the
 +/// clippy binary, if this is true, we should clean the lintchec shared target directory and recheck
 +fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool {
 +    if !lintcheck_logs_path.exists() {
 +        return true;
 +    }
 +
 +    let clippy_modified: std::time::SystemTime = {
 +        let mut times = [CLIPPY_DRIVER_PATH, CARGO_CLIPPY_PATH].iter().map(|p| {
 +            std::fs::metadata(p)
 +                .expect("failed to get metadata of file")
 +                .modified()
 +                .expect("failed to get modification date")
 +        });
 +        // the oldest modification of either of the binaries
 +        std::cmp::max(times.next().unwrap(), times.next().unwrap())
 +    };
 +
 +    let logs_modified: std::time::SystemTime = std::fs::metadata(lintcheck_logs_path)
 +        .expect("failed to get metadata of file")
 +        .modified()
 +        .expect("failed to get modification date");
 +
 +    // time is represented in seconds since X
 +    // logs_modified 2 and clippy_modified 5 means clippy binary is older and we need to recheck
 +    logs_modified < clippy_modified
 +}
 +
 +/// lintchecks `main()` function
 +///
 +/// # Panics
 +///
 +/// This function panics if the clippy binaries don't exist
 +/// or if lintcheck is executed from the wrong directory (aka none-repo-root)
 +pub fn main() {
 +    // assert that we launch lintcheck from the repo root (via cargo lintcheck)
 +    if std::fs::metadata("lintcheck/Cargo.toml").is_err() {
 +        eprintln!("lintcheck needs to be run from clippys repo root!\nUse `cargo lintcheck` alternatively.");
 +        std::process::exit(3);
 +    }
 +
 +    let clap_config = &get_clap_config();
 +
 +    let config = LintcheckConfig::from_clap(clap_config);
 +
 +    println!("Compiling clippy...");
 +    build_clippy();
 +    println!("Done compiling");
 +
 +    // if the clippy bin is newer than our logs, throw away target dirs to force clippy to
 +    // refresh the logs
 +    if lintcheck_needs_rerun(&config.lintcheck_results_path) {
 +        let shared_target_dir = "target/lintcheck/shared_target_dir";
 +        // if we get an Err here, the shared target dir probably does simply not exist
 +        if let Ok(metadata) = std::fs::metadata(&shared_target_dir) {
 +            if metadata.is_dir() {
 +                println!("Clippy is newer than lint check logs, clearing lintcheck shared target dir...");
 +                std::fs::remove_dir_all(&shared_target_dir)
 +                    .expect("failed to remove target/lintcheck/shared_target_dir");
 +            }
 +        }
 +    }
 +
 +    let cargo_clippy_path: PathBuf = PathBuf::from(CARGO_CLIPPY_PATH)
 +        .canonicalize()
 +        .expect("failed to canonicalize path to clippy binary");
 +
 +    // assert that clippy is found
 +    assert!(
 +        cargo_clippy_path.is_file(),
 +        "target/debug/cargo-clippy binary not found! {}",
 +        cargo_clippy_path.display()
 +    );
 +
 +    let clippy_ver = std::process::Command::new(CARGO_CLIPPY_PATH)
 +        .arg("--version")
 +        .output()
 +        .map(|o| String::from_utf8_lossy(&o.stdout).into_owned())
 +        .expect("could not get clippy version!");
 +
 +    // download and extract the crates, then run clippy on them and collect clippys warnings
 +    // flatten into one big list of warnings
 +
 +    let crates = read_crates(&config.sources_toml_path);
 +    let old_stats = read_stats_from_file(&config.lintcheck_results_path);
 +
 +    let counter = AtomicUsize::new(1);
 +    let lint_filter: Vec<String> = config
 +        .lint_filter
 +        .iter()
 +        .map(|filter| {
 +            let mut filter = filter.clone();
 +            filter.insert_str(0, "--force-warn=");
 +            filter
 +        })
 +        .collect();
 +
 +    let clippy_warnings: Vec<ClippyWarning> = if let Some(only_one_crate) = clap_config.value_of("only") {
 +        // if we don't have the specified crate in the .toml, throw an error
 +        if !crates.iter().any(|krate| {
 +            let name = match krate {
 +                CrateSource::CratesIo { name, .. } | CrateSource::Git { name, .. } | CrateSource::Path { name, .. } => {
 +                    name
 +                },
 +            };
 +            name == only_one_crate
 +        }) {
 +            eprintln!(
 +                "ERROR: could not find crate '{}' in lintcheck/lintcheck_crates.toml",
 +                only_one_crate
 +            );
 +            std::process::exit(1);
 +        }
 +
 +        // only check a single crate that was passed via cmdline
 +        crates
 +            .into_iter()
 +            .map(|krate| krate.download_and_extract())
 +            .filter(|krate| krate.name == only_one_crate)
 +            .flat_map(|krate| {
 +                krate.run_clippy_lints(&cargo_clippy_path, &AtomicUsize::new(0), 1, 1, config.fix, &lint_filter)
 +            })
 +            .collect()
 +    } else {
 +        if config.max_jobs > 1 {
 +            // run parallel with rayon
 +
 +            // Ask rayon for thread count. Assume that half of that is the number of physical cores
 +            // Use one target dir for each core so that we can run N clippys in parallel.
 +            // We need to use different target dirs because cargo would lock them for a single build otherwise,
 +            // killing the parallelism. However this also means that deps will only be reused half/a
 +            // quarter of the time which might result in a longer wall clock runtime
 +
 +            // This helps when we check many small crates with dep-trees that don't have a lot of branches in
 +            // order to achieve some kind of parallelism
 +
 +            // by default, use a single thread
 +            let num_cpus = config.max_jobs;
 +            let num_crates = crates.len();
 +
 +            // check all crates (default)
 +            crates
 +                .into_par_iter()
 +                .map(|krate| krate.download_and_extract())
 +                .flat_map(|krate| {
 +                    krate.run_clippy_lints(
 +                        &cargo_clippy_path,
 +                        &counter,
 +                        num_cpus,
 +                        num_crates,
 +                        config.fix,
 +                        &lint_filter,
 +                    )
 +                })
 +                .collect()
 +        } else {
 +            // run sequential
 +            let num_crates = crates.len();
 +            crates
 +                .into_iter()
 +                .map(|krate| krate.download_and_extract())
 +                .flat_map(|krate| {
 +                    krate.run_clippy_lints(&cargo_clippy_path, &counter, 1, num_crates, config.fix, &lint_filter)
 +                })
 +                .collect()
 +        }
 +    };
 +
 +    // if we are in --fix mode, don't change the log files, terminate here
 +    if config.fix {
 +        return;
 +    }
 +
 +    // generate some stats
 +    let (stats_formatted, new_stats) = gather_stats(&clippy_warnings);
 +
 +    // grab crashes/ICEs, save the crate name and the ice message
 +    let ices: Vec<(&String, &String)> = clippy_warnings
 +        .iter()
 +        .filter(|warning| warning.is_ice)
 +        .map(|w| (&w.crate_name, &w.message))
 +        .collect();
 +
 +    let mut all_msgs: Vec<String> = clippy_warnings
 +        .iter()
 +        .map(|warn| warn.to_output(config.markdown))
 +        .collect();
 +    all_msgs.sort();
 +    all_msgs.push("\n\n### Stats:\n\n".into());
 +    all_msgs.push(stats_formatted);
 +
 +    // save the text into lintcheck-logs/logs.txt
 +    let mut text = clippy_ver; // clippy version number on top
 +    text.push_str("\n### Reports\n\n");
 +    if config.markdown {
 +        text.push_str("| file | lint | message |\n");
 +        text.push_str("| --- | --- | --- |\n");
 +    }
-     ices.iter()
-         .for_each(|(cratename, msg)| text.push_str(&format!("{}: '{}'", cratename, msg)));
++    write!(text, "{}", all_msgs.join(""));
 +    text.push_str("\n\n### ICEs:\n");
++    for (cratename, msg) in ices.iter() {
++        let _ = write!(text, "{}: '{}'", cratename, msg);
++    }
 +
 +    println!("Writing logs to {}", config.lintcheck_results_path.display());
 +    std::fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap();
 +    write(&config.lintcheck_results_path, text).unwrap();
 +
 +    print_stats(old_stats, new_stats, &config.lint_filter);
 +}
 +
 +/// read the previous stats from the lintcheck-log file
 +fn read_stats_from_file(file_path: &Path) -> HashMap<String, usize> {
 +    let file_content: String = match std::fs::read_to_string(file_path).ok() {
 +        Some(content) => content,
 +        None => {
 +            return HashMap::new();
 +        },
 +    };
 +
 +    let lines: Vec<String> = file_content.lines().map(ToString::to_string).collect();
 +
 +    lines
 +        .iter()
 +        .skip_while(|line| line.as_str() != "### Stats:")
 +        // Skipping the table header and the `Stats:` label
 +        .skip(4)
 +        .take_while(|line| line.starts_with("| "))
 +        .filter_map(|line| {
 +            let mut spl = line.split('|');
 +            // Skip the first `|` symbol
 +            spl.next();
 +            if let (Some(lint), Some(count)) = (spl.next(), spl.next()) {
 +                Some((lint.trim().to_string(), count.trim().parse::<usize>().unwrap()))
 +            } else {
 +                None
 +            }
 +        })
 +        .collect::<HashMap<String, usize>>()
 +}
 +
 +/// print how lint counts changed between runs
 +fn print_stats(old_stats: HashMap<String, usize>, new_stats: HashMap<&String, usize>, lint_filter: &Vec<String>) {
 +    let same_in_both_hashmaps = old_stats
 +        .iter()
 +        .filter(|(old_key, old_val)| new_stats.get::<&String>(&old_key) == Some(old_val))
 +        .map(|(k, v)| (k.to_string(), *v))
 +        .collect::<Vec<(String, usize)>>();
 +
 +    let mut old_stats_deduped = old_stats;
 +    let mut new_stats_deduped = new_stats;
 +
 +    // remove duplicates from both hashmaps
 +    same_in_both_hashmaps.iter().for_each(|(k, v)| {
 +        assert!(old_stats_deduped.remove(k) == Some(*v));
 +        assert!(new_stats_deduped.remove(k) == Some(*v));
 +    });
 +
 +    println!("\nStats:");
 +
 +    // list all new counts  (key is in new stats but not in old stats)
 +    new_stats_deduped
 +        .iter()
 +        .filter(|(new_key, _)| old_stats_deduped.get::<str>(&new_key).is_none())
 +        .for_each(|(new_key, new_value)| {
 +            println!("{} 0 => {}", new_key, new_value);
 +        });
 +
 +    // list all changed counts (key is in both maps but value differs)
 +    new_stats_deduped
 +        .iter()
 +        .filter(|(new_key, _new_val)| old_stats_deduped.get::<str>(&new_key).is_some())
 +        .for_each(|(new_key, new_val)| {
 +            let old_val = old_stats_deduped.get::<str>(&new_key).unwrap();
 +            println!("{} {} => {}", new_key, old_val, new_val);
 +        });
 +
 +    // list all gone counts (key is in old status but not in new stats)
 +    old_stats_deduped
 +        .iter()
 +        .filter(|(old_key, _)| new_stats_deduped.get::<&String>(&old_key).is_none())
 +        .filter(|(old_key, _)| lint_filter.is_empty() || lint_filter.contains(old_key))
 +        .for_each(|(old_key, old_value)| {
 +            println!("{} {} => 0", old_key, old_value);
 +        });
 +}
 +
 +/// Create necessary directories to run the lintcheck tool.
 +///
 +/// # Panics
 +///
 +/// This function panics if creating one of the dirs fails.
 +fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) {
 +    std::fs::create_dir("target/lintcheck/").unwrap_or_else(|err| {
 +        if err.kind() != ErrorKind::AlreadyExists {
 +            panic!("cannot create lintcheck target dir");
 +        }
 +    });
 +    std::fs::create_dir(&krate_download_dir).unwrap_or_else(|err| {
 +        if err.kind() != ErrorKind::AlreadyExists {
 +            panic!("cannot create crate download dir");
 +        }
 +    });
 +    std::fs::create_dir(&extract_dir).unwrap_or_else(|err| {
 +        if err.kind() != ErrorKind::AlreadyExists {
 +            panic!("cannot create crate extraction dir");
 +        }
 +    });
 +}
 +
 +fn get_clap_config<'a>() -> ArgMatches<'a> {
 +    App::new("lintcheck")
 +        .about("run clippy on a set of crates and check output")
 +        .arg(
 +            Arg::with_name("only")
 +                .takes_value(true)
 +                .value_name("CRATE")
 +                .long("only")
 +                .help("only process a single crate of the list"),
 +        )
 +        .arg(
 +            Arg::with_name("crates-toml")
 +                .takes_value(true)
 +                .value_name("CRATES-SOURCES-TOML-PATH")
 +                .long("crates-toml")
 +                .help("set the path for a crates.toml where lintcheck should read the sources from"),
 +        )
 +        .arg(
 +            Arg::with_name("threads")
 +                .takes_value(true)
 +                .value_name("N")
 +                .short("j")
 +                .long("jobs")
 +                .help("number of threads to use, 0 automatic choice"),
 +        )
 +        .arg(
 +            Arg::with_name("fix")
 +                .long("--fix")
 +                .help("runs cargo clippy --fix and checks if all suggestions apply"),
 +        )
 +        .arg(
 +            Arg::with_name("filter")
 +                .long("--filter")
 +                .takes_value(true)
 +                .multiple(true)
 +                .value_name("clippy_lint_name")
 +                .help("apply a filter to only collect specified lints, this also overrides `allow` attributes"),
 +        )
 +        .arg(
 +            Arg::with_name("markdown")
 +                .long("--markdown")
 +                .help("change the reports table to use markdown links"),
 +        )
 +        .get_matches()
 +}
 +
 +/// Returns the path to the Clippy project directory
 +///
 +/// # Panics
 +///
 +/// Panics if the current directory could not be retrieved, there was an error reading any of the
 +/// Cargo.toml files or ancestor directory is the clippy root directory
 +#[must_use]
 +pub fn clippy_project_root() -> PathBuf {
 +    let current_dir = std::env::current_dir().unwrap();
 +    for path in current_dir.ancestors() {
 +        let result = std::fs::read_to_string(path.join("Cargo.toml"));
 +        if let Err(err) = &result {
 +            if err.kind() == std::io::ErrorKind::NotFound {
 +                continue;
 +            }
 +        }
 +
 +        let content = result.unwrap();
 +        if content.contains("[package]\nname = \"clippy\"") {
 +            return path.to_path_buf();
 +        }
 +    }
 +    panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
 +}
 +
 +#[test]
 +fn lintcheck_test() {
 +    let args = [
 +        "run",
 +        "--target-dir",
 +        "lintcheck/target",
 +        "--manifest-path",
 +        "./lintcheck/Cargo.toml",
 +        "--",
 +        "--crates-toml",
 +        "lintcheck/test_sources.toml",
 +    ];
 +    let status = std::process::Command::new("cargo")
 +        .args(&args)
 +        .current_dir("..") // repo root
 +        .status();
 +    //.output();
 +
 +    assert!(status.unwrap().success());
 +}
index bb29c71e9f455889ff8cb1f001ee916fb273b0c1,0000000000000000000000000000000000000000..03acb51306d7a92081b181f66bbb964bbca66a76
mode 100644,000000..100644
--- /dev/null
@@@ -1,3 -1,0 +1,3 @@@
- channel = "nightly-2022-04-07"
 +[toolchain]
++channel = "nightly-2022-05-05"
 +components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
index 32a09fdb9d9ffe6b407ea415b95487f863920367,0000000000000000000000000000000000000000..7de40fe63ac23a4559051c6f14c26b34ad4c067a
mode 100644,000000..100644
--- /dev/null
@@@ -1,352 -1,0 +1,351 @@@
-     let fallback_bundle =
-         rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
 +#![feature(rustc_private)]
 +#![feature(once_cell)]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_driver;
 +extern crate rustc_errors;
 +extern crate rustc_interface;
 +extern crate rustc_session;
 +extern crate rustc_span;
 +
 +use rustc_interface::interface;
 +use rustc_session::parse::ParseSess;
 +use rustc_span::symbol::Symbol;
 +use rustc_tools_util::VersionInfo;
 +
 +use std::borrow::Cow;
 +use std::env;
 +use std::lazy::SyncLazy;
 +use std::ops::Deref;
 +use std::panic;
 +use std::path::{Path, PathBuf};
 +use std::process::{exit, Command};
 +
 +/// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If
 +/// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`.
 +fn arg_value<'a, T: Deref<Target = str>>(
 +    args: &'a [T],
 +    find_arg: &str,
 +    pred: impl Fn(&str) -> bool,
 +) -> Option<&'a str> {
 +    let mut args = args.iter().map(Deref::deref);
 +    while let Some(arg) = args.next() {
 +        let mut arg = arg.splitn(2, '=');
 +        if arg.next() != Some(find_arg) {
 +            continue;
 +        }
 +
 +        match arg.next().or_else(|| args.next()) {
 +            Some(v) if pred(v) => return Some(v),
 +            _ => {},
 +        }
 +    }
 +    None
 +}
 +
 +#[test]
 +fn test_arg_value() {
 +    let args = &["--bar=bar", "--foobar", "123", "--foo"];
 +
 +    assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None);
 +    assert_eq!(arg_value(args, "--bar", |_| false), None);
 +    assert_eq!(arg_value(args, "--bar", |_| true), Some("bar"));
 +    assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar"));
 +    assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
 +    assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
 +    assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
 +    assert_eq!(arg_value(args, "--foo", |_| true), None);
 +}
 +
 +fn track_clippy_args(parse_sess: &mut ParseSess, args_env_var: &Option<String>) {
 +    parse_sess.env_depinfo.get_mut().insert((
 +        Symbol::intern("CLIPPY_ARGS"),
 +        args_env_var.as_deref().map(Symbol::intern),
 +    ));
 +}
 +
 +struct DefaultCallbacks;
 +impl rustc_driver::Callbacks for DefaultCallbacks {}
 +
 +/// This is different from `DefaultCallbacks` that it will inform Cargo to track the value of
 +/// `CLIPPY_ARGS` environment variable.
 +struct RustcCallbacks {
 +    clippy_args_var: Option<String>,
 +}
 +
 +impl rustc_driver::Callbacks for RustcCallbacks {
 +    fn config(&mut self, config: &mut interface::Config) {
 +        let clippy_args_var = self.clippy_args_var.take();
 +        config.parse_sess_created = Some(Box::new(move |parse_sess| {
 +            track_clippy_args(parse_sess, &clippy_args_var);
 +        }));
 +    }
 +}
 +
 +struct ClippyCallbacks {
 +    clippy_args_var: Option<String>,
 +}
 +
 +impl rustc_driver::Callbacks for ClippyCallbacks {
 +    fn config(&mut self, config: &mut interface::Config) {
 +        let previous = config.register_lints.take();
 +        let clippy_args_var = self.clippy_args_var.take();
 +        config.parse_sess_created = Some(Box::new(move |parse_sess| {
 +            track_clippy_args(parse_sess, &clippy_args_var);
 +        }));
 +        config.register_lints = Some(Box::new(move |sess, lint_store| {
 +            // technically we're ~guaranteed that this is none but might as well call anything that
 +            // is there already. Certainly it can't hurt.
 +            if let Some(previous) = &previous {
 +                (previous)(sess, lint_store);
 +            }
 +
 +            let conf = clippy_lints::read_conf(sess);
 +            clippy_lints::register_plugins(lint_store, sess, &conf);
 +            clippy_lints::register_pre_expansion_lints(lint_store, sess, &conf);
 +            clippy_lints::register_renamed(lint_store);
 +        }));
 +
 +        // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be
 +        // run on the unoptimized MIR. On the other hand this results in some false negatives. If
 +        // MIR passes can be enabled / disabled separately, we should figure out, what passes to
 +        // use for Clippy.
 +        config.opts.debugging_opts.mir_opt_level = Some(0);
 +    }
 +}
 +
 +fn display_help() {
 +    println!(
 +        "\
 +Checks a package to catch common mistakes and improve your Rust code.
 +
 +Usage:
 +    cargo clippy [options] [--] [<opts>...]
 +
 +Common options:
 +    -h, --help               Print this message
 +        --rustc              Pass all args to rustc
 +    -V, --version            Print version info and exit
 +
 +Other options are the same as `cargo check`.
 +
 +To allow or deny a lint from the command line you can use `cargo clippy --`
 +with:
 +
 +    -W --warn OPT       Set lint warnings
 +    -A --allow OPT      Set lint allowed
 +    -D --deny OPT       Set lint denied
 +    -F --forbid OPT     Set lint forbidden
 +
 +You can use tool lints to allow or deny lints from your code, eg.:
 +
 +    #[allow(clippy::needless_lifetimes)]
 +"
 +    );
 +}
 +
 +const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new";
 +
 +static ICE_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> = SyncLazy::new(|| {
 +    let hook = panic::take_hook();
 +    panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL)));
 +    hook
 +});
 +
 +fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
 +    // Invoke our ICE handler, which prints the actual panic message and optionally a backtrace
 +    (*ICE_HOOK)(info);
 +
 +    // Separate the output with an empty line
 +    eprintln!();
 +
++    let fallback_bundle = rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
 +    let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
 +        rustc_errors::ColorConfig::Auto,
 +        None,
 +        None,
 +        fallback_bundle,
 +        false,
 +        false,
 +        None,
 +        false,
 +    ));
 +    let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
 +
 +    // a .span_bug or .bug call has already printed what
 +    // it wants to print.
 +    if !info.payload().is::<rustc_errors::ExplicitBug>() {
 +        let mut d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
 +        handler.emit_diagnostic(&mut d);
 +    }
 +
 +    let version_info = rustc_tools_util::get_version_info!();
 +
 +    let xs: Vec<Cow<'static, str>> = vec![
 +        "the compiler unexpectedly panicked. this is a bug.".into(),
 +        format!("we would appreciate a bug report: {}", bug_report_url).into(),
 +        format!("Clippy version: {}", version_info).into(),
 +    ];
 +
 +    for note in &xs {
 +        handler.note_without_error(note.as_ref());
 +    }
 +
 +    // If backtraces are enabled, also print the query stack
 +    let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");
 +
 +    let num_frames = if backtrace { None } else { Some(2) };
 +
 +    interface::try_print_query_stack(&handler, num_frames);
 +}
 +
 +fn toolchain_path(home: Option<String>, toolchain: Option<String>) -> Option<PathBuf> {
 +    home.and_then(|home| {
 +        toolchain.map(|toolchain| {
 +            let mut path = PathBuf::from(home);
 +            path.push("toolchains");
 +            path.push(toolchain);
 +            path
 +        })
 +    })
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +pub fn main() {
 +    rustc_driver::init_rustc_env_logger();
 +    SyncLazy::force(&ICE_HOOK);
 +    exit(rustc_driver::catch_with_exit_code(move || {
 +        let mut orig_args: Vec<String> = env::args().collect();
 +
 +        // Get the sysroot, looking from most specific to this invocation to the least:
 +        // - command line
 +        // - runtime environment
 +        //    - SYSROOT
 +        //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
 +        // - sysroot from rustc in the path
 +        // - compile-time environment
 +        //    - SYSROOT
 +        //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
 +        let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true);
 +        let have_sys_root_arg = sys_root_arg.is_some();
 +        let sys_root = sys_root_arg
 +            .map(PathBuf::from)
 +            .or_else(|| std::env::var("SYSROOT").ok().map(PathBuf::from))
 +            .or_else(|| {
 +                let home = std::env::var("RUSTUP_HOME")
 +                    .or_else(|_| std::env::var("MULTIRUST_HOME"))
 +                    .ok();
 +                let toolchain = std::env::var("RUSTUP_TOOLCHAIN")
 +                    .or_else(|_| std::env::var("MULTIRUST_TOOLCHAIN"))
 +                    .ok();
 +                toolchain_path(home, toolchain)
 +            })
 +            .or_else(|| {
 +                Command::new("rustc")
 +                    .arg("--print")
 +                    .arg("sysroot")
 +                    .output()
 +                    .ok()
 +                    .and_then(|out| String::from_utf8(out.stdout).ok())
 +                    .map(|s| PathBuf::from(s.trim()))
 +            })
 +            .or_else(|| option_env!("SYSROOT").map(PathBuf::from))
 +            .or_else(|| {
 +                let home = option_env!("RUSTUP_HOME")
 +                    .or(option_env!("MULTIRUST_HOME"))
 +                    .map(ToString::to_string);
 +                let toolchain = option_env!("RUSTUP_TOOLCHAIN")
 +                    .or(option_env!("MULTIRUST_TOOLCHAIN"))
 +                    .map(ToString::to_string);
 +                toolchain_path(home, toolchain)
 +            })
 +            .map(|pb| pb.to_string_lossy().to_string())
 +            .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");
 +
 +        // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc"
 +        // for example `clippy-driver --rustc --version` will print the rustc version that clippy-driver
 +        // uses
 +        if let Some(pos) = orig_args.iter().position(|arg| arg == "--rustc") {
 +            orig_args.remove(pos);
 +            orig_args[0] = "rustc".to_string();
 +
 +            // if we call "rustc", we need to pass --sysroot here as well
 +            let mut args: Vec<String> = orig_args.clone();
 +            if !have_sys_root_arg {
 +                args.extend(vec!["--sysroot".into(), sys_root]);
 +            };
 +
 +            return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run();
 +        }
 +
 +        if orig_args.iter().any(|a| a == "--version" || a == "-V") {
 +            let version_info = rustc_tools_util::get_version_info!();
 +            println!("{}", version_info);
 +            exit(0);
 +        }
 +
 +        // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
 +        // We're invoking the compiler programmatically, so we ignore this/
 +        let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref());
 +
 +        if wrapper_mode {
 +            // we still want to be able to invoke it normally though
 +            orig_args.remove(1);
 +        }
 +
 +        if !wrapper_mode && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1) {
 +            display_help();
 +            exit(0);
 +        }
 +
 +        // this conditional check for the --sysroot flag is there so users can call
 +        // `clippy_driver` directly
 +        // without having to pass --sysroot or anything
 +        let mut args: Vec<String> = orig_args.clone();
 +        if !have_sys_root_arg {
 +            args.extend(vec!["--sysroot".into(), sys_root]);
 +        };
 +
 +        let mut no_deps = false;
 +        let clippy_args_var = env::var("CLIPPY_ARGS").ok();
 +        let clippy_args = clippy_args_var
 +            .as_deref()
 +            .unwrap_or_default()
 +            .split("__CLIPPY_HACKERY__")
 +            .filter_map(|s| match s {
 +                "" => None,
 +                "--no-deps" => {
 +                    no_deps = true;
 +                    None
 +                },
 +                _ => Some(s.to_string()),
 +            })
 +            .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()])
 +            .collect::<Vec<String>>();
 +
 +        // We enable Clippy if one of the following conditions is met
 +        // - IF Clippy is run on its test suite OR
 +        // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN
 +        //    - IF `--no-deps` is not set (`!no_deps`) OR
 +        //    - IF `--no-deps` is set and Clippy is run on the specified primary package
 +        let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some();
 +        let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok();
 +
 +        let clippy_enabled = !cap_lints_allow && (!no_deps || in_primary_package);
 +        if clippy_enabled {
 +            args.extend(clippy_args);
 +        }
 +
 +        if clippy_enabled {
 +            rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var }).run()
 +        } else {
 +            rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var }).run()
 +        }
 +    }))
 +}
index 67af9d05bf402473a3cc6d34080872e15c30ec61,0000000000000000000000000000000000000000..eb97d1933d5d71bdd8621993e7cd6a50538d982d
mode 100644,000000..100644
--- /dev/null
@@@ -1,95 -1,0 +1,99 @@@
-     // internal lints only exist if we build with the internal feature
 +//! 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);
 +    }
 +}
 +
 +#[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");
 +
 +    // 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");
 +}
 +
 +fn run_clippy_for_package(project: &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")
 +        .arg("--")
 +        .args(&["-D", "clippy::all"])
 +        .args(&["-D", "clippy::pedantic"])
 +        .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(&["-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 dc82ba891fb1f098d2c1b1099c5a0453ec67b311,0000000000000000000000000000000000000000..dd1d441203600cf6f3d124177a4ef29f1ea0d67c
mode 100644,000000..100644
--- /dev/null
@@@ -1,111 -1,0 +1,111 @@@
-     // * don't have puncuation at the end of the last sentence
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use std::ffi::OsStr;
 +use std::path::PathBuf;
 +
 +use regex::RegexSet;
 +
 +#[derive(Debug)]
 +struct Message {
 +    path: PathBuf,
 +    bad_lines: Vec<String>,
 +}
 +
 +impl Message {
 +    fn new(path: PathBuf) -> Self {
 +        let content: String = std::fs::read_to_string(&path).unwrap();
 +        // we don't want the first letter after "error: ", "help: " ... to be capitalized
 +        // also no punctuation (except for "?" ?) at the end of a line
 +        let regex_set: RegexSet = 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 "?"
 +        let exceptions_set: RegexSet = RegexSet::new(&[
 +            r".*C-like enum variant discriminant is not portable to 32-bit targets",
 +            r".*did you mean `unix`?",
 +            r".*the arguments may be inverted...",
 +            r".*Intel x86 assembly syntax used",
 +            r".*AT&T x86 assembly syntax used",
 +            r".*remove .*the return type...",
 +            r"note: Clippy version: .*",
 +            r"the compiler unexpectedly panicked. this is a bug.",
 +        ])
 +        .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 558d129916078b4d4883203ec7f311dc5d41cc11,0000000000000000000000000000000000000000..0852fe65aafd0241e2938cef70a1cdee20964720
mode 100644,000000..100644
--- /dev/null
@@@ -1,49 -1,0 +1,49 @@@
- error: this call is collspible
 +error: this call is collapsible
 +  --> $DIR/collapsible_span_lint_calls.rs:36:9
 +   |
 +LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 +LL | |             db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable);
 +LL | |         });
 +   | |__________^ help: collapse into: `span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable)`
 +   |
 +note: the lint level is defined here
 +  --> $DIR/collapsible_span_lint_calls.rs:2:9
 +   |
 +LL | #![deny(clippy::internal)]
 +   |         ^^^^^^^^^^^^^^^^
 +   = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]`
 +
 +error: this call is collapsible
 +  --> $DIR/collapsible_span_lint_calls.rs:39:9
 +   |
 +LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 +LL | |             db.span_help(expr.span, help_msg);
 +LL | |         });
 +   | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)`
 +
 +error: this call is collapsible
 +  --> $DIR/collapsible_span_lint_calls.rs:42:9
 +   |
 +LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 +LL | |             db.help(help_msg);
 +LL | |         });
 +   | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)`
 +
- error: this call is collspible
++error: this call is collapsible
 +  --> $DIR/collapsible_span_lint_calls.rs:45:9
 +   |
 +LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 +LL | |             db.span_note(expr.span, note_msg);
 +LL | |         });
 +   | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)`
 +
++error: this call is collapsible
 +  --> $DIR/collapsible_span_lint_calls.rs:48:9
 +   |
 +LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 +LL | |             db.note(note_msg);
 +LL | |         });
 +   | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg)`
 +
 +error: aborting due to 5 previous errors
 +
index 6b7fd6efe394c0ebdf597fa5a4b6cc740fadfa61,0000000000000000000000000000000000000000..eaea218e128886373d6c4ebc82fe5e377a831ce8
mode 100644,000000..100644
--- /dev/null
@@@ -1,37 -1,0 +1,37 @@@
- #![allow(clippy::missing_clippy_version_attribute)]
 +// run-rustfix
 +#![deny(clippy::internal)]
++#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)]
 +#![feature(rustc_private)]
 +
 +extern crate rustc_span;
 +
 +use rustc_span::symbol::Symbol;
 +
 +macro_rules! sym {
 +    ($tt:tt) => {
 +        rustc_span::symbol::Symbol::intern(stringify!($tt))
 +    };
 +}
 +
 +fn main() {
 +    // Direct use of Symbol::intern
 +    let _ = rustc_span::sym::f32;
 +
 +    // Using a sym macro
 +    let _ = rustc_span::sym::f32;
 +
 +    // Correct suggestion when symbol isn't stringified constant name
 +    let _ = rustc_span::sym::proc_dash_macro;
 +
 +    // interning a keyword
 +    let _ = rustc_span::symbol::kw::SelfLower;
 +
 +    // Interning a symbol that is not defined
 +    let _ = Symbol::intern("xyz123");
 +    let _ = sym!(xyz123);
 +
 +    // Using a different `intern` function
 +    let _ = intern("f32");
 +}
 +
 +fn intern(_: &str) {}
index 98d7d7adad17037fbdc275cf9500582717b0bd71,0000000000000000000000000000000000000000..7efebb8fae486f456a3ade83ac5c510acb81fdc4
mode 100644,000000..100644
--- /dev/null
@@@ -1,37 -1,0 +1,37 @@@
- #![allow(clippy::missing_clippy_version_attribute)]
 +// run-rustfix
 +#![deny(clippy::internal)]
++#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)]
 +#![feature(rustc_private)]
 +
 +extern crate rustc_span;
 +
 +use rustc_span::symbol::Symbol;
 +
 +macro_rules! sym {
 +    ($tt:tt) => {
 +        rustc_span::symbol::Symbol::intern(stringify!($tt))
 +    };
 +}
 +
 +fn main() {
 +    // Direct use of Symbol::intern
 +    let _ = Symbol::intern("f32");
 +
 +    // Using a sym macro
 +    let _ = sym!(f32);
 +
 +    // Correct suggestion when symbol isn't stringified constant name
 +    let _ = Symbol::intern("proc-macro");
 +
 +    // interning a keyword
 +    let _ = Symbol::intern("self");
 +
 +    // Interning a symbol that is not defined
 +    let _ = Symbol::intern("xyz123");
 +    let _ = sym!(xyz123);
 +
 +    // Using a different `intern` function
 +    let _ = intern("f32");
 +}
 +
 +fn intern(_: &str) {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fbef5c4564b1bb6444016ec07b0ca11ca91bb5c6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++#![warn(clippy::await_holding_invalid_type)]
++use std::net::Ipv4Addr;
++
++async fn bad() -> u32 {
++    let _x = String::from("hello");
++    baz().await
++}
++
++async fn bad_reason() -> u32 {
++    let _x = Ipv4Addr::new(127, 0, 0, 1);
++    baz().await
++}
++
++async fn good() -> u32 {
++    {
++        let _x = String::from("hi!");
++        let _y = Ipv4Addr::new(127, 0, 0, 1);
++    }
++    baz().await;
++    let _x = String::from("hi!");
++    47
++}
++
++async fn baz() -> u32 {
++    42
++}
++
++#[allow(clippy::manual_async_fn)]
++fn block_bad() -> impl std::future::Future<Output = u32> {
++    async move {
++        let _x = String::from("hi!");
++        baz().await
++    }
++}
++
++fn main() {
++    good();
++    bad();
++    bad_reason();
++    block_bad();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..62c45b54634f4298060b729a3674e9d2bfe1468a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++error: `std::string::String` may not be held across an `await` point per `clippy.toml`
++  --> $DIR/await_holding_invalid_type.rs:5:9
++   |
++LL |     let _x = String::from("hello");
++   |         ^^
++   |
++   = note: `-D clippy::await-holding-invalid-type` implied by `-D warnings`
++   = note: strings are bad
++
++error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml`
++  --> $DIR/await_holding_invalid_type.rs:10:9
++   |
++LL |     let _x = Ipv4Addr::new(127, 0, 0, 1);
++   |         ^^
++
++error: `std::string::String` may not be held across an `await` point per `clippy.toml`
++  --> $DIR/await_holding_invalid_type.rs:31:13
++   |
++LL |         let _x = String::from("hi!");
++   |             ^^
++   |
++   = note: strings are bad
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..79990096b84e0947281b3714c1142f03b33841bf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++await-holding-invalid-types = [
++    { path = "std::string::String", reason = "strings are bad" },
++    "std::net::Ipv4Addr",
++]
index e678c896fd3e3b3dc8e7bcba92fb5b641a6f8797,0000000000000000000000000000000000000000..4ac0378544c7d724ac4f49e28a7168791e21044a
mode 100644,000000..100644
--- /dev/null
@@@ -1,59 -1,0 +1,60 @@@
 +#![warn(clippy::too_many_lines)]
++#![allow(clippy::let_unit_value)]
 +
 +// This function should be considered one line.
 +fn many_comments_but_one_line_of_code() {
 +    /* println!("This is good."); */
 +    // println!("This is good.");
 +    /* */ // println!("This is good.");
 +    /* */ // println!("This is good.");
 +    /* */ // println!("This is good.");
 +    /* */ // println!("This is good.");
 +    /* println!("This is good.");
 +    println!("This is good.");
 +    println!("This is good."); */
 +    println!("This is good.");
 +}
 +
 +// This should be considered two and a fail.
 +fn too_many_lines() {
 +    println!("This is bad.");
 +    println!("This is bad.");
 +}
 +
 +// This should only fail once (#7517).
 +async fn async_too_many_lines() {
 +    println!("This is bad.");
 +    println!("This is bad.");
 +}
 +
 +// This should fail only once, without failing on the closure.
 +fn closure_too_many_lines() {
 +    let _ = {
 +        println!("This is bad.");
 +        println!("This is bad.");
 +    };
 +}
 +
 +// This should be considered one line.
 +#[rustfmt::skip]
 +fn comment_starts_after_code() {
 +    let _ = 5; /* closing comment. */ /*
 +    this line shouldn't be counted theoretically.
 +    */
 +}
 +
 +// This should be considered one line.
 +fn comment_after_code() {
 +    let _ = 5; /* this line should get counted once. */
 +}
 +
 +// This should fail since it is technically two lines.
 +#[rustfmt::skip]
 +fn comment_before_code() {
 +    let _ = "test";
 +    /* This comment extends to the front of
 +    the code but this line should still count. */ let _ = 5;
 +}
 +
 +// This should be considered one line.
 +fn main() {}
index d736bf899735a5cfb5ed02eef151b4c20cbd635f,0000000000000000000000000000000000000000..dc255bdcabafea568c725c28f65b79074ec024a4
mode 100644,000000..100644
--- /dev/null
@@@ -1,43 -1,0 +1,43 @@@
-   --> $DIR/test.rs:18:1
 +error: this function has too many lines (2/1)
-   --> $DIR/test.rs:24:1
++  --> $DIR/test.rs:19:1
 +   |
 +LL | / fn too_many_lines() {
 +LL | |     println!("This is bad.");
 +LL | |     println!("This is bad.");
 +LL | | }
 +   | |_^
 +   |
 +   = note: `-D clippy::too-many-lines` implied by `-D warnings`
 +
 +error: this function has too many lines (4/1)
-   --> $DIR/test.rs:30:1
++  --> $DIR/test.rs:25:1
 +   |
 +LL | / async fn async_too_many_lines() {
 +LL | |     println!("This is bad.");
 +LL | |     println!("This is bad.");
 +LL | | }
 +   | |_^
 +
 +error: this function has too many lines (4/1)
-   --> $DIR/test.rs:52:1
++  --> $DIR/test.rs:31:1
 +   |
 +LL | / fn closure_too_many_lines() {
 +LL | |     let _ = {
 +LL | |         println!("This is bad.");
 +LL | |         println!("This is bad.");
 +LL | |     };
 +LL | | }
 +   | |_^
 +
 +error: this function has too many lines (2/1)
++  --> $DIR/test.rs:53:1
 +   |
 +LL | / fn comment_before_code() {
 +LL | |     let _ = "test";
 +LL | |     /* This comment extends to the front of
 +LL | |     the code but this line should still count. */ let _ = 5;
 +LL | | }
 +   | |_^
 +
 +error: aborting due to 4 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ea34bf9fbe01d2d9391064b919ef9142a8c36396
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++max-include-file-size = 600
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f3dbb6ad1cf59a4d4aa385ab5365aef2d9dc9ac2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++#![warn(clippy::large_include_file)]
++
++// Good
++const GOOD_INCLUDE_BYTES: &[u8; 581] = include_bytes!("large_include_file.rs");
++const GOOD_INCLUDE_STR: &str = include_str!("large_include_file.rs");
++
++#[allow(clippy::large_include_file)]
++const ALLOWED_TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
++#[allow(clippy::large_include_file)]
++const ALLOWED_TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
++
++// Bad
++const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
++const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6a685a58318b9b16d0b6d4189f2bae64eae86216
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++error: attempted to include a large file
++  --> $DIR/large_include_file.rs:13:43
++   |
++LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
++   |                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::large-include-file` implied by `-D warnings`
++   = note: the configuration allows a maximum size of 600 bytes
++   = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: attempted to include a large file
++  --> $DIR/large_include_file.rs:14:35
++   |
++LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
++   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: the configuration allows a maximum size of 600 bytes
++   = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9829c46bc00f63cc6a1dedc8677cb754f2d39b71
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Maecenas accumsan lacus vel facilisis volutpat. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Tellus id interdum velit laoreet id donec ultrices. Est ultricies integer quis auctor elit sed vulputate. Erat velit scelerisque in dictum non consectetur a erat nam. Sed blandit libero volutpat sed. Tortor condimentum lacinia quis vel eros. Enim ut tellus elementum sagittis vitae et leo duis. Congue mauris rhoncus aenean vel elit scelerisque. Id consectetur purus ut faucibus pulvinar elementum integer.
index a1e7361c0cb32fe4bbe9e9c5a09320691b3b123a,0000000000000000000000000000000000000000..5dae5af7eb5a9cc9413582d37256b2869abd32c8
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
- error: you are using an explicit closure for copying elements
++error: you are using an explicit closure for cloning elements
 +  --> $DIR/min_rust_version.rs:74:26
 +   |
 +LL |     let _: Option<u64> = Some(&16).map(|b| *b);
 +   |                          ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `Some(&16).cloned()`
 +   |
 +   = note: `-D clippy::map-clone` implied by `-D warnings`
 +
 +error: aborting due to previous error
 +
index 00ddbd608a7c725dbee190ad54a3cc3c2c6dd88e,0000000000000000000000000000000000000000..8701809b4daaaa8901bafbfc9479ac4874244899
mode 100644,000000..100644
--- /dev/null
@@@ -1,4 -1,0 +1,4 @@@
- error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `third-party` at line 5 column 1
++error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `await-holding-invalid-types`, `max-include-file-size`, `third-party` at line 5 column 1
 +
 +error: aborting due to previous error
 +
index 7477c01ca78283e173392dd12c86c40a210ebe8d,0000000000000000000000000000000000000000..7bea9563d47d3d1f34b81de73794aea329f10520
mode 100644,000000..100644
--- /dev/null
@@@ -1,33 -1,0 +1,39 @@@
- #![allow(non_fmt_panics)]
++#![allow(non_fmt_panics, clippy::needless_bool)]
 +
 +macro_rules! assert_const {
 +    ($len:expr) => {
 +        assert!($len > 0);
 +        debug_assert!($len < 0);
 +    };
 +}
 +fn main() {
 +    assert!(true);
 +    assert!(false);
 +    assert!(true, "true message");
 +    assert!(false, "false message");
 +
 +    let msg = "panic message";
 +    assert!(false, "{}", msg.to_uppercase());
 +
 +    const B: bool = true;
 +    assert!(B);
 +
 +    const C: bool = false;
 +    assert!(C);
 +    assert!(C, "C message");
 +
 +    debug_assert!(true);
 +    // Don't lint this, since there is no better way for expressing "Only panic in debug mode".
 +    debug_assert!(false); // #3948
 +    assert_const!(3);
 +    assert_const!(-1);
 +
-     // Don't lint on this:
++    // Don't lint if based on `cfg!(..)`:
 +    assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf")));
++
++    let flag: bool = cfg!(not(feature = "asdf"));
++    assert!(flag);
++
++    const CFG_FLAG: &bool = &cfg!(feature = "hey");
++    assert!(!CFG_FLAG);
 +}
index 4b7b7fec78fe8dd7dc9e1941c3871976c60de176,0000000000000000000000000000000000000000..ed7b17651e675e8942bceb5867087398b282eff2
mode 100644,000000..100644
--- /dev/null
@@@ -1,74 -1,0 +1,74 @@@
-     // Shound not trigger `used_underscore_binding`
 +// compile-flags: --emit=link
 +// no-prefer-dynamic
 +
 +#![crate_type = "proc-macro"]
 +#![feature(repr128, proc_macro_quote)]
 +#![allow(incomplete_features)]
 +#![allow(clippy::field_reassign_with_default)]
 +#![allow(clippy::eq_op)]
 +
 +extern crate proc_macro;
 +
 +use proc_macro::{quote, TokenStream};
 +
 +#[proc_macro_derive(DeriveSomething)]
 +pub fn derive(_: TokenStream) -> TokenStream {
++    // Should not trigger `used_underscore_binding`
 +    let _inside_derive = 1;
 +    assert_eq!(_inside_derive, _inside_derive);
 +
 +    let output = quote! {
 +        // Should not trigger `useless_attribute`
 +        #[allow(dead_code)]
 +        extern crate rustc_middle;
 +    };
 +    output
 +}
 +
 +#[proc_macro_derive(FieldReassignWithDefault)]
 +pub fn derive_foo(_input: TokenStream) -> TokenStream {
 +    quote! {
 +        #[derive(Default)]
 +        struct A {
 +            pub i: i32,
 +            pub j: i64,
 +        }
 +        #[automatically_derived]
 +        fn lint() {
 +            let mut a: A = Default::default();
 +            a.i = 42;
 +            a;
 +        }
 +    }
 +}
 +
 +#[proc_macro_derive(StructAUseSelf)]
 +pub fn derive_use_self(_input: TokenStream) -> proc_macro::TokenStream {
 +    quote! {
 +        struct A;
 +        impl A {
 +            fn new() -> A {
 +                A
 +            }
 +        }
 +    }
 +}
 +
 +#[proc_macro_derive(ClippyMiniMacroTest)]
 +pub fn mini_macro(_: TokenStream) -> TokenStream {
 +    quote!(
 +        #[allow(unused)]
 +        fn needless_take_by_value(s: String) {
 +            println!("{}", s.len());
 +        }
 +        #[allow(unused)]
 +        fn needless_loop(items: &[u8]) {
 +            for i in 0..items.len() {
 +                println!("{}", items[i]);
 +            }
 +        }
 +        fn line_wrapper() {
 +            println!("{}", line!());
 +        }
 +    )
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8ea631f2bbd420fd20795fde30cf81e1bd939d80
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++// compile-flags: --emit=link
++// no-prefer-dynamic
++
++#![crate_type = "proc-macro"]
++
++extern crate proc_macro;
++
++use proc_macro::{token_stream::IntoIter, Group, Span, TokenStream, TokenTree};
++
++#[proc_macro]
++pub fn with_span(input: TokenStream) -> TokenStream {
++    let mut iter = input.into_iter();
++    let span = iter.next().unwrap().span();
++    let mut res = TokenStream::new();
++    write_with_span(span, iter, &mut res);
++    res
++}
++
++fn write_with_span(s: Span, input: IntoIter, out: &mut TokenStream) {
++    for mut tt in input {
++        if let TokenTree::Group(g) = tt {
++            let mut stream = TokenStream::new();
++            write_with_span(s, g.stream().into_iter(), &mut stream);
++            let mut group = Group::new(g.delimiter(), stream);
++            group.set_span(s);
++            out.extend([TokenTree::Group(group)]);
++        } else {
++            tt.set_span(s);
++            out.extend([tt]);
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..860642363b5f0ecf370379dc2ee071b58a66e72a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++// run-rustfix
++#![warn(clippy::bytes_count_to_len)]
++use std::fs::File;
++use std::io::Read;
++
++fn main() {
++    // should fix, because type is String
++    let _ = String::from("foo").len();
++
++    let s1 = String::from("foo");
++    let _ = s1.len();
++
++    // should fix, because type is &str
++    let _ = "foo".len();
++
++    let s2 = "foo";
++    let _ = s2.len();
++
++    // make sure using count() normally doesn't trigger warning
++    let vector = [0, 1, 2];
++    let _ = vector.iter().count();
++
++    // The type is slice, so should not fix
++    let _ = &[1, 2, 3].bytes().count();
++
++    let bytes: &[u8] = &[1, 2, 3];
++    bytes.bytes().count();
++
++    // The type is File, so should not fix
++    let _ = File::open("foobar").unwrap().bytes().count();
++
++    let f = File::open("foobar").unwrap();
++    let _ = f.bytes().count();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..162730c2842a1615db2b1fe1cbb5e68d369737a7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++// run-rustfix
++#![warn(clippy::bytes_count_to_len)]
++use std::fs::File;
++use std::io::Read;
++
++fn main() {
++    // should fix, because type is String
++    let _ = String::from("foo").bytes().count();
++
++    let s1 = String::from("foo");
++    let _ = s1.bytes().count();
++
++    // should fix, because type is &str
++    let _ = "foo".bytes().count();
++
++    let s2 = "foo";
++    let _ = s2.bytes().count();
++
++    // make sure using count() normally doesn't trigger warning
++    let vector = [0, 1, 2];
++    let _ = vector.iter().count();
++
++    // The type is slice, so should not fix
++    let _ = &[1, 2, 3].bytes().count();
++
++    let bytes: &[u8] = &[1, 2, 3];
++    bytes.bytes().count();
++
++    // The type is File, so should not fix
++    let _ = File::open("foobar").unwrap().bytes().count();
++
++    let f = File::open("foobar").unwrap();
++    let _ = f.bytes().count();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..224deb779871c7bdd81ffa5b38d0d9dcfff2fbb4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: using long and hard to read `.bytes().count()`
++  --> $DIR/bytes_count_to_len.rs:8:13
++   |
++LL |     let _ = String::from("foo").bytes().count();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `String::from("foo").len()`
++   |
++   = note: `-D clippy::bytes-count-to-len` implied by `-D warnings`
++
++error: using long and hard to read `.bytes().count()`
++  --> $DIR/bytes_count_to_len.rs:11:13
++   |
++LL |     let _ = s1.bytes().count();
++   |             ^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `s1.len()`
++
++error: using long and hard to read `.bytes().count()`
++  --> $DIR/bytes_count_to_len.rs:14:13
++   |
++LL |     let _ = "foo".bytes().count();
++   |             ^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `"foo".len()`
++
++error: using long and hard to read `.bytes().count()`
++  --> $DIR/bytes_count_to_len.rs:17:13
++   |
++LL |     let _ = s2.bytes().count();
++   |             ^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `s2.len()`
++
++error: aborting due to 4 previous errors
++
index cf85a5ca931dd1e560134cf39304659254af3b43,0000000000000000000000000000000000000000..e6031e9adaeb66c084d8c96cb421d9d5ee186d91
mode 100644,000000..100644
--- /dev/null
@@@ -1,254 -1,0 +1,262 @@@
- #[warn(
 +#![feature(repr128)]
 +#![allow(incomplete_features)]
- #[allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)]
++#![warn(
 +    clippy::cast_precision_loss,
 +    clippy::cast_possible_truncation,
 +    clippy::cast_sign_loss,
 +    clippy::cast_possible_wrap
 +)]
++#![allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)]
++
 +fn main() {
 +    // Test clippy::cast_precision_loss
 +    let x0 = 1i32;
 +    x0 as f32;
 +    let x1 = 1i64;
 +    x1 as f32;
 +    x1 as f64;
 +    let x2 = 1u32;
 +    x2 as f32;
 +    let x3 = 1u64;
 +    x3 as f32;
 +    x3 as f64;
 +    // Test clippy::cast_possible_truncation
 +    1f32 as i32;
 +    1f32 as u32;
 +    1f64 as f32;
 +    1i32 as i8;
 +    1i32 as u8;
 +    1f64 as isize;
 +    1f64 as usize;
 +    // Test clippy::cast_possible_wrap
 +    1u8 as i8;
 +    1u16 as i16;
 +    1u32 as i32;
 +    1u64 as i64;
 +    1usize as isize;
 +    // Test clippy::cast_sign_loss
 +    1i32 as u32;
 +    -1i32 as u32;
 +    1isize as usize;
 +    -1isize as usize;
 +    0i8 as u8;
 +    i8::MAX as u8;
 +    i16::MAX as u16;
 +    i32::MAX as u32;
 +    i64::MAX as u64;
 +    i128::MAX as u128;
 +
 +    (-1i8).abs() as u8;
 +    (-1i16).abs() as u16;
 +    (-1i32).abs() as u32;
 +    (-1i64).abs() as u64;
 +    (-1isize).abs() as usize;
 +
 +    (-1i8).checked_abs().unwrap() as u8;
 +    (-1i16).checked_abs().unwrap() as u16;
 +    (-1i32).checked_abs().unwrap() as u32;
 +    (-1i64).checked_abs().unwrap() as u64;
 +    (-1isize).checked_abs().unwrap() as usize;
 +
 +    (-1i8).rem_euclid(1i8) as u8;
 +    (-1i8).rem_euclid(1i8) as u16;
 +    (-1i16).rem_euclid(1i16) as u16;
 +    (-1i16).rem_euclid(1i16) as u32;
 +    (-1i32).rem_euclid(1i32) as u32;
 +    (-1i32).rem_euclid(1i32) as u64;
 +    (-1i64).rem_euclid(1i64) as u64;
 +    (-1i64).rem_euclid(1i64) as u128;
 +    (-1isize).rem_euclid(1isize) as usize;
 +    (1i8).rem_euclid(-1i8) as u8;
 +    (1i8).rem_euclid(-1i8) as u16;
 +    (1i16).rem_euclid(-1i16) as u16;
 +    (1i16).rem_euclid(-1i16) as u32;
 +    (1i32).rem_euclid(-1i32) as u32;
 +    (1i32).rem_euclid(-1i32) as u64;
 +    (1i64).rem_euclid(-1i64) as u64;
 +    (1i64).rem_euclid(-1i64) as u128;
 +    (1isize).rem_euclid(-1isize) as usize;
 +
 +    (-1i8).checked_rem_euclid(1i8).unwrap() as u8;
 +    (-1i8).checked_rem_euclid(1i8).unwrap() as u16;
 +    (-1i16).checked_rem_euclid(1i16).unwrap() as u16;
 +    (-1i16).checked_rem_euclid(1i16).unwrap() as u32;
 +    (-1i32).checked_rem_euclid(1i32).unwrap() as u32;
 +    (-1i32).checked_rem_euclid(1i32).unwrap() as u64;
 +    (-1i64).checked_rem_euclid(1i64).unwrap() as u64;
 +    (-1i64).checked_rem_euclid(1i64).unwrap() as u128;
 +    (-1isize).checked_rem_euclid(1isize).unwrap() as usize;
 +    (1i8).checked_rem_euclid(-1i8).unwrap() as u8;
 +    (1i8).checked_rem_euclid(-1i8).unwrap() as u16;
 +    (1i16).checked_rem_euclid(-1i16).unwrap() as u16;
 +    (1i16).checked_rem_euclid(-1i16).unwrap() as u32;
 +    (1i32).checked_rem_euclid(-1i32).unwrap() as u32;
 +    (1i32).checked_rem_euclid(-1i32).unwrap() as u64;
 +    (1i64).checked_rem_euclid(-1i64).unwrap() as u64;
 +    (1i64).checked_rem_euclid(-1i64).unwrap() as u128;
 +    (1isize).checked_rem_euclid(-1isize).unwrap() as usize;
 +
 +    // no lint for `cast_possible_truncation`
 +    // with `signum` method call (see issue #5395)
 +    let x: i64 = 5;
 +    let _ = x.signum() as i32;
 +
 +    let s = x.signum();
 +    let _ = s as i32;
 +
 +    // Test for signed min
 +    (-99999999999i64).min(1) as i8; // should be linted because signed
 +
 +    // Test for various operations that remove enough bits for the result to fit
 +    (999999u64 & 1) as u8;
 +    (999999u64 % 15) as u8;
 +    (999999u64 / 0x1_0000_0000_0000) as u16;
 +    ({ 999999u64 >> 56 }) as u8;
 +    ({
 +        let x = 999999u64;
 +        x.min(1)
 +    }) as u8;
 +    999999u64.clamp(0, 255) as u8;
 +    999999u64.clamp(0, 256) as u8; // should still be linted
 +
 +    #[derive(Clone, Copy)]
 +    enum E1 {
 +        A,
 +        B,
 +        C,
 +    }
 +    impl E1 {
 +        fn test(self) {
 +            let _ = self as u8; // Don't lint. `0..=2` fits in u8
 +        }
 +    }
 +
 +    #[derive(Clone, Copy)]
 +    enum E2 {
 +        A = 255,
 +        B,
 +    }
 +    impl E2 {
 +        fn test(self) {
 +            let _ = self as u8;
 +            let _ = Self::B as u8;
 +            let _ = self as i16; // Don't lint. `255..=256` fits in i16
 +            let _ = Self::A as u8; // Don't lint.
 +        }
 +    }
 +
 +    #[derive(Clone, Copy)]
 +    enum E3 {
 +        A = -1,
 +        B,
 +        C = 50,
 +    }
 +    impl E3 {
 +        fn test(self) {
 +            let _ = self as i8; // Don't lint. `-1..=50` fits in i8
 +        }
 +    }
 +
 +    #[derive(Clone, Copy)]
 +    enum E4 {
 +        A = -128,
 +        B,
 +    }
 +    impl E4 {
 +        fn test(self) {
 +            let _ = self as i8; // Don't lint. `-128..=-127` fits in i8
 +        }
 +    }
 +
 +    #[derive(Clone, Copy)]
 +    enum E5 {
 +        A = -129,
 +        B = 127,
 +    }
 +    impl E5 {
 +        fn test(self) {
 +            let _ = self as i8;
 +            let _ = Self::A as i8;
 +            let _ = self as i16; // Don't lint. `-129..=127` fits in i16
 +            let _ = Self::B as u8; // Don't lint.
 +        }
 +    }
 +
 +    #[derive(Clone, Copy)]
 +    #[repr(u32)]
 +    enum E6 {
 +        A = u16::MAX as u32,
 +        B,
 +    }
 +    impl E6 {
 +        fn test(self) {
 +            let _ = self as i16;
 +            let _ = Self::A as u16; // Don't lint. `2^16-1` fits in u16
 +            let _ = self as u32; // Don't lint. `2^16-1..=2^16` fits in u32
 +            let _ = Self::A as u16; // Don't lint.
 +        }
 +    }
 +
 +    #[derive(Clone, Copy)]
 +    #[repr(u64)]
 +    enum E7 {
 +        A = u32::MAX as u64,
 +        B,
 +    }
 +    impl E7 {
 +        fn test(self) {
 +            let _ = self as usize;
 +            let _ = Self::A as usize; // Don't lint.
 +            let _ = self as u64; // Don't lint. `2^32-1..=2^32` fits in u64
 +        }
 +    }
 +
 +    #[derive(Clone, Copy)]
 +    #[repr(i128)]
 +    enum E8 {
 +        A = i128::MIN,
 +        B,
 +        C = 0,
 +        D = i128::MAX,
 +    }
 +    impl E8 {
 +        fn test(self) {
 +            let _ = self as i128; // Don't lint. `-(2^127)..=2^127-1` fits it i128
 +        }
 +    }
 +
 +    #[derive(Clone, Copy)]
 +    #[repr(u128)]
 +    enum E9 {
 +        A,
 +        B = u128::MAX,
 +    }
 +    impl E9 {
 +        fn test(self) {
 +            let _ = Self::A as u8; // Don't lint.
 +            let _ = self as u128; // Don't lint. `0..=2^128-1` fits in u128
 +        }
 +    }
 +
 +    #[derive(Clone, Copy)]
 +    #[repr(usize)]
 +    enum E10 {
 +        A,
 +        B = u32::MAX as usize,
 +    }
 +    impl E10 {
 +        fn test(self) {
 +            let _ = self as u16;
 +            let _ = Self::B as u32; // Don't lint.
 +            let _ = self as u64; // Don't lint.
 +        }
 +    }
 +}
++
++fn avoid_subtract_overflow(q: u32) {
++    let c = (q >> 16) as u8;
++    c as usize;
++
++    let c = (q / 1000) as u8;
++    c as usize;
++}
index 7a68c0984f140dda78c8c6d5b927b6867b76beac,0000000000000000000000000000000000000000..0c63b4af30865da871c4d9b241fd9f6d9dc979aa
mode 100644,000000..100644
--- /dev/null
@@@ -1,198 -1,0 +1,210 @@@
- error: aborting due to 31 previous errors
 +error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
 +  --> $DIR/cast.rs:14:5
 +   |
 +LL |     x0 as f32;
 +   |     ^^^^^^^^^
 +   |
 +   = note: `-D clippy::cast-precision-loss` implied by `-D warnings`
 +
 +error: casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
 +  --> $DIR/cast.rs:16:5
 +   |
 +LL |     x1 as f32;
 +   |     ^^^^^^^^^
 +
 +error: casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
 +  --> $DIR/cast.rs:17:5
 +   |
 +LL |     x1 as f64;
 +   |     ^^^^^^^^^
 +
 +error: casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
 +  --> $DIR/cast.rs:19:5
 +   |
 +LL |     x2 as f32;
 +   |     ^^^^^^^^^
 +
 +error: casting `u64` to `f32` causes a loss of precision (`u64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
 +  --> $DIR/cast.rs:21:5
 +   |
 +LL |     x3 as f32;
 +   |     ^^^^^^^^^
 +
 +error: casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
 +  --> $DIR/cast.rs:22:5
 +   |
 +LL |     x3 as f64;
 +   |     ^^^^^^^^^
 +
 +error: casting `f32` to `i32` may truncate the value
 +  --> $DIR/cast.rs:24:5
 +   |
 +LL |     1f32 as i32;
 +   |     ^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
 +
 +error: casting `f32` to `u32` may truncate the value
 +  --> $DIR/cast.rs:25:5
 +   |
 +LL |     1f32 as u32;
 +   |     ^^^^^^^^^^^
 +
 +error: casting `f32` to `u32` may lose the sign of the value
 +  --> $DIR/cast.rs:25:5
 +   |
 +LL |     1f32 as u32;
 +   |     ^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::cast-sign-loss` implied by `-D warnings`
 +
 +error: casting `f64` to `f32` may truncate the value
 +  --> $DIR/cast.rs:26:5
 +   |
 +LL |     1f64 as f32;
 +   |     ^^^^^^^^^^^
 +
 +error: casting `i32` to `i8` may truncate the value
 +  --> $DIR/cast.rs:27:5
 +   |
 +LL |     1i32 as i8;
 +   |     ^^^^^^^^^^
 +
 +error: casting `i32` to `u8` may truncate the value
 +  --> $DIR/cast.rs:28:5
 +   |
 +LL |     1i32 as u8;
 +   |     ^^^^^^^^^^
 +
 +error: casting `f64` to `isize` may truncate the value
 +  --> $DIR/cast.rs:29:5
 +   |
 +LL |     1f64 as isize;
 +   |     ^^^^^^^^^^^^^
 +
 +error: casting `f64` to `usize` may truncate the value
 +  --> $DIR/cast.rs:30:5
 +   |
 +LL |     1f64 as usize;
 +   |     ^^^^^^^^^^^^^
 +
 +error: casting `f64` to `usize` may lose the sign of the value
 +  --> $DIR/cast.rs:30:5
 +   |
 +LL |     1f64 as usize;
 +   |     ^^^^^^^^^^^^^
 +
 +error: casting `u8` to `i8` may wrap around the value
 +  --> $DIR/cast.rs:32:5
 +   |
 +LL |     1u8 as i8;
 +   |     ^^^^^^^^^
 +   |
 +   = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
 +
 +error: casting `u16` to `i16` may wrap around the value
 +  --> $DIR/cast.rs:33:5
 +   |
 +LL |     1u16 as i16;
 +   |     ^^^^^^^^^^^
 +
 +error: casting `u32` to `i32` may wrap around the value
 +  --> $DIR/cast.rs:34:5
 +   |
 +LL |     1u32 as i32;
 +   |     ^^^^^^^^^^^
 +
 +error: casting `u64` to `i64` may wrap around the value
 +  --> $DIR/cast.rs:35:5
 +   |
 +LL |     1u64 as i64;
 +   |     ^^^^^^^^^^^
 +
 +error: casting `usize` to `isize` may wrap around the value
 +  --> $DIR/cast.rs:36:5
 +   |
 +LL |     1usize as isize;
 +   |     ^^^^^^^^^^^^^^^
 +
 +error: casting `i32` to `u32` may lose the sign of the value
 +  --> $DIR/cast.rs:39:5
 +   |
 +LL |     -1i32 as u32;
 +   |     ^^^^^^^^^^^^
 +
 +error: casting `isize` to `usize` may lose the sign of the value
 +  --> $DIR/cast.rs:41:5
 +   |
 +LL |     -1isize as usize;
 +   |     ^^^^^^^^^^^^^^^^
 +
 +error: casting `i64` to `i8` may truncate the value
 +  --> $DIR/cast.rs:108:5
 +   |
 +LL |     (-99999999999i64).min(1) as i8; // should be linted because signed
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: casting `u64` to `u8` may truncate the value
 +  --> $DIR/cast.rs:120:5
 +   |
 +LL |     999999u64.clamp(0, 256) as u8; // should still be linted
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: casting `main::E2` to `u8` may truncate the value
 +  --> $DIR/cast.rs:141:21
 +   |
 +LL |             let _ = self as u8;
 +   |                     ^^^^^^^^^^
 +
 +error: casting `main::E2::B` to `u8` will truncate the value
 +  --> $DIR/cast.rs:142:21
 +   |
 +LL |             let _ = Self::B as u8;
 +   |                     ^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::cast-enum-truncation` implied by `-D warnings`
 +
 +error: casting `main::E5` to `i8` may truncate the value
 +  --> $DIR/cast.rs:178:21
 +   |
 +LL |             let _ = self as i8;
 +   |                     ^^^^^^^^^^
 +
 +error: casting `main::E5::A` to `i8` will truncate the value
 +  --> $DIR/cast.rs:179:21
 +   |
 +LL |             let _ = Self::A as i8;
 +   |                     ^^^^^^^^^^^^^
 +
 +error: casting `main::E6` to `i16` may truncate the value
 +  --> $DIR/cast.rs:193:21
 +   |
 +LL |             let _ = self as i16;
 +   |                     ^^^^^^^^^^^
 +
 +error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers
 +  --> $DIR/cast.rs:208:21
 +   |
 +LL |             let _ = self as usize;
 +   |                     ^^^^^^^^^^^^^
 +
 +error: casting `main::E10` to `u16` may truncate the value
 +  --> $DIR/cast.rs:249:21
 +   |
 +LL |             let _ = self as u16;
 +   |                     ^^^^^^^^^^^
 +
++error: casting `u32` to `u8` may truncate the value
++  --> $DIR/cast.rs:257:13
++   |
++LL |     let c = (q >> 16) as u8;
++   |             ^^^^^^^^^^^^^^^
++
++error: casting `u32` to `u8` may truncate the value
++  --> $DIR/cast.rs:260:13
++   |
++LL |     let c = (q / 1000) as u8;
++   |             ^^^^^^^^^^^^^^^^
++
++error: aborting due to 33 previous errors
 +
index e4e7290a30e9e711c911c34d96a1239af8e0d19a,0000000000000000000000000000000000000000..95bb883df1bf10877db0fff7a242606284dc55a8
mode 100644,000000..100644
--- /dev/null
@@@ -1,51 -1,0 +1,51 @@@
-         let _ = (ptr as *mut u16).write_unaligned(0);
-         let _ = core::ptr::write_unaligned(ptr as *mut u16, 0);
-         let _ = core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0);
 +//! Test casts for alignment issues
 +
 +#![feature(rustc_private)]
 +#![feature(core_intrinsics)]
 +extern crate libc;
 +
 +#[warn(clippy::cast_ptr_alignment)]
 +#[allow(
 +    clippy::no_effect,
 +    clippy::unnecessary_operation,
 +    clippy::cast_lossless,
 +    clippy::borrow_as_ptr
 +)]
 +
 +fn main() {
 +    /* These should be warned against */
 +
 +    // cast to more-strictly-aligned type
 +    (&1u8 as *const u8) as *const u16;
 +    (&mut 1u8 as *mut u8) as *mut u16;
 +
 +    // cast to more-strictly-aligned type, but with the `pointer::cast` function.
 +    (&1u8 as *const u8).cast::<u16>();
 +    (&mut 1u8 as *mut u8).cast::<u16>();
 +
 +    /* These should be ok */
 +
 +    // not a pointer type
 +    1u8 as u16;
 +    // cast to less-strictly-aligned type
 +    (&1u16 as *const u16) as *const u8;
 +    (&mut 1u16 as *mut u16) as *mut u8;
 +    // For c_void, we should trust the user. See #2677
 +    (&1u32 as *const u32 as *const std::os::raw::c_void) as *const u32;
 +    (&1u32 as *const u32 as *const libc::c_void) as *const u32;
 +    // For ZST, we should trust the user. See #4256
 +    (&1u32 as *const u32 as *const ()) as *const u32;
 +
 +    // Issue #2881
 +    let mut data = [0u8, 0u8];
 +    unsafe {
 +        let ptr = &data as *const [u8; 2] as *const u8;
 +        let _ = (ptr as *const u16).read_unaligned();
 +        let _ = core::ptr::read_unaligned(ptr as *const u16);
 +        let _ = core::intrinsics::unaligned_volatile_load(ptr as *const u16);
 +        let ptr = &mut data as *mut [u8; 2] as *mut u8;
++        (ptr as *mut u16).write_unaligned(0);
++        core::ptr::write_unaligned(ptr as *mut u16, 0);
++        core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0);
 +    }
 +}
index cfe1cca2eba9490ffb277a4d18dc21a71910ffeb,0000000000000000000000000000000000000000..24d7eb28a197aa03b75fd663cbeda11339ee8c4a
mode 100644,000000..100644
--- /dev/null
@@@ -1,39 -1,0 +1,82 @@@
++#![allow(clippy::let_unit_value)]
++
 +fn main() {
 +    let x: [i32; 3] = [1_i32, 2, 3];
 +    let r_x = &x;
 +    // Check casting through multiple bindings
 +    // Because it's separate, it does not check the cast back to something of the same size
 +    let a = r_x as *const [i32];
 +    let b = a as *const [u8];
 +    let c = b as *const [u32];
 +
 +    // loses data
 +    let loss = r_x as *const [i32] as *const [u8];
 +
 +    // Cast back to same size but different type loses no data, just type conversion
 +    // This is weird code but there's no reason for this lint specifically to fire *twice* on it
 +    let restore = r_x as *const [i32] as *const [u8] as *const [u32];
 +
 +    // Check casting through blocks is detected
 +    let loss_block_1 = { r_x as *const [i32] } as *const [u8];
 +    let loss_block_2 = {
 +        let _ = ();
 +        r_x as *const [i32]
 +    } as *const [u8];
 +
 +    // Check that resores of the same size are detected through blocks
 +    let restore_block_1 = { r_x as *const [i32] } as *const [u8] as *const [u32];
 +    let restore_block_2 = { ({ r_x as *const [i32] }) as *const [u8] } as *const [u32];
 +    let restore_block_3 = {
 +        let _ = ();
 +        ({
 +            let _ = ();
 +            r_x as *const [i32]
 +        }) as *const [u8]
 +    } as *const [u32];
 +
 +    // Check that the result of a long chain of casts is detected
 +    let long_chain_loss = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8];
 +    let long_chain_restore =
 +        r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8] as *const [u32];
 +}
++
++// foo and foo2 should not fire, they're the same size
++fn foo(x: *mut [u8]) -> *mut [u8] {
++    x as *mut [u8]
++}
++
++fn foo2(x: *mut [u8]) -> *mut [u8] {
++    x as *mut _
++}
++
++// Test that casts as part of function returns work
++fn bar(x: *mut [u16]) -> *mut [u8] {
++    x as *mut [u8]
++}
++
++fn uwu(x: *mut [u16]) -> *mut [u8] {
++    x as *mut _
++}
++
++fn bar2(x: *mut [u16]) -> *mut [u8] {
++    x as _
++}
++
++// constify
++fn bar3(x: *mut [u16]) -> *const [u8] {
++    x as _
++}
++
++// unconstify
++fn bar4(x: *const [u16]) -> *mut [u8] {
++    x as _
++}
++
++// function returns plus blocks
++fn blocks(x: *mut [u16]) -> *mut [u8] {
++    ({ x }) as _
++}
++
++fn more_blocks(x: *mut [u16]) -> *mut [u8] {
++    { ({ x }) as _ }
++}
index a37cec7cb3be0935e00772d85edc5d339e45005c,0000000000000000000000000000000000000000..40721dcd05d5dbc909d140a82d70de3bfb28ddc7
mode 100644,000000..100644
--- /dev/null
@@@ -1,52 -1,0 +1,121 @@@
-   --> $DIR/cast_slice_different_sizes.rs:7:13
 +error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
-   --> $DIR/cast_slice_different_sizes.rs:8:13
++  --> $DIR/cast_slice_different_sizes.rs:9:13
 +   |
 +LL |     let b = a as *const [u8];
 +   |             ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(a as *const u8, ..)`
 +   |
 +   = note: `#[deny(clippy::cast_slice_different_sizes)]` on by default
 +
 +error: casting between raw pointers to `[u8]` (element size 1) and `[u32]` (element size 4) does not adjust the count
-   --> $DIR/cast_slice_different_sizes.rs:11:16
++  --> $DIR/cast_slice_different_sizes.rs:10:13
 +   |
 +LL |     let c = b as *const [u32];
 +   |             ^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(b as *const u32, ..)`
 +
 +error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
-   --> $DIR/cast_slice_different_sizes.rs:18:24
++  --> $DIR/cast_slice_different_sizes.rs:13:16
 +   |
 +LL |     let loss = r_x as *const [i32] as *const [u8];
 +   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)`
 +
 +error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
-   --> $DIR/cast_slice_different_sizes.rs:19:24
++  --> $DIR/cast_slice_different_sizes.rs:20:24
 +   |
 +LL |     let loss_block_1 = { r_x as *const [i32] } as *const [u8];
 +   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts({ r_x as *const [i32] } as *const u8, ..)`
 +
 +error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
-   --> $DIR/cast_slice_different_sizes.rs:36:27
++  --> $DIR/cast_slice_different_sizes.rs:21:24
 +   |
 +LL |       let loss_block_2 = {
 +   |  ________________________^
 +LL | |         let _ = ();
 +LL | |         r_x as *const [i32]
 +LL | |     } as *const [u8];
 +   | |____________________^
 +   |
 +help: replace with `ptr::slice_from_raw_parts`
 +   |
 +LL ~     let loss_block_2 = core::ptr::slice_from_raw_parts({
 +LL +         let _ = ();
 +LL +         r_x as *const [i32]
 +LL ~     } as *const u8, ..);
 +   |
 +
 +error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
-    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const u8, ..)`
++  --> $DIR/cast_slice_different_sizes.rs:38:27
 +   |
 +LL |     let long_chain_loss = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8];
- error: aborting due to 6 previous errors
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)`
 +
++error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
++  --> $DIR/cast_slice_different_sizes.rs:53:36
++   |
++LL |   fn bar(x: *mut [u16]) -> *mut [u8] {
++   |  ____________________________________^
++LL | |     x as *mut [u8]
++LL | | }
++   | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)`
++
++error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
++  --> $DIR/cast_slice_different_sizes.rs:57:36
++   |
++LL |   fn uwu(x: *mut [u16]) -> *mut [u8] {
++   |  ____________________________________^
++LL | |     x as *mut _
++LL | | }
++   | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)`
++
++error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
++  --> $DIR/cast_slice_different_sizes.rs:61:37
++   |
++LL |   fn bar2(x: *mut [u16]) -> *mut [u8] {
++   |  _____________________________________^
++LL | |     x as _
++LL | | }
++   | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)`
++
++error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
++  --> $DIR/cast_slice_different_sizes.rs:66:39
++   |
++LL |   fn bar3(x: *mut [u16]) -> *const [u8] {
++   |  _______________________________________^
++LL | |     x as _
++LL | | }
++   | |_^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(x as *const u8, ..)`
++
++error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
++  --> $DIR/cast_slice_different_sizes.rs:71:39
++   |
++LL |   fn bar4(x: *const [u16]) -> *mut [u8] {
++   |  _______________________________________^
++LL | |     x as _
++LL | | }
++   | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)`
++
++error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
++  --> $DIR/cast_slice_different_sizes.rs:76:39
++   |
++LL |   fn blocks(x: *mut [u16]) -> *mut [u8] {
++   |  _______________________________________^
++LL | |     ({ x }) as _
++LL | | }
++   | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)`
++
++error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
++  --> $DIR/cast_slice_different_sizes.rs:80:44
++   |
++LL |   fn more_blocks(x: *mut [u16]) -> *mut [u8] {
++   |  ____________________________________________^
++LL | |     { ({ x }) as _ }
++LL | | }
++   | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)`
++
++error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
++  --> $DIR/cast_slice_different_sizes.rs:81:5
++   |
++LL |     { ({ x }) as _ }
++   |     ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)`
++
++error: aborting due to 14 previous errors
 +
index bb6c4c0703d5168579216e7b37fd571e94570d52,0000000000000000000000000000000000000000..d6a5a78506791a0e782c4c4911091d4f96ef7104
mode 100644,000000..100644
--- /dev/null
@@@ -1,77 -1,0 +1,84 @@@
 +// run-rustfix
 +#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)]
 +
 +#[rustfmt::skip]
 +#[warn(clippy::collapsible_if)]
 +#[warn(clippy::collapsible_else_if)]
 +
 +fn main() {
 +    let x = "hello";
 +    let y = "world";
 +    // Collapse `else { if .. }` to `else if ..`
 +    if x == "hello" {
 +        print!("Hello ");
 +    } else if y == "world" {
 +        println!("world!")
 +    }
 +
 +    if x == "hello" {
 +        print!("Hello ");
 +    } else if let Some(42) = Some(42) {
 +        println!("world!")
 +    }
 +
 +    if x == "hello" {
 +        print!("Hello ");
 +    } else if y == "world" {
 +        println!("world")
 +    }
 +    else {
 +        println!("!")
 +    }
 +
 +    if x == "hello" {
 +        print!("Hello ");
 +    } else if let Some(42) = Some(42) {
 +        println!("world")
 +    }
 +    else {
 +        println!("!")
 +    }
 +
 +    if let Some(42) = Some(42) {
 +        print!("Hello ");
 +    } else if let Some(42) = Some(42) {
 +        println!("world")
 +    }
 +    else {
 +        println!("!")
 +    }
 +
 +    if let Some(42) = Some(42) {
 +        print!("Hello ");
 +    } else if x == "hello" {
 +        println!("world")
 +    }
 +    else {
 +        println!("!")
 +    }
 +
 +    if let Some(42) = Some(42) {
 +        print!("Hello ");
 +    } else if let Some(42) = Some(42) {
 +        println!("world")
 +    }
 +    else {
 +        println!("!")
 +    }
 +
 +    if x == "hello" {
 +        print!("Hello ");
 +    } else {
 +        #[cfg(not(roflol))]
 +        if y == "world" {
 +            println!("world!")
 +        }
 +    }
 +}
++
++#[rustfmt::skip]
++#[allow(dead_code)]
++fn issue_7318() {
++    if true { println!("I've been resolved!")
++    }else if false {}
++}
index 6d4f688db8c0a7a80114ea1e2842f914fc94881c,0000000000000000000000000000000000000000..4399fc8b2bd1d4b760828576d4d09055a6aaeabd
mode 100644,000000..100644
--- /dev/null
@@@ -1,91 -1,0 +1,100 @@@
 +// run-rustfix
 +#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)]
 +
 +#[rustfmt::skip]
 +#[warn(clippy::collapsible_if)]
 +#[warn(clippy::collapsible_else_if)]
 +
 +fn main() {
 +    let x = "hello";
 +    let y = "world";
 +    // Collapse `else { if .. }` to `else if ..`
 +    if x == "hello" {
 +        print!("Hello ");
 +    } else {
 +        if y == "world" {
 +            println!("world!")
 +        }
 +    }
 +
 +    if x == "hello" {
 +        print!("Hello ");
 +    } else {
 +        if let Some(42) = Some(42) {
 +            println!("world!")
 +        }
 +    }
 +
 +    if x == "hello" {
 +        print!("Hello ");
 +    } else {
 +        if y == "world" {
 +            println!("world")
 +        }
 +        else {
 +            println!("!")
 +        }
 +    }
 +
 +    if x == "hello" {
 +        print!("Hello ");
 +    } else {
 +        if let Some(42) = Some(42) {
 +            println!("world")
 +        }
 +        else {
 +            println!("!")
 +        }
 +    }
 +
 +    if let Some(42) = Some(42) {
 +        print!("Hello ");
 +    } else {
 +        if let Some(42) = Some(42) {
 +            println!("world")
 +        }
 +        else {
 +            println!("!")
 +        }
 +    }
 +
 +    if let Some(42) = Some(42) {
 +        print!("Hello ");
 +    } else {
 +        if x == "hello" {
 +            println!("world")
 +        }
 +        else {
 +            println!("!")
 +        }
 +    }
 +
 +    if let Some(42) = Some(42) {
 +        print!("Hello ");
 +    } else {
 +        if let Some(42) = Some(42) {
 +            println!("world")
 +        }
 +        else {
 +            println!("!")
 +        }
 +    }
 +
 +    if x == "hello" {
 +        print!("Hello ");
 +    } else {
 +        #[cfg(not(roflol))]
 +        if y == "world" {
 +            println!("world!")
 +        }
 +    }
 +}
++
++#[rustfmt::skip]
++#[allow(dead_code)]
++fn issue_7318() {
++    if true { println!("I've been resolved!")
++    }else{
++        if false {}
++    }
++}
index 6970f66097908ee23aa202b1f86370f6cd742646,0000000000000000000000000000000000000000..45b2094c9948b6961e4da37a27bc5ce631ccb56c
mode 100644,000000..100644
--- /dev/null
@@@ -1,154 -1,0 +1,163 @@@
- error: aborting due to 7 previous errors
 +error: this `else { if .. }` block can be collapsed
 +  --> $DIR/collapsible_else_if.rs:14:12
 +   |
 +LL |       } else {
 +   |  ____________^
 +LL | |         if y == "world" {
 +LL | |             println!("world!")
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +   = note: `-D clippy::collapsible-else-if` implied by `-D warnings`
 +help: collapse nested if block
 +   |
 +LL ~     } else if y == "world" {
 +LL +         println!("world!")
 +LL +     }
 +   |
 +
 +error: this `else { if .. }` block can be collapsed
 +  --> $DIR/collapsible_else_if.rs:22:12
 +   |
 +LL |       } else {
 +   |  ____________^
 +LL | |         if let Some(42) = Some(42) {
 +LL | |             println!("world!")
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +help: collapse nested if block
 +   |
 +LL ~     } else if let Some(42) = Some(42) {
 +LL +         println!("world!")
 +LL +     }
 +   |
 +
 +error: this `else { if .. }` block can be collapsed
 +  --> $DIR/collapsible_else_if.rs:30:12
 +   |
 +LL |       } else {
 +   |  ____________^
 +LL | |         if y == "world" {
 +LL | |             println!("world")
 +LL | |         }
 +...  |
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +help: collapse nested if block
 +   |
 +LL ~     } else if y == "world" {
 +LL +         println!("world")
 +LL +     }
 +LL +     else {
 +LL +         println!("!")
 +LL +     }
 +   |
 +
 +error: this `else { if .. }` block can be collapsed
 +  --> $DIR/collapsible_else_if.rs:41:12
 +   |
 +LL |       } else {
 +   |  ____________^
 +LL | |         if let Some(42) = Some(42) {
 +LL | |             println!("world")
 +LL | |         }
 +...  |
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +help: collapse nested if block
 +   |
 +LL ~     } else if let Some(42) = Some(42) {
 +LL +         println!("world")
 +LL +     }
 +LL +     else {
 +LL +         println!("!")
 +LL +     }
 +   |
 +
 +error: this `else { if .. }` block can be collapsed
 +  --> $DIR/collapsible_else_if.rs:52:12
 +   |
 +LL |       } else {
 +   |  ____________^
 +LL | |         if let Some(42) = Some(42) {
 +LL | |             println!("world")
 +LL | |         }
 +...  |
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +help: collapse nested if block
 +   |
 +LL ~     } else if let Some(42) = Some(42) {
 +LL +         println!("world")
 +LL +     }
 +LL +     else {
 +LL +         println!("!")
 +LL +     }
 +   |
 +
 +error: this `else { if .. }` block can be collapsed
 +  --> $DIR/collapsible_else_if.rs:63:12
 +   |
 +LL |       } else {
 +   |  ____________^
 +LL | |         if x == "hello" {
 +LL | |             println!("world")
 +LL | |         }
 +...  |
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +help: collapse nested if block
 +   |
 +LL ~     } else if x == "hello" {
 +LL +         println!("world")
 +LL +     }
 +LL +     else {
 +LL +         println!("!")
 +LL +     }
 +   |
 +
 +error: this `else { if .. }` block can be collapsed
 +  --> $DIR/collapsible_else_if.rs:74:12
 +   |
 +LL |       } else {
 +   |  ____________^
 +LL | |         if let Some(42) = Some(42) {
 +LL | |             println!("world")
 +LL | |         }
 +...  |
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +help: collapse nested if block
 +   |
 +LL ~     } else if let Some(42) = Some(42) {
 +LL +         println!("world")
 +LL +     }
 +LL +     else {
 +LL +         println!("!")
 +LL +     }
 +   |
 +
++error: this `else { if .. }` block can be collapsed
++  --> $DIR/collapsible_else_if.rs:97:10
++   |
++LL |       }else{
++   |  __________^
++LL | |         if false {}
++LL | |     }
++   | |_____^ help: collapse nested if block: `if false {}`
++
++error: aborting due to 8 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..95b6315132526460293acf9da62bbcf617f68ac1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++pub fn foo(x: &u32) -> u32 {
++    /* Safety:
++     * This is totally ok.
++     */
++    unsafe { *(x as *const u32) }
++}
index 6b1ceb5056933df6ee6bc782f118fcad3c1a7e72,0000000000000000000000000000000000000000..c6298139601625392c47a5bd3614d3acd5ef1038
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,16 @@@
- #[allow(dead_code)]
++#![allow(dead_code, clippy::extra_unused_lifetimes)]
 +
 +/// Test for https://github.com/rust-lang/rust-clippy/issues/2865
 +
 +struct Ice {
 +    size: String,
 +}
 +
 +impl<'a> From<String> for Ice {
 +    fn from(_: String) -> Self {
 +        let text = || "iceberg".to_string();
 +        Self { size: text() }
 +    }
 +}
 +
 +fn main() {}
index fef4d7db84ddf0d23a5671dab0d0791b6bf90db5,0000000000000000000000000000000000000000..268ba86fc7aa80756b1aa151b1aff72949a4d75c
mode 100644,000000..100644
--- /dev/null
@@@ -1,15 -1,0 +1,15 @@@
- /// Test for https://github.com/rust-lang/rust-clippy/issues/2865
++/// Test for https://github.com/rust-lang/rust-clippy/issues/3151
 +
 +#[derive(Clone)]
 +pub struct HashMap<V, S> {
 +    hash_builder: S,
 +    table: RawTable<V>,
 +}
 +
 +#[derive(Clone)]
 +pub struct RawTable<V> {
 +    size: usize,
 +    val: V,
 +}
 +
 +fn main() {}
index 5caf29c619735384ea91d3b304dcd490a12c57eb,0000000000000000000000000000000000000000..ce46bc1acc1b7fbd718f5ffe8de0f19c38e0fe1e
mode 100644,000000..100644
--- /dev/null
@@@ -1,13 -1,0 +1,14 @@@
 +#![warn(clippy::repeat_once)]
++#![allow(clippy::let_unit_value)]
 +
 +trait Repeat {
 +    fn repeat(&self) {}
 +}
 +
 +impl Repeat for usize {
 +    fn repeat(&self) {}
 +}
 +
 +fn main() {
 +    let _ = 42.repeat();
 +}
index 04ea4456656525c570cd1a39eb4348119ab291f4,0000000000000000000000000000000000000000..8ed8f3b3a0642f2bea6f9da9a5fd19c08ce850e0
mode 100644,000000..100644
--- /dev/null
@@@ -1,18 -1,0 +1,10 @@@
- error: manual implementation of `split_once`
-   --> $DIR/ice-8250.rs:2:13
-    |
- LL |     let _ = s[1..].splitn(2, '.').next()?;
-    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s[1..].split_once('.').map_or(s[1..], |x| x.0)`
-    |
-    = note: `-D clippy::manual-split-once` implied by `-D warnings`
 +error: unnecessary use of `splitn`
 +  --> $DIR/ice-8250.rs:2:13
 +   |
 +LL |     let _ = s[1..].splitn(2, '.').next()?;
 +   |             ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s[1..].split('.')`
 +   |
 +   = note: `-D clippy::needless-splitn` implied by `-D warnings`
 +
- error: aborting due to 2 previous errors
++error: aborting due to previous error
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ee14f011f631bb534baece790c3c07ef901db1cb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++// aux-build: ice-8681-aux.rs
++
++#![warn(clippy::undocumented_unsafe_blocks)]
++
++#[path = "auxiliary/ice-8681-aux.rs"]
++mod ice_8681_aux;
++
++fn main() {
++    let _ = ice_8681_aux::foo(&0u32);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4b3fb764010821e375a167aca49b93620ef0fa7a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++macro_rules! foo {
++    () => {
++        "bar.rs"
++    };
++}
++
++#[path = foo!()] //~ ERROR malformed `path` attribute
++mod abc {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..78c567b8e772e989945c4098d8c9efbb09d0fd6b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++error: malformed `path` attribute input
++  --> $DIR/ice-96721.rs:7:1
++   |
++LL | #[path = foo!()] //~ ERROR malformed `path` attribute
++   | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[path = "file"]`
++
++error: aborting due to previous error
++
index e0b4a2f6942392c575e3f491914dfbf11b7cf6cd,0000000000000000000000000000000000000000..a28bff76755b62f982bf364d3e8f3fbfaeb01db7
mode 100644,000000..100644
--- /dev/null
@@@ -1,174 -1,0 +1,177 @@@
- #![allow(unused)]
- #![allow(clippy::never_loop)]
- #![allow(clippy::no_effect)]
- #![allow(clippy::unnecessary_operation)]
- #![allow(clippy::branches_sharing_code)]
- #![allow(clippy::match_single_binding)]
 +// run-rustfix
 +// aux-build:macro_rules.rs
 +
 +#![warn(clippy::default_numeric_fallback)]
++#![allow(
++    unused,
++    clippy::never_loop,
++    clippy::no_effect,
++    clippy::unnecessary_operation,
++    clippy::branches_sharing_code,
++    clippy::match_single_binding,
++    clippy::let_unit_value
++)]
 +
 +#[macro_use]
 +extern crate macro_rules;
 +
 +mod basic_expr {
 +    fn test() {
 +        // Should lint unsuffixed literals typed `f64`.
 +        let x = 0.12_f64;
 +        let x = [1.0_f64, 2.0_f64, 3.0_f64];
 +        let x = if true { (1.0_f64, 2.0_f64) } else { (3.0_f64, 4.0_f64) };
 +        let x = match 1.0_f64 {
 +            _ => 1.0_f64,
 +        };
 +
 +        // Should NOT lint suffixed literals.
 +        let x = 0.12_f64;
 +
 +        // Should NOT lint literals in init expr if `Local` has a type annotation.
 +        let x: f64 = 0.1;
 +        let x: [f64; 3] = [1., 2., 3.];
 +        let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) };
 +        let x: _ = 1.;
 +    }
 +}
 +
 +mod nested_local {
 +    fn test() {
 +        let x: _ = {
 +            // Should lint this because this literal is not bound to any types.
 +            let y = 1.0_f64;
 +
 +            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
 +            1.
 +        };
 +
 +        let x: _ = if true {
 +            // Should lint this because this literal is not bound to any types.
 +            let y = 1.0_f64;
 +
 +            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
 +            1.
 +        } else {
 +            // Should lint this because this literal is not bound to any types.
 +            let y = 1.0_f64;
 +
 +            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
 +            2.
 +        };
 +    }
 +}
 +
 +mod function_def {
 +    fn ret_f64() -> f64 {
 +        // Even though the output type is specified,
 +        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
 +        1.0_f64
 +    }
 +
 +    fn test() {
 +        // Should lint this because return type is inferred to `f64` and NOT bound to a concrete
 +        // type.
 +        let f = || -> _ { 1.0_f64 };
 +
 +        // Even though the output type is specified,
 +        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
 +        let f = || -> f64 { 1.0_f64 };
 +    }
 +}
 +
 +mod function_calls {
 +    fn concrete_arg(f: f64) {}
 +
 +    fn generic_arg<T>(t: T) {}
 +
 +    fn test() {
 +        // Should NOT lint this because the argument type is bound to a concrete type.
 +        concrete_arg(1.);
 +
 +        // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
 +        generic_arg(1.0_f64);
 +
 +        // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
 +        let x: _ = generic_arg(1.0_f64);
 +    }
 +}
 +
 +mod struct_ctor {
 +    struct ConcreteStruct {
 +        x: f64,
 +    }
 +
 +    struct GenericStruct<T> {
 +        x: T,
 +    }
 +
 +    fn test() {
 +        // Should NOT lint this because the field type is bound to a concrete type.
 +        ConcreteStruct { x: 1. };
 +
 +        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
 +        GenericStruct { x: 1.0_f64 };
 +
 +        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
 +        let _ = GenericStruct { x: 1.0_f64 };
 +    }
 +}
 +
 +mod enum_ctor {
 +    enum ConcreteEnum {
 +        X(f64),
 +    }
 +
 +    enum GenericEnum<T> {
 +        X(T),
 +    }
 +
 +    fn test() {
 +        // Should NOT lint this because the field type is bound to a concrete type.
 +        ConcreteEnum::X(1.);
 +
 +        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
 +        GenericEnum::X(1.0_f64);
 +    }
 +}
 +
 +mod method_calls {
 +    struct StructForMethodCallTest;
 +
 +    impl StructForMethodCallTest {
 +        fn concrete_arg(&self, f: f64) {}
 +
 +        fn generic_arg<T>(&self, t: T) {}
 +    }
 +
 +    fn test() {
 +        let s = StructForMethodCallTest {};
 +
 +        // Should NOT lint this because the argument type is bound to a concrete type.
 +        s.concrete_arg(1.);
 +
 +        // Should lint this because the argument type is bound to a concrete type.
 +        s.generic_arg(1.0_f64);
 +    }
 +}
 +
 +mod in_macro {
 +    macro_rules! internal_macro {
 +        () => {
 +            let x = 22.0_f64;
 +        };
 +    }
 +
 +    // Should lint in internal macro.
 +    fn internal() {
 +        internal_macro!();
 +    }
 +
 +    // Should NOT lint in external macro.
 +    fn external() {
 +        default_numeric_fallback!();
 +    }
 +}
 +
 +fn main() {}
index 50bbb6eec6c700fdbdb7a54051e48c21715b0dbd,0000000000000000000000000000000000000000..b48435cc7b282a049e8c4425381898e7b31b2702
mode 100644,000000..100644
--- /dev/null
@@@ -1,174 -1,0 +1,177 @@@
- #![allow(unused)]
- #![allow(clippy::never_loop)]
- #![allow(clippy::no_effect)]
- #![allow(clippy::unnecessary_operation)]
- #![allow(clippy::branches_sharing_code)]
- #![allow(clippy::match_single_binding)]
 +// run-rustfix
 +// aux-build:macro_rules.rs
 +
 +#![warn(clippy::default_numeric_fallback)]
++#![allow(
++    unused,
++    clippy::never_loop,
++    clippy::no_effect,
++    clippy::unnecessary_operation,
++    clippy::branches_sharing_code,
++    clippy::match_single_binding,
++    clippy::let_unit_value
++)]
 +
 +#[macro_use]
 +extern crate macro_rules;
 +
 +mod basic_expr {
 +    fn test() {
 +        // Should lint unsuffixed literals typed `f64`.
 +        let x = 0.12;
 +        let x = [1., 2., 3.];
 +        let x = if true { (1., 2.) } else { (3., 4.) };
 +        let x = match 1. {
 +            _ => 1.,
 +        };
 +
 +        // Should NOT lint suffixed literals.
 +        let x = 0.12_f64;
 +
 +        // Should NOT lint literals in init expr if `Local` has a type annotation.
 +        let x: f64 = 0.1;
 +        let x: [f64; 3] = [1., 2., 3.];
 +        let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) };
 +        let x: _ = 1.;
 +    }
 +}
 +
 +mod nested_local {
 +    fn test() {
 +        let x: _ = {
 +            // Should lint this because this literal is not bound to any types.
 +            let y = 1.;
 +
 +            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
 +            1.
 +        };
 +
 +        let x: _ = if true {
 +            // Should lint this because this literal is not bound to any types.
 +            let y = 1.;
 +
 +            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
 +            1.
 +        } else {
 +            // Should lint this because this literal is not bound to any types.
 +            let y = 1.;
 +
 +            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
 +            2.
 +        };
 +    }
 +}
 +
 +mod function_def {
 +    fn ret_f64() -> f64 {
 +        // Even though the output type is specified,
 +        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
 +        1.
 +    }
 +
 +    fn test() {
 +        // Should lint this because return type is inferred to `f64` and NOT bound to a concrete
 +        // type.
 +        let f = || -> _ { 1. };
 +
 +        // Even though the output type is specified,
 +        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
 +        let f = || -> f64 { 1. };
 +    }
 +}
 +
 +mod function_calls {
 +    fn concrete_arg(f: f64) {}
 +
 +    fn generic_arg<T>(t: T) {}
 +
 +    fn test() {
 +        // Should NOT lint this because the argument type is bound to a concrete type.
 +        concrete_arg(1.);
 +
 +        // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
 +        generic_arg(1.);
 +
 +        // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
 +        let x: _ = generic_arg(1.);
 +    }
 +}
 +
 +mod struct_ctor {
 +    struct ConcreteStruct {
 +        x: f64,
 +    }
 +
 +    struct GenericStruct<T> {
 +        x: T,
 +    }
 +
 +    fn test() {
 +        // Should NOT lint this because the field type is bound to a concrete type.
 +        ConcreteStruct { x: 1. };
 +
 +        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
 +        GenericStruct { x: 1. };
 +
 +        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
 +        let _ = GenericStruct { x: 1. };
 +    }
 +}
 +
 +mod enum_ctor {
 +    enum ConcreteEnum {
 +        X(f64),
 +    }
 +
 +    enum GenericEnum<T> {
 +        X(T),
 +    }
 +
 +    fn test() {
 +        // Should NOT lint this because the field type is bound to a concrete type.
 +        ConcreteEnum::X(1.);
 +
 +        // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
 +        GenericEnum::X(1.);
 +    }
 +}
 +
 +mod method_calls {
 +    struct StructForMethodCallTest;
 +
 +    impl StructForMethodCallTest {
 +        fn concrete_arg(&self, f: f64) {}
 +
 +        fn generic_arg<T>(&self, t: T) {}
 +    }
 +
 +    fn test() {
 +        let s = StructForMethodCallTest {};
 +
 +        // Should NOT lint this because the argument type is bound to a concrete type.
 +        s.concrete_arg(1.);
 +
 +        // Should lint this because the argument type is bound to a concrete type.
 +        s.generic_arg(1.);
 +    }
 +}
 +
 +mod in_macro {
 +    macro_rules! internal_macro {
 +        () => {
 +            let x = 22.;
 +        };
 +    }
 +
 +    // Should lint in internal macro.
 +    fn internal() {
 +        internal_macro!();
 +    }
 +
 +    // Should NOT lint in external macro.
 +    fn external() {
 +        default_numeric_fallback!();
 +    }
 +}
 +
 +fn main() {}
index f8a2407b6933d8d6be71bf32087083eeee09b79b,0000000000000000000000000000000000000000..f8b6c7746edbb728f1ee699b2b345bb1963ecd66
mode 100644,000000..100644
--- /dev/null
@@@ -1,147 -1,0 +1,147 @@@
-   --> $DIR/default_numeric_fallback_f64.rs:18:17
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:19:18
++  --> $DIR/default_numeric_fallback_f64.rs:21:17
 +   |
 +LL |         let x = 0.12;
 +   |                 ^^^^ help: consider adding suffix: `0.12_f64`
 +   |
 +   = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:19:22
++  --> $DIR/default_numeric_fallback_f64.rs:22:18
 +   |
 +LL |         let x = [1., 2., 3.];
 +   |                  ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:19:26
++  --> $DIR/default_numeric_fallback_f64.rs:22:22
 +   |
 +LL |         let x = [1., 2., 3.];
 +   |                      ^^ help: consider adding suffix: `2.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:20:28
++  --> $DIR/default_numeric_fallback_f64.rs:22:26
 +   |
 +LL |         let x = [1., 2., 3.];
 +   |                          ^^ help: consider adding suffix: `3.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:20:32
++  --> $DIR/default_numeric_fallback_f64.rs:23:28
 +   |
 +LL |         let x = if true { (1., 2.) } else { (3., 4.) };
 +   |                            ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:20:46
++  --> $DIR/default_numeric_fallback_f64.rs:23:32
 +   |
 +LL |         let x = if true { (1., 2.) } else { (3., 4.) };
 +   |                                ^^ help: consider adding suffix: `2.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:20:50
++  --> $DIR/default_numeric_fallback_f64.rs:23:46
 +   |
 +LL |         let x = if true { (1., 2.) } else { (3., 4.) };
 +   |                                              ^^ help: consider adding suffix: `3.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:21:23
++  --> $DIR/default_numeric_fallback_f64.rs:23:50
 +   |
 +LL |         let x = if true { (1., 2.) } else { (3., 4.) };
 +   |                                                  ^^ help: consider adding suffix: `4.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:22:18
++  --> $DIR/default_numeric_fallback_f64.rs:24:23
 +   |
 +LL |         let x = match 1. {
 +   |                       ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:40:21
++  --> $DIR/default_numeric_fallback_f64.rs:25:18
 +   |
 +LL |             _ => 1.,
 +   |                  ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:48:21
++  --> $DIR/default_numeric_fallback_f64.rs:43:21
 +   |
 +LL |             let y = 1.;
 +   |                     ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:54:21
++  --> $DIR/default_numeric_fallback_f64.rs:51:21
 +   |
 +LL |             let y = 1.;
 +   |                     ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:66:9
++  --> $DIR/default_numeric_fallback_f64.rs:57:21
 +   |
 +LL |             let y = 1.;
 +   |                     ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:72:27
++  --> $DIR/default_numeric_fallback_f64.rs:69:9
 +   |
 +LL |         1.
 +   |         ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:76:29
++  --> $DIR/default_numeric_fallback_f64.rs:75:27
 +   |
 +LL |         let f = || -> _ { 1. };
 +   |                           ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:90:21
++  --> $DIR/default_numeric_fallback_f64.rs:79:29
 +   |
 +LL |         let f = || -> f64 { 1. };
 +   |                             ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:93:32
++  --> $DIR/default_numeric_fallback_f64.rs:93:21
 +   |
 +LL |         generic_arg(1.);
 +   |                     ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:111:28
++  --> $DIR/default_numeric_fallback_f64.rs:96:32
 +   |
 +LL |         let x: _ = generic_arg(1.);
 +   |                                ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:114:36
++  --> $DIR/default_numeric_fallback_f64.rs:114:28
 +   |
 +LL |         GenericStruct { x: 1. };
 +   |                            ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:132:24
++  --> $DIR/default_numeric_fallback_f64.rs:117:36
 +   |
 +LL |         let _ = GenericStruct { x: 1. };
 +   |                                    ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:152:23
++  --> $DIR/default_numeric_fallback_f64.rs:135:24
 +   |
 +LL |         GenericEnum::X(1.);
 +   |                        ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:159:21
++  --> $DIR/default_numeric_fallback_f64.rs:155:23
 +   |
 +LL |         s.generic_arg(1.);
 +   |                       ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
++  --> $DIR/default_numeric_fallback_f64.rs:162:21
 +   |
 +LL |             let x = 22.;
 +   |                     ^^^ help: consider adding suffix: `22.0_f64`
 +...
 +LL |         internal_macro!();
 +   |         ----------------- in this macro invocation
 +   |
 +   = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: aborting due to 23 previous errors
 +
index bded9e2c0e801723b89547d606120a989078473d,0000000000000000000000000000000000000000..fa85d278c8fcad8c82cd3fba79a9de7d81dcd287
mode 100644,000000..100644
--- /dev/null
@@@ -1,173 -1,0 +1,176 @@@
- #![allow(unused)]
- #![allow(clippy::never_loop)]
- #![allow(clippy::no_effect)]
- #![allow(clippy::unnecessary_operation)]
- #![allow(clippy::branches_sharing_code)]
 +// run-rustfix
 +// aux-build:macro_rules.rs
 +
 +#![warn(clippy::default_numeric_fallback)]
++#![allow(
++    unused,
++    clippy::never_loop,
++    clippy::no_effect,
++    clippy::unnecessary_operation,
++    clippy::branches_sharing_code,
++    clippy::let_unit_value
++)]
 +
 +#[macro_use]
 +extern crate macro_rules;
 +
 +mod basic_expr {
 +    fn test() {
 +        // Should lint unsuffixed literals typed `i32`.
 +        let x = 22_i32;
 +        let x = [1_i32, 2_i32, 3_i32];
 +        let x = if true { (1_i32, 2_i32) } else { (3_i32, 4_i32) };
 +        let x = match 1_i32 {
 +            1_i32 => 1_i32,
 +            _ => 2_i32,
 +        };
 +
 +        // Should NOT lint suffixed literals.
 +        let x = 22_i32;
 +
 +        // Should NOT lint literals in init expr if `Local` has a type annotation.
 +        let x: [i32; 3] = [1, 2, 3];
 +        let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
 +        let x: _ = 1;
 +    }
 +}
 +
 +mod nested_local {
 +    fn test() {
 +        let x: _ = {
 +            // Should lint this because this literal is not bound to any types.
 +            let y = 1_i32;
 +
 +            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
 +            1
 +        };
 +
 +        let x: _ = if true {
 +            // Should lint this because this literal is not bound to any types.
 +            let y = 1_i32;
 +
 +            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
 +            1
 +        } else {
 +            // Should lint this because this literal is not bound to any types.
 +            let y = 1_i32;
 +
 +            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
 +            2
 +        };
 +    }
 +}
 +
 +mod function_def {
 +    fn ret_i32() -> i32 {
 +        // Even though the output type is specified,
 +        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
 +        1_i32
 +    }
 +
 +    fn test() {
 +        // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
 +        // type.
 +        let f = || -> _ { 1_i32 };
 +
 +        // Even though the output type is specified,
 +        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
 +        let f = || -> i32 { 1_i32 };
 +    }
 +}
 +
 +mod function_calls {
 +    fn concrete_arg(x: i32) {}
 +
 +    fn generic_arg<T>(t: T) {}
 +
 +    fn test() {
 +        // Should NOT lint this because the argument type is bound to a concrete type.
 +        concrete_arg(1);
 +
 +        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
 +        generic_arg(1_i32);
 +
 +        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
 +        let x: _ = generic_arg(1_i32);
 +    }
 +}
 +
 +mod struct_ctor {
 +    struct ConcreteStruct {
 +        x: i32,
 +    }
 +
 +    struct GenericStruct<T> {
 +        x: T,
 +    }
 +
 +    fn test() {
 +        // Should NOT lint this because the field type is bound to a concrete type.
 +        ConcreteStruct { x: 1 };
 +
 +        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
 +        GenericStruct { x: 1_i32 };
 +
 +        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
 +        let _ = GenericStruct { x: 1_i32 };
 +    }
 +}
 +
 +mod enum_ctor {
 +    enum ConcreteEnum {
 +        X(i32),
 +    }
 +
 +    enum GenericEnum<T> {
 +        X(T),
 +    }
 +
 +    fn test() {
 +        // Should NOT lint this because the field type is bound to a concrete type.
 +        ConcreteEnum::X(1);
 +
 +        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
 +        GenericEnum::X(1_i32);
 +    }
 +}
 +
 +mod method_calls {
 +    struct StructForMethodCallTest;
 +
 +    impl StructForMethodCallTest {
 +        fn concrete_arg(&self, x: i32) {}
 +
 +        fn generic_arg<T>(&self, t: T) {}
 +    }
 +
 +    fn test() {
 +        let s = StructForMethodCallTest {};
 +
 +        // Should NOT lint this because the argument type is bound to a concrete type.
 +        s.concrete_arg(1);
 +
 +        // Should lint this because the argument type is bound to a concrete type.
 +        s.generic_arg(1_i32);
 +    }
 +}
 +
 +mod in_macro {
 +    macro_rules! internal_macro {
 +        () => {
 +            let x = 22_i32;
 +        };
 +    }
 +
 +    // Should lint in internal macro.
 +    fn internal() {
 +        internal_macro!();
 +    }
 +
 +    // Should NOT lint in external macro.
 +    fn external() {
 +        default_numeric_fallback!();
 +    }
 +}
 +
 +fn main() {}
index 3fceefa551c7843c4d1b54a36f98c3ac86bf2612,0000000000000000000000000000000000000000..71acccd702b062de3964431c62fdf4bed3e1c88b
mode 100644,000000..100644
--- /dev/null
@@@ -1,173 -1,0 +1,176 @@@
- #![allow(unused)]
- #![allow(clippy::never_loop)]
- #![allow(clippy::no_effect)]
- #![allow(clippy::unnecessary_operation)]
- #![allow(clippy::branches_sharing_code)]
 +// run-rustfix
 +// aux-build:macro_rules.rs
 +
 +#![warn(clippy::default_numeric_fallback)]
++#![allow(
++    unused,
++    clippy::never_loop,
++    clippy::no_effect,
++    clippy::unnecessary_operation,
++    clippy::branches_sharing_code,
++    clippy::let_unit_value
++)]
 +
 +#[macro_use]
 +extern crate macro_rules;
 +
 +mod basic_expr {
 +    fn test() {
 +        // Should lint unsuffixed literals typed `i32`.
 +        let x = 22;
 +        let x = [1, 2, 3];
 +        let x = if true { (1, 2) } else { (3, 4) };
 +        let x = match 1 {
 +            1 => 1,
 +            _ => 2,
 +        };
 +
 +        // Should NOT lint suffixed literals.
 +        let x = 22_i32;
 +
 +        // Should NOT lint literals in init expr if `Local` has a type annotation.
 +        let x: [i32; 3] = [1, 2, 3];
 +        let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
 +        let x: _ = 1;
 +    }
 +}
 +
 +mod nested_local {
 +    fn test() {
 +        let x: _ = {
 +            // Should lint this because this literal is not bound to any types.
 +            let y = 1;
 +
 +            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
 +            1
 +        };
 +
 +        let x: _ = if true {
 +            // Should lint this because this literal is not bound to any types.
 +            let y = 1;
 +
 +            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
 +            1
 +        } else {
 +            // Should lint this because this literal is not bound to any types.
 +            let y = 1;
 +
 +            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
 +            2
 +        };
 +    }
 +}
 +
 +mod function_def {
 +    fn ret_i32() -> i32 {
 +        // Even though the output type is specified,
 +        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
 +        1
 +    }
 +
 +    fn test() {
 +        // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
 +        // type.
 +        let f = || -> _ { 1 };
 +
 +        // Even though the output type is specified,
 +        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
 +        let f = || -> i32 { 1 };
 +    }
 +}
 +
 +mod function_calls {
 +    fn concrete_arg(x: i32) {}
 +
 +    fn generic_arg<T>(t: T) {}
 +
 +    fn test() {
 +        // Should NOT lint this because the argument type is bound to a concrete type.
 +        concrete_arg(1);
 +
 +        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
 +        generic_arg(1);
 +
 +        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
 +        let x: _ = generic_arg(1);
 +    }
 +}
 +
 +mod struct_ctor {
 +    struct ConcreteStruct {
 +        x: i32,
 +    }
 +
 +    struct GenericStruct<T> {
 +        x: T,
 +    }
 +
 +    fn test() {
 +        // Should NOT lint this because the field type is bound to a concrete type.
 +        ConcreteStruct { x: 1 };
 +
 +        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
 +        GenericStruct { x: 1 };
 +
 +        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
 +        let _ = GenericStruct { x: 1 };
 +    }
 +}
 +
 +mod enum_ctor {
 +    enum ConcreteEnum {
 +        X(i32),
 +    }
 +
 +    enum GenericEnum<T> {
 +        X(T),
 +    }
 +
 +    fn test() {
 +        // Should NOT lint this because the field type is bound to a concrete type.
 +        ConcreteEnum::X(1);
 +
 +        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
 +        GenericEnum::X(1);
 +    }
 +}
 +
 +mod method_calls {
 +    struct StructForMethodCallTest;
 +
 +    impl StructForMethodCallTest {
 +        fn concrete_arg(&self, x: i32) {}
 +
 +        fn generic_arg<T>(&self, t: T) {}
 +    }
 +
 +    fn test() {
 +        let s = StructForMethodCallTest {};
 +
 +        // Should NOT lint this because the argument type is bound to a concrete type.
 +        s.concrete_arg(1);
 +
 +        // Should lint this because the argument type is bound to a concrete type.
 +        s.generic_arg(1);
 +    }
 +}
 +
 +mod in_macro {
 +    macro_rules! internal_macro {
 +        () => {
 +            let x = 22;
 +        };
 +    }
 +
 +    // Should lint in internal macro.
 +    fn internal() {
 +        internal_macro!();
 +    }
 +
 +    // Should NOT lint in external macro.
 +    fn external() {
 +        default_numeric_fallback!();
 +    }
 +}
 +
 +fn main() {}
index 6f9e124704b2c76834dd95840a8cebfecec1d589,0000000000000000000000000000000000000000..3cc84ff1132321873e65e03dcde9d1017e75b379
mode 100644,000000..100644
--- /dev/null
@@@ -1,159 -1,0 +1,159 @@@
-   --> $DIR/default_numeric_fallback_i32.rs:17:17
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:18:18
++  --> $DIR/default_numeric_fallback_i32.rs:20:17
 +   |
 +LL |         let x = 22;
 +   |                 ^^ help: consider adding suffix: `22_i32`
 +   |
 +   = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:18:21
++  --> $DIR/default_numeric_fallback_i32.rs:21:18
 +   |
 +LL |         let x = [1, 2, 3];
 +   |                  ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:18:24
++  --> $DIR/default_numeric_fallback_i32.rs:21:21
 +   |
 +LL |         let x = [1, 2, 3];
 +   |                     ^ help: consider adding suffix: `2_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:19:28
++  --> $DIR/default_numeric_fallback_i32.rs:21:24
 +   |
 +LL |         let x = [1, 2, 3];
 +   |                        ^ help: consider adding suffix: `3_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:19:31
++  --> $DIR/default_numeric_fallback_i32.rs:22:28
 +   |
 +LL |         let x = if true { (1, 2) } else { (3, 4) };
 +   |                            ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:19:44
++  --> $DIR/default_numeric_fallback_i32.rs:22:31
 +   |
 +LL |         let x = if true { (1, 2) } else { (3, 4) };
 +   |                               ^ help: consider adding suffix: `2_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:19:47
++  --> $DIR/default_numeric_fallback_i32.rs:22:44
 +   |
 +LL |         let x = if true { (1, 2) } else { (3, 4) };
 +   |                                            ^ help: consider adding suffix: `3_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:20:23
++  --> $DIR/default_numeric_fallback_i32.rs:22:47
 +   |
 +LL |         let x = if true { (1, 2) } else { (3, 4) };
 +   |                                               ^ help: consider adding suffix: `4_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:21:13
++  --> $DIR/default_numeric_fallback_i32.rs:23:23
 +   |
 +LL |         let x = match 1 {
 +   |                       ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:21:18
++  --> $DIR/default_numeric_fallback_i32.rs:24:13
 +   |
 +LL |             1 => 1,
 +   |             ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:22:18
++  --> $DIR/default_numeric_fallback_i32.rs:24:18
 +   |
 +LL |             1 => 1,
 +   |                  ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:39:21
++  --> $DIR/default_numeric_fallback_i32.rs:25:18
 +   |
 +LL |             _ => 2,
 +   |                  ^ help: consider adding suffix: `2_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:47:21
++  --> $DIR/default_numeric_fallback_i32.rs:42:21
 +   |
 +LL |             let y = 1;
 +   |                     ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:53:21
++  --> $DIR/default_numeric_fallback_i32.rs:50:21
 +   |
 +LL |             let y = 1;
 +   |                     ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:65:9
++  --> $DIR/default_numeric_fallback_i32.rs:56:21
 +   |
 +LL |             let y = 1;
 +   |                     ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:71:27
++  --> $DIR/default_numeric_fallback_i32.rs:68:9
 +   |
 +LL |         1
 +   |         ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:75:29
++  --> $DIR/default_numeric_fallback_i32.rs:74:27
 +   |
 +LL |         let f = || -> _ { 1 };
 +   |                           ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:89:21
++  --> $DIR/default_numeric_fallback_i32.rs:78:29
 +   |
 +LL |         let f = || -> i32 { 1 };
 +   |                             ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:92:32
++  --> $DIR/default_numeric_fallback_i32.rs:92:21
 +   |
 +LL |         generic_arg(1);
 +   |                     ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:110:28
++  --> $DIR/default_numeric_fallback_i32.rs:95:32
 +   |
 +LL |         let x: _ = generic_arg(1);
 +   |                                ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:113:36
++  --> $DIR/default_numeric_fallback_i32.rs:113:28
 +   |
 +LL |         GenericStruct { x: 1 };
 +   |                            ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:131:24
++  --> $DIR/default_numeric_fallback_i32.rs:116:36
 +   |
 +LL |         let _ = GenericStruct { x: 1 };
 +   |                                    ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:151:23
++  --> $DIR/default_numeric_fallback_i32.rs:134:24
 +   |
 +LL |         GenericEnum::X(1);
 +   |                        ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:158:21
++  --> $DIR/default_numeric_fallback_i32.rs:154:23
 +   |
 +LL |         s.generic_arg(1);
 +   |                       ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
++  --> $DIR/default_numeric_fallback_i32.rs:161:21
 +   |
 +LL |             let x = 22;
 +   |                     ^^ help: consider adding suffix: `22_i32`
 +...
 +LL |         internal_macro!();
 +   |         ----------------- in this macro invocation
 +   |
 +   = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: aborting due to 25 previous errors
 +
index 39a2601fee9aca39e6d3882f27c8d1008ffce339,0000000000000000000000000000000000000000..07270bd76362a1a8cf4eadc533d6c0e01465d259
mode 100644,000000..100644
--- /dev/null
@@@ -1,18 -1,0 +1,22 @@@
++// 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.
++
 +#![warn(clippy::should_assert_eq)]
 +#![warn(clippy::extend_from_slice)]
 +#![warn(clippy::range_step_by_zero)]
 +#![warn(clippy::unstable_as_slice)]
 +#![warn(clippy::unstable_as_mut_slice)]
 +#![warn(clippy::misaligned_transmute)]
 +#![warn(clippy::assign_ops)]
 +#![warn(clippy::if_let_redundant_pattern_matching)]
 +#![warn(clippy::unsafe_vector_initialization)]
 +#![warn(clippy::unused_collect)]
 +#![warn(clippy::replace_consts)]
 +#![warn(clippy::regex_macro)]
 +#![warn(clippy::find_map)]
 +#![warn(clippy::filter_map)]
 +#![warn(clippy::pub_enum_variant_names)]
 +#![warn(clippy::wrong_pub_self_convention)]
 +
 +fn main() {}
index 6095f134d55e0d2ea7452696adfb9a047521f077,0000000000000000000000000000000000000000..0e142ac8f20e7a3a83d0f1792f7363cb402be641
mode 100644,000000..100644
--- /dev/null
@@@ -1,100 -1,0 +1,100 @@@
-   --> $DIR/deprecated.rs:1:9
 +error: lint `clippy::should_assert_eq` has been removed: `assert!()` will be more flexible with RFC 2011
-   --> $DIR/deprecated.rs:2:9
++  --> $DIR/deprecated.rs:5:9
 +   |
 +LL | #![warn(clippy::should_assert_eq)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D renamed-and-removed-lints` implied by `-D warnings`
 +
 +error: lint `clippy::extend_from_slice` has been removed: `.extend_from_slice(_)` is a faster way to extend a Vec by a slice
-   --> $DIR/deprecated.rs:3:9
++  --> $DIR/deprecated.rs:6:9
 +   |
 +LL | #![warn(clippy::extend_from_slice)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::range_step_by_zero` has been removed: `iterator.step_by(0)` panics nowadays
-   --> $DIR/deprecated.rs:4:9
++  --> $DIR/deprecated.rs:7:9
 +   |
 +LL | #![warn(clippy::range_step_by_zero)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7
-   --> $DIR/deprecated.rs:5:9
++  --> $DIR/deprecated.rs:8:9
 +   |
 +LL | #![warn(clippy::unstable_as_slice)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7
-   --> $DIR/deprecated.rs:6:9
++  --> $DIR/deprecated.rs:9:9
 +   |
 +LL | #![warn(clippy::unstable_as_mut_slice)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr
-   --> $DIR/deprecated.rs:7:9
++  --> $DIR/deprecated.rs:10:9
 +   |
 +LL | #![warn(clippy::misaligned_transmute)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::assign_ops` has been removed: using compound assignment operators (e.g., `+=`) is harmless
-   --> $DIR/deprecated.rs:8:9
++  --> $DIR/deprecated.rs:11:9
 +   |
 +LL | #![warn(clippy::assign_ops)]
 +   |         ^^^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::if_let_redundant_pattern_matching` has been removed: this lint has been changed to redundant_pattern_matching
-   --> $DIR/deprecated.rs:9:9
++  --> $DIR/deprecated.rs:12:9
 +   |
 +LL | #![warn(clippy::if_let_redundant_pattern_matching)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::unsafe_vector_initialization` has been removed: the replacement suggested by this lint had substantially different behavior
-   --> $DIR/deprecated.rs:10:9
++  --> $DIR/deprecated.rs:13:9
 +   |
 +LL | #![warn(clippy::unsafe_vector_initialization)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::unused_collect` has been removed: `collect` has been marked as #[must_use] in rustc and that covers all cases of this lint
-   --> $DIR/deprecated.rs:11:9
++  --> $DIR/deprecated.rs:14:9
 +   |
 +LL | #![warn(clippy::unused_collect)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::replace_consts` has been removed: associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants
-   --> $DIR/deprecated.rs:12:9
++  --> $DIR/deprecated.rs:15:9
 +   |
 +LL | #![warn(clippy::replace_consts)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018
-   --> $DIR/deprecated.rs:13:9
++  --> $DIR/deprecated.rs:16:9
 +   |
 +LL | #![warn(clippy::regex_macro)]
 +   |         ^^^^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint
-   --> $DIR/deprecated.rs:14:9
++  --> $DIR/deprecated.rs:17:9
 +   |
 +LL | #![warn(clippy::find_map)]
 +   |         ^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint
-   --> $DIR/deprecated.rs:15:9
++  --> $DIR/deprecated.rs:18:9
 +   |
 +LL | #![warn(clippy::filter_map)]
 +   |         ^^^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items
-   --> $DIR/deprecated.rs:16:9
++  --> $DIR/deprecated.rs:19:9
 +   |
 +LL | #![warn(clippy::pub_enum_variant_names)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items
++  --> $DIR/deprecated.rs:20:9
 +   |
 +LL | #![warn(clippy::wrong_pub_self_convention)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: aborting due to 16 previous errors
 +
index 4464a21b3b654bdf89b238a5c0594b61edbf06ce,0000000000000000000000000000000000000000..b91f7aa0dd8d2bfe51710c8ab57adea3861d498d
mode 100644,000000..100644
--- /dev/null
@@@ -1,132 -1,0 +1,134 @@@
 +// aux-build:doc_unsafe_macros.rs
 +
++#![allow(clippy::let_unit_value)]
++
 +#[macro_use]
 +extern crate doc_unsafe_macros;
 +
 +/// This is not sufficiently documented
 +pub unsafe fn destroy_the_planet() {
 +    unimplemented!();
 +}
 +
 +/// This one is
 +///
 +/// # Safety
 +///
 +/// This function shouldn't be called unless the horsemen are ready
 +pub unsafe fn apocalypse(universe: &mut ()) {
 +    unimplemented!();
 +}
 +
 +/// This is a private function, so docs aren't necessary
 +unsafe fn you_dont_see_me() {
 +    unimplemented!();
 +}
 +
 +mod private_mod {
 +    pub unsafe fn only_crate_wide_accessible() {
 +        unimplemented!();
 +    }
 +
 +    pub unsafe fn republished() {
 +        unimplemented!();
 +    }
 +}
 +
 +pub use private_mod::republished;
 +
 +pub trait SafeTraitUnsafeMethods {
 +    unsafe fn woefully_underdocumented(self);
 +
 +    /// # Safety
 +    unsafe fn at_least_somewhat_documented(self);
 +}
 +
 +pub unsafe trait UnsafeTrait {
 +    fn method();
 +}
 +
 +/// # Safety
 +pub unsafe trait DocumentedUnsafeTrait {
 +    fn method2();
 +}
 +
 +pub struct Struct;
 +
 +impl SafeTraitUnsafeMethods for Struct {
 +    unsafe fn woefully_underdocumented(self) {
 +        // all is well
 +    }
 +
 +    unsafe fn at_least_somewhat_documented(self) {
 +        // all is still well
 +    }
 +}
 +
 +unsafe impl UnsafeTrait for Struct {
 +    fn method() {}
 +}
 +
 +unsafe impl DocumentedUnsafeTrait for Struct {
 +    fn method2() {}
 +}
 +
 +impl Struct {
 +    pub unsafe fn more_undocumented_unsafe() -> Self {
 +        unimplemented!();
 +    }
 +
 +    /// # Safety
 +    pub unsafe fn somewhat_documented(&self) {
 +        unimplemented!();
 +    }
 +
 +    unsafe fn private(&self) {
 +        unimplemented!();
 +    }
 +}
 +
 +macro_rules! very_unsafe {
 +    () => {
 +        pub unsafe fn whee() {
 +            unimplemented!()
 +        }
 +
 +        /// # Safety
 +        ///
 +        /// Please keep the seat belt fastened
 +        pub unsafe fn drive() {
 +            whee()
 +        }
 +    };
 +}
 +
 +very_unsafe!();
 +
 +// we don't lint code from external macros
 +undocd_unsafe!();
 +
 +fn main() {
 +    unsafe {
 +        you_dont_see_me();
 +        destroy_the_planet();
 +        let mut universe = ();
 +        apocalypse(&mut universe);
 +        private_mod::only_crate_wide_accessible();
 +        drive();
 +    }
 +}
 +
 +// do not lint if any parent has `#[doc(hidden)]` attribute
 +// see #7347
 +#[doc(hidden)]
 +pub mod __macro {
 +    pub struct T;
 +    impl T {
 +        pub unsafe fn f() {}
 +    }
 +}
 +
 +/// # Implementation safety
 +pub unsafe trait DocumentedUnsafeTraitWithImplementationHeader {
 +    fn method();
 +}
index d68b8a0c67be68fda36fdfd3b961ed6f3ff97945,0000000000000000000000000000000000000000..904b88eaef62280a1a56991466904e7afd57ef9c
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,55 @@@
-   --> $DIR/doc_unsafe.rs:7:1
 +error: unsafe function's docs miss `# Safety` section
-   --> $DIR/doc_unsafe.rs:30:5
++  --> $DIR/doc_unsafe.rs:9:1
 +   |
 +LL | / pub unsafe fn destroy_the_planet() {
 +LL | |     unimplemented!();
 +LL | | }
 +   | |_^
 +   |
 +   = note: `-D clippy::missing-safety-doc` implied by `-D warnings`
 +
 +error: unsafe function's docs miss `# Safety` section
-   --> $DIR/doc_unsafe.rs:38:5
++  --> $DIR/doc_unsafe.rs:32:5
 +   |
 +LL | /     pub unsafe fn republished() {
 +LL | |         unimplemented!();
 +LL | |     }
 +   | |_____^
 +
 +error: unsafe function's docs miss `# Safety` section
-   --> $DIR/doc_unsafe.rs:44:1
++  --> $DIR/doc_unsafe.rs:40:5
 +   |
 +LL |     unsafe fn woefully_underdocumented(self);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: docs for unsafe trait missing `# Safety` section
-   --> $DIR/doc_unsafe.rs:74:5
++  --> $DIR/doc_unsafe.rs:46:1
 +   |
 +LL | / pub unsafe trait UnsafeTrait {
 +LL | |     fn method();
 +LL | | }
 +   | |_^
 +
 +error: unsafe function's docs miss `# Safety` section
-   --> $DIR/doc_unsafe.rs:90:9
++  --> $DIR/doc_unsafe.rs:76:5
 +   |
 +LL | /     pub unsafe fn more_undocumented_unsafe() -> Self {
 +LL | |         unimplemented!();
 +LL | |     }
 +   | |_____^
 +
 +error: unsafe function's docs miss `# Safety` section
++  --> $DIR/doc_unsafe.rs:92:9
 +   |
 +LL | /         pub unsafe fn whee() {
 +LL | |             unimplemented!()
 +LL | |         }
 +   | |_________^
 +...
 +LL |   very_unsafe!();
 +   |   -------------- in this macro invocation
 +   |
 +   = note: this error originates in the macro `very_unsafe` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: aborting due to 6 previous errors
 +
index f73068901c503ac2ff3eb1b685b3336247b1ddd9,0000000000000000000000000000000000000000..30121033de7eae113ff2025830aae75fa0c24d10
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,27 @@@
- error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes
++error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes
 +  --> $DIR/drop_non_drop.rs:22:5
 +   |
 +LL |     drop(Foo);
 +   |     ^^^^^^^^^
 +   |
 +   = note: `-D clippy::drop-non-drop` implied by `-D warnings`
 +note: argument has type `main::Foo`
 +  --> $DIR/drop_non_drop.rs:22:10
 +   |
 +LL |     drop(Foo);
 +   |          ^^^
 +
- error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes
++error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes
 +  --> $DIR/drop_non_drop.rs:37:5
 +   |
 +LL |     drop(Baz(Foo));
 +   |     ^^^^^^^^^^^^^^
 +   |
 +note: argument has type `main::Baz<main::Foo>`
 +  --> $DIR/drop_non_drop.rs:37:10
 +   |
 +LL |     drop(Baz(Foo));
 +   |          ^^^^^^^^
 +
 +error: aborting due to 2 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2e1b768461ab2a1e7483831b842e4822b4c13502
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++// run-rustfix
++#![warn(clippy::empty_drop)]
++#![allow(unused)]
++
++// should cause an error
++struct Foo;
++
++
++
++// shouldn't cause an error
++struct Bar;
++
++impl Drop for Bar {
++    fn drop(&mut self) {
++        println!("dropping bar!");
++    }
++}
++
++// should error
++struct Baz;
++
++
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..75232b0334df6c9153bc927021b4dfd611f75c48
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++// run-rustfix
++#![warn(clippy::empty_drop)]
++#![allow(unused)]
++
++// should cause an error
++struct Foo;
++
++impl Drop for Foo {
++    fn drop(&mut self) {}
++}
++
++// shouldn't cause an error
++struct Bar;
++
++impl Drop for Bar {
++    fn drop(&mut self) {
++        println!("dropping bar!");
++    }
++}
++
++// should error
++struct Baz;
++
++impl Drop for Baz {
++    fn drop(&mut self) {
++        {}
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..70f7880d03601314e4ea7bd40f6b8d0098b3dd8a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: empty drop implementation
++  --> $DIR/empty_drop.rs:8:1
++   |
++LL | / impl Drop for Foo {
++LL | |     fn drop(&mut self) {}
++LL | | }
++   | |_^ help: try removing this impl
++   |
++   = note: `-D clippy::empty-drop` implied by `-D warnings`
++
++error: empty drop implementation
++  --> $DIR/empty_drop.rs:24:1
++   |
++LL | / impl Drop for Baz {
++LL | |     fn drop(&mut self) {
++LL | |         {}
++LL | |     }
++LL | | }
++   | |_^ help: try removing this impl
++
++error: aborting due to 2 previous errors
++
index 5aedbea381f2317d80c661cc20f7ffbc07748eae,0000000000000000000000000000000000000000..6c2272f4dff97f350344b4fbb66fc5d4ba4cb146
mode 100644,000000..100644
--- /dev/null
@@@ -1,277 -1,0 +1,293 @@@
-     let e = Some(1u8).map(divergent);
 +// run-rustfix
 +
 +#![allow(
 +    unused,
 +    clippy::no_effect,
 +    clippy::redundant_closure_call,
 +    clippy::needless_pass_by_value,
 +    clippy::option_map_unit_fn,
 +    clippy::needless_borrow
 +)]
 +#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)]
 +
 +use std::path::{Path, PathBuf};
 +
 +macro_rules! mac {
 +    () => {
 +        foobar()
 +    };
 +}
 +
 +macro_rules! closure_mac {
 +    () => {
 +        |n| foo(n)
 +    };
 +}
 +
 +fn main() {
 +    let a = Some(1u8).map(foo);
 +    let c = Some(1u8).map(|a| {1+2; foo}(a));
 +    true.then(|| mac!()); // don't lint function in macro expansion
 +    Some(1).map(closure_mac!()); // don't lint closure in macro expansion
 +    let _: Option<Vec<u8>> = true.then(std::vec::Vec::new); // special case vec!
 +    let d = Some(1u8).map(|a| foo(foo2(a))); //is adjusted?
 +    all(&[1, 2, 3], &&2, below); //is adjusted
 +    unsafe {
 +        Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
 +    }
 +
 +    // See #815
-     map_str_to_path(std::convert::AsRef::as_ref);
++    let e = Some(1u8).map(|a| divergent(a));
 +    let e = Some(1u8).map(generic);
 +    let e = Some(1u8).map(generic);
 +    // See #515
 +    let a: Option<Box<dyn (::std::ops::Deref<Target = [i32]>)>> =
 +        Some(vec![1i32, 2]).map(|v| -> Box<dyn (::std::ops::Deref<Target = [i32]>)> { Box::new(v) });
 +
 +    // issue #7224
 +    let _: Option<Vec<u32>> = Some(0).map(|_| vec![]);
 +}
 +
 +trait TestTrait {
 +    fn trait_foo(self) -> bool;
 +    fn trait_foo_ref(&self) -> bool;
 +}
 +
 +struct TestStruct<'a> {
 +    some_ref: &'a i32,
 +}
 +
 +impl<'a> TestStruct<'a> {
 +    fn foo(self) -> bool {
 +        false
 +    }
 +    unsafe fn foo_unsafe(self) -> bool {
 +        true
 +    }
 +}
 +
 +impl<'a> TestTrait for TestStruct<'a> {
 +    fn trait_foo(self) -> bool {
 +        false
 +    }
 +    fn trait_foo_ref(&self) -> bool {
 +        false
 +    }
 +}
 +
 +impl<'a> std::ops::Deref for TestStruct<'a> {
 +    type Target = char;
 +    fn deref(&self) -> &char {
 +        &'a'
 +    }
 +}
 +
 +fn test_redundant_closures_containing_method_calls() {
 +    let i = 10;
 +    let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
 +    let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
 +    let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
 +    let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
 +    unsafe {
 +        let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
 +    }
 +    let e = Some("str").map(std::string::ToString::to_string);
 +    let e = Some('a').map(char::to_uppercase);
 +    let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
 +    let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
 +    let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str());
 +    let c = Some(TestStruct { some_ref: &i })
 +        .as_ref()
 +        .map(|c| c.to_ascii_uppercase());
 +
 +    fn test_different_borrow_levels<T>(t: &[&T])
 +    where
 +        T: TestTrait,
 +    {
 +        t.iter().filter(|x| x.trait_foo_ref());
 +        t.iter().map(|x| x.trait_foo_ref());
 +    }
 +}
 +
 +struct Thunk<T>(Box<dyn FnMut() -> T>);
 +
 +impl<T> Thunk<T> {
 +    fn new<F: 'static + FnOnce() -> T>(f: F) -> Thunk<T> {
 +        let mut option = Some(f);
 +        // This should not trigger redundant_closure (#1439)
 +        Thunk(Box::new(move || option.take().unwrap()()))
 +    }
 +
 +    fn unwrap(self) -> T {
 +        let Thunk(mut f) = self;
 +        f()
 +    }
 +}
 +
 +fn foobar() {
 +    let thunk = Thunk::new(|| println!("Hello, world!"));
 +    thunk.unwrap()
 +}
 +
 +fn foo(_: u8) {}
 +
 +fn foo2(_: u8) -> u8 {
 +    1u8
 +}
 +
 +fn all<X, F>(x: &[X], y: &X, f: F) -> bool
 +where
 +    F: Fn(&X, &X) -> bool,
 +{
 +    x.iter().all(|e| f(e, y))
 +}
 +
 +fn below(x: &u8, y: &u8) -> bool {
 +    x < y
 +}
 +
 +unsafe fn unsafe_fn(_: u8) {}
 +
 +fn divergent(_: u8) -> ! {
 +    unimplemented!()
 +}
 +
 +fn generic<T>(_: T) -> u8 {
 +    0
 +}
 +
 +fn passes_fn_mut(mut x: Box<dyn FnMut()>) {
 +    requires_fn_once(x);
 +}
 +fn requires_fn_once<T: FnOnce()>(_: T) {}
 +
 +fn test_redundant_closure_with_function_pointer() {
 +    type FnPtrType = fn(u8);
 +    let foo_ptr: FnPtrType = foo;
 +    let a = Some(1u8).map(foo_ptr);
 +}
 +
 +fn test_redundant_closure_with_another_closure() {
 +    let closure = |a| println!("{}", a);
 +    let a = Some(1u8).map(closure);
 +}
 +
 +fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 {
 +    // Currently f is called when result of make_lazy is called.
 +    // If the closure is removed, f will be called when make_lazy itself is
 +    // called. This changes semantics, so the closure must stay.
 +    Box::new(move |x| f()(x))
 +}
 +
 +fn call<F: FnOnce(&mut String) -> String>(f: F) -> String {
 +    f(&mut "Hello".to_owned())
 +}
 +fn test_difference_in_mutability() {
 +    call(|s| s.clone());
 +}
 +
 +struct Bar;
 +impl std::ops::Deref for Bar {
 +    type Target = str;
 +    fn deref(&self) -> &str {
 +        "hi"
 +    }
 +}
 +
 +fn test_deref_with_trait_method() {
 +    let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
 +}
 +
 +fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
 +    let mut res = Vec::new();
 +    let mut add_to_res = |n| res.push(n);
 +    x.into_iter().for_each(&mut add_to_res);
 +    y.into_iter().for_each(&mut add_to_res);
 +    z.into_iter().for_each(add_to_res);
 +}
 +
 +fn mutable_closure_in_loop() {
 +    let mut value = 0;
 +    let mut closure = |n| value += n;
 +    for _ in 0..5 {
 +        Some(1).map(&mut closure);
++
++        let mut value = 0;
++        let mut in_loop = |n| value += n;
++        Some(1).map(in_loop);
 +    }
 +}
 +
 +fn late_bound_lifetimes() {
 +    fn take_asref_path<P: AsRef<Path>>(path: P) {}
 +
 +    fn map_str<F>(thunk: F)
 +    where
 +        F: FnOnce(&str),
 +    {
 +    }
 +
 +    fn map_str_to_path<F>(thunk: F)
 +    where
 +        F: FnOnce(&str) -> &Path,
 +    {
 +    }
 +    map_str(|s| take_asref_path(s));
++    map_str_to_path(|s| s.as_ref());
 +}
 +
 +mod type_param_bound {
 +    trait Trait {
 +        fn fun();
 +    }
 +
 +    fn take<T: 'static>(_: T) {}
 +
 +    fn test<X: Trait>() {
 +        // don't lint, but it's questionable that rust requires a cast
 +        take(|| X::fun());
 +        take(X::fun as fn());
 +    }
 +}
 +
 +// #8073 Don't replace closure with `Arc<F>` or `Rc<F>`
 +fn arc_fp() {
 +    let rc = std::rc::Rc::new(|| 7);
 +    let arc = std::sync::Arc::new(|n| n + 1);
 +    let ref_arc = &std::sync::Arc::new(|_| 5);
 +
 +    true.then(|| rc());
 +    (0..5).map(|n| arc(n));
 +    Some(4).map(|n| ref_arc(n));
 +}
 +
 +// #8460 Don't replace closures with params bounded as `ref`
 +mod bind_by_ref {
 +    struct A;
 +    struct B;
 +
 +    impl From<&A> for B {
 +        fn from(A: &A) -> Self {
 +            B
 +        }
 +    }
 +
 +    fn test() {
 +        // should not lint
 +        Some(A).map(|a| B::from(&a));
 +        // should not lint
 +        Some(A).map(|ref a| B::from(a));
 +    }
 +}
++
++// #7812 False positive on coerced closure
++fn coerced_closure() {
++    fn function_returning_unit<F: FnMut(i32)>(f: F) {}
++    function_returning_unit(|x| std::process::exit(x));
++
++    fn arr() -> &'static [u8; 0] {
++        &[]
++    }
++    fn slice_fn(_: impl FnOnce() -> &'static [u8]) {}
++    slice_fn(|| arr());
++}
index 5fdf7fb9771697e2330265458903ac5c12528f94,0000000000000000000000000000000000000000..a1a9c0dfbf381d64ae7f47d3fdc46cb1e981ad69
mode 100644,000000..100644
--- /dev/null
@@@ -1,277 -1,0 +1,293 @@@
 +// run-rustfix
 +
 +#![allow(
 +    unused,
 +    clippy::no_effect,
 +    clippy::redundant_closure_call,
 +    clippy::needless_pass_by_value,
 +    clippy::option_map_unit_fn,
 +    clippy::needless_borrow
 +)]
 +#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)]
 +
 +use std::path::{Path, PathBuf};
 +
 +macro_rules! mac {
 +    () => {
 +        foobar()
 +    };
 +}
 +
 +macro_rules! closure_mac {
 +    () => {
 +        |n| foo(n)
 +    };
 +}
 +
 +fn main() {
 +    let a = Some(1u8).map(|a| foo(a));
 +    let c = Some(1u8).map(|a| {1+2; foo}(a));
 +    true.then(|| mac!()); // don't lint function in macro expansion
 +    Some(1).map(closure_mac!()); // don't lint closure in macro expansion
 +    let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
 +    let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
 +    all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
 +    unsafe {
 +        Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
 +    }
 +
 +    // See #815
 +    let e = Some(1u8).map(|a| divergent(a));
 +    let e = Some(1u8).map(|a| generic(a));
 +    let e = Some(1u8).map(generic);
 +    // See #515
 +    let a: Option<Box<dyn (::std::ops::Deref<Target = [i32]>)>> =
 +        Some(vec![1i32, 2]).map(|v| -> Box<dyn (::std::ops::Deref<Target = [i32]>)> { Box::new(v) });
 +
 +    // issue #7224
 +    let _: Option<Vec<u32>> = Some(0).map(|_| vec![]);
 +}
 +
 +trait TestTrait {
 +    fn trait_foo(self) -> bool;
 +    fn trait_foo_ref(&self) -> bool;
 +}
 +
 +struct TestStruct<'a> {
 +    some_ref: &'a i32,
 +}
 +
 +impl<'a> TestStruct<'a> {
 +    fn foo(self) -> bool {
 +        false
 +    }
 +    unsafe fn foo_unsafe(self) -> bool {
 +        true
 +    }
 +}
 +
 +impl<'a> TestTrait for TestStruct<'a> {
 +    fn trait_foo(self) -> bool {
 +        false
 +    }
 +    fn trait_foo_ref(&self) -> bool {
 +        false
 +    }
 +}
 +
 +impl<'a> std::ops::Deref for TestStruct<'a> {
 +    type Target = char;
 +    fn deref(&self) -> &char {
 +        &'a'
 +    }
 +}
 +
 +fn test_redundant_closures_containing_method_calls() {
 +    let i = 10;
 +    let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
 +    let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
 +    let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
 +    let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
 +    unsafe {
 +        let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
 +    }
 +    let e = Some("str").map(|s| s.to_string());
 +    let e = Some('a').map(|s| s.to_uppercase());
 +    let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
 +    let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
 +    let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str());
 +    let c = Some(TestStruct { some_ref: &i })
 +        .as_ref()
 +        .map(|c| c.to_ascii_uppercase());
 +
 +    fn test_different_borrow_levels<T>(t: &[&T])
 +    where
 +        T: TestTrait,
 +    {
 +        t.iter().filter(|x| x.trait_foo_ref());
 +        t.iter().map(|x| x.trait_foo_ref());
 +    }
 +}
 +
 +struct Thunk<T>(Box<dyn FnMut() -> T>);
 +
 +impl<T> Thunk<T> {
 +    fn new<F: 'static + FnOnce() -> T>(f: F) -> Thunk<T> {
 +        let mut option = Some(f);
 +        // This should not trigger redundant_closure (#1439)
 +        Thunk(Box::new(move || option.take().unwrap()()))
 +    }
 +
 +    fn unwrap(self) -> T {
 +        let Thunk(mut f) = self;
 +        f()
 +    }
 +}
 +
 +fn foobar() {
 +    let thunk = Thunk::new(|| println!("Hello, world!"));
 +    thunk.unwrap()
 +}
 +
 +fn foo(_: u8) {}
 +
 +fn foo2(_: u8) -> u8 {
 +    1u8
 +}
 +
 +fn all<X, F>(x: &[X], y: &X, f: F) -> bool
 +where
 +    F: Fn(&X, &X) -> bool,
 +{
 +    x.iter().all(|e| f(e, y))
 +}
 +
 +fn below(x: &u8, y: &u8) -> bool {
 +    x < y
 +}
 +
 +unsafe fn unsafe_fn(_: u8) {}
 +
 +fn divergent(_: u8) -> ! {
 +    unimplemented!()
 +}
 +
 +fn generic<T>(_: T) -> u8 {
 +    0
 +}
 +
 +fn passes_fn_mut(mut x: Box<dyn FnMut()>) {
 +    requires_fn_once(|| x());
 +}
 +fn requires_fn_once<T: FnOnce()>(_: T) {}
 +
 +fn test_redundant_closure_with_function_pointer() {
 +    type FnPtrType = fn(u8);
 +    let foo_ptr: FnPtrType = foo;
 +    let a = Some(1u8).map(|a| foo_ptr(a));
 +}
 +
 +fn test_redundant_closure_with_another_closure() {
 +    let closure = |a| println!("{}", a);
 +    let a = Some(1u8).map(|a| closure(a));
 +}
 +
 +fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 {
 +    // Currently f is called when result of make_lazy is called.
 +    // If the closure is removed, f will be called when make_lazy itself is
 +    // called. This changes semantics, so the closure must stay.
 +    Box::new(move |x| f()(x))
 +}
 +
 +fn call<F: FnOnce(&mut String) -> String>(f: F) -> String {
 +    f(&mut "Hello".to_owned())
 +}
 +fn test_difference_in_mutability() {
 +    call(|s| s.clone());
 +}
 +
 +struct Bar;
 +impl std::ops::Deref for Bar {
 +    type Target = str;
 +    fn deref(&self) -> &str {
 +        "hi"
 +    }
 +}
 +
 +fn test_deref_with_trait_method() {
 +    let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
 +}
 +
 +fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
 +    let mut res = Vec::new();
 +    let mut add_to_res = |n| res.push(n);
 +    x.into_iter().for_each(|x| add_to_res(x));
 +    y.into_iter().for_each(|x| add_to_res(x));
 +    z.into_iter().for_each(|x| add_to_res(x));
 +}
 +
 +fn mutable_closure_in_loop() {
 +    let mut value = 0;
 +    let mut closure = |n| value += n;
 +    for _ in 0..5 {
 +        Some(1).map(|n| closure(n));
++
++        let mut value = 0;
++        let mut in_loop = |n| value += n;
++        Some(1).map(|n| in_loop(n));
 +    }
 +}
 +
 +fn late_bound_lifetimes() {
 +    fn take_asref_path<P: AsRef<Path>>(path: P) {}
 +
 +    fn map_str<F>(thunk: F)
 +    where
 +        F: FnOnce(&str),
 +    {
 +    }
 +
 +    fn map_str_to_path<F>(thunk: F)
 +    where
 +        F: FnOnce(&str) -> &Path,
 +    {
 +    }
 +    map_str(|s| take_asref_path(s));
 +    map_str_to_path(|s| s.as_ref());
 +}
 +
 +mod type_param_bound {
 +    trait Trait {
 +        fn fun();
 +    }
 +
 +    fn take<T: 'static>(_: T) {}
 +
 +    fn test<X: Trait>() {
 +        // don't lint, but it's questionable that rust requires a cast
 +        take(|| X::fun());
 +        take(X::fun as fn());
 +    }
 +}
 +
 +// #8073 Don't replace closure with `Arc<F>` or `Rc<F>`
 +fn arc_fp() {
 +    let rc = std::rc::Rc::new(|| 7);
 +    let arc = std::sync::Arc::new(|n| n + 1);
 +    let ref_arc = &std::sync::Arc::new(|_| 5);
 +
 +    true.then(|| rc());
 +    (0..5).map(|n| arc(n));
 +    Some(4).map(|n| ref_arc(n));
 +}
 +
 +// #8460 Don't replace closures with params bounded as `ref`
 +mod bind_by_ref {
 +    struct A;
 +    struct B;
 +
 +    impl From<&A> for B {
 +        fn from(A: &A) -> Self {
 +            B
 +        }
 +    }
 +
 +    fn test() {
 +        // should not lint
 +        Some(A).map(|a| B::from(&a));
 +        // should not lint
 +        Some(A).map(|ref a| B::from(a));
 +    }
 +}
++
++// #7812 False positive on coerced closure
++fn coerced_closure() {
++    fn function_returning_unit<F: FnMut(i32)>(f: F) {}
++    function_returning_unit(|x| std::process::exit(x));
++
++    fn arr() -> &'static [u8; 0] {
++        &[]
++    }
++    fn slice_fn(_: impl FnOnce() -> &'static [u8]) {}
++    slice_fn(|| arr());
++}
index cda84982c9b7508020113a57acf2474077147971,0000000000000000000000000000000000000000..bf2e97e744ab348d639991e748d71df3d87c1b82
mode 100644,000000..100644
--- /dev/null
@@@ -1,126 -1,0 +1,120 @@@
- error: redundant closure
-   --> $DIR/eta.rs:40:27
-    |
- LL |     let e = Some(1u8).map(|a| divergent(a));
-    |                           ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `divergent`
 +error: redundant closure
 +  --> $DIR/eta.rs:28:27
 +   |
 +LL |     let a = Some(1u8).map(|a| foo(a));
 +   |                           ^^^^^^^^^^ help: replace the closure with the function itself: `foo`
 +   |
 +   = note: `-D clippy::redundant-closure` implied by `-D warnings`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:32:40
 +   |
 +LL |     let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
 +   |                                        ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:33:35
 +   |
 +LL |     let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
 +   |                                   ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:34:26
 +   |
 +LL |     all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
 +   |                          ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below`
 +
-   --> $DIR/eta.rs:232:21
 +error: redundant closure
 +  --> $DIR/eta.rs:41:27
 +   |
 +LL |     let e = Some(1u8).map(|a| generic(a));
 +   |                           ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:87:51
 +   |
 +LL |     let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
 +   |                                                   ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo`
 +   |
 +   = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:88:51
 +   |
 +LL |     let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
 +   |                                                   ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:90:42
 +   |
 +LL |     let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
 +   |                                          ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:94:29
 +   |
 +LL |     let e = Some("str").map(|s| s.to_string());
 +   |                             ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:95:27
 +   |
 +LL |     let e = Some('a').map(|s| s.to_uppercase());
 +   |                           ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:97:65
 +   |
 +LL |     let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
 +   |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:160:22
 +   |
 +LL |     requires_fn_once(|| x());
 +   |                      ^^^^^^ help: replace the closure with the function itself: `x`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:167:27
 +   |
 +LL |     let a = Some(1u8).map(|a| foo_ptr(a));
 +   |                           ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:172:27
 +   |
 +LL |     let a = Some(1u8).map(|a| closure(a));
 +   |                           ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:204:28
 +   |
 +LL |     x.into_iter().for_each(|x| add_to_res(x));
 +   |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:205:28
 +   |
 +LL |     y.into_iter().for_each(|x| add_to_res(x));
 +   |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:206:28
 +   |
 +LL |     z.into_iter().for_each(|x| add_to_res(x));
 +   |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:213:21
 +   |
 +LL |         Some(1).map(|n| closure(n));
 +   |                     ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
 +
 +error: redundant closure
- LL |     map_str_to_path(|s| s.as_ref());
-    |                     ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref`
++  --> $DIR/eta.rs:217:21
 +   |
- error: aborting due to 20 previous errors
++LL |         Some(1).map(|n| in_loop(n));
++   |                     ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop`
 +
++error: aborting due to 19 previous errors
 +
index 150acfbfee75996fd89b882784a26fcbc3a5e002,0000000000000000000000000000000000000000..f76127a7105fd5333dc9cca49947f943f394cf21
mode 100644,000000..100644
--- /dev/null
@@@ -1,75 -1,0 +1,117 @@@
 +#![allow(
 +    unused,
 +    dead_code,
 +    clippy::needless_lifetimes,
 +    clippy::needless_pass_by_value,
 +    clippy::needless_arbitrary_self_type
 +)]
 +#![warn(clippy::extra_unused_lifetimes)]
 +
 +fn empty() {}
 +
 +fn used_lt<'a>(x: &'a u8) {}
 +
 +fn unused_lt<'a>(x: u8) {}
 +
 +fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) {
 +    // 'a is useless here since it's not directly bound
 +}
 +
 +fn lt_return<'a, 'b: 'a>(x: &'b u8) -> &'a u8 {
 +    panic!()
 +}
 +
 +fn lt_return_only<'a>() -> &'a u8 {
 +    panic!()
 +}
 +
 +fn unused_lt_blergh<'a>(x: Option<Box<dyn Send + 'a>>) {}
 +
 +trait Foo<'a> {
 +    fn x(&self, a: &'a u8);
 +}
 +
 +impl<'a> Foo<'a> for u8 {
 +    fn x(&self, a: &'a u8) {}
 +}
 +
 +struct Bar;
 +
 +impl Bar {
 +    fn x<'a>(&self) {}
 +}
 +
 +// test for #489 (used lifetimes in bounds)
 +pub fn parse<'a, I: Iterator<Item = &'a str>>(_it: &mut I) {
 +    unimplemented!()
 +}
 +pub fn parse2<'a, I>(_it: &mut I)
 +where
 +    I: Iterator<Item = &'a str>,
 +{
 +    unimplemented!()
 +}
 +
 +struct X {
 +    x: u32,
 +}
 +
 +impl X {
 +    fn self_ref_with_lifetime<'a>(&'a self) {}
 +    fn explicit_self_with_lifetime<'a>(self: &'a Self) {}
 +}
 +
 +// Methods implementing traits must have matching lifetimes
 +mod issue4291 {
 +    trait BadTrait {
 +        fn unused_lt<'a>(x: u8) {}
 +    }
 +
 +    impl BadTrait for () {
 +        fn unused_lt<'a>(_x: u8) {}
 +    }
 +}
 +
++mod issue6437 {
++    pub struct Scalar;
++
++    impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar {
++        fn add_assign(&mut self, _rhs: &Scalar) {
++            unimplemented!();
++        }
++    }
++
++    impl<'b> Scalar {
++        pub fn something<'c>() -> Self {
++            Self
++        }
++    }
++}
++
++// https://github.com/rust-lang/rust-clippy/pull/8737#pullrequestreview-951268213
++mod first_case {
++    use serde::de::Visitor;
++    pub trait Expected {
++        fn fmt(&self, formatter: &mut std::fmt::Formatter);
++    }
++
++    impl<'de, T> Expected for T
++    where
++        T: Visitor<'de>,
++    {
++        fn fmt(&self, formatter: &mut std::fmt::Formatter) {}
++    }
++}
++
++// https://github.com/rust-lang/rust-clippy/pull/8737#pullrequestreview-951268213
++mod second_case {
++    pub trait Source {
++        fn hey();
++    }
++
++    impl<'a, T: Source + ?Sized + 'a> Source for Box<T> {
++        fn hey() {}
++    }
++}
++
 +fn main() {}
index 9143fb2c208a05604d86a10e3fdb59f569b7b3b6,0000000000000000000000000000000000000000..fcc12d4ce14b95977f4a5f46e4509b0b32dac7c6
mode 100644,000000..100644
--- /dev/null
@@@ -1,22 -1,0 +1,40 @@@
- error: aborting due to 3 previous errors
 +error: this lifetime isn't used in the function definition
 +  --> $DIR/extra_unused_lifetimes.rs:14:14
 +   |
 +LL | fn unused_lt<'a>(x: u8) {}
 +   |              ^^
 +   |
 +   = note: `-D clippy::extra-unused-lifetimes` implied by `-D warnings`
 +
 +error: this lifetime isn't used in the function definition
 +  --> $DIR/extra_unused_lifetimes.rs:41:10
 +   |
 +LL |     fn x<'a>(&self) {}
 +   |          ^^
 +
 +error: this lifetime isn't used in the function definition
 +  --> $DIR/extra_unused_lifetimes.rs:67:22
 +   |
 +LL |         fn unused_lt<'a>(x: u8) {}
 +   |                      ^^
 +
++error: this lifetime isn't used in the impl
++  --> $DIR/extra_unused_lifetimes.rs:78:10
++   |
++LL |     impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar {
++   |          ^^
++
++error: this lifetime isn't used in the impl
++  --> $DIR/extra_unused_lifetimes.rs:84:10
++   |
++LL |     impl<'b> Scalar {
++   |          ^^
++
++error: this lifetime isn't used in the function definition
++  --> $DIR/extra_unused_lifetimes.rs:85:26
++   |
++LL |         pub fn something<'c>() -> Self {
++   |                          ^^
++
++error: aborting due to 6 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4db13d650eb18f8b734e7b36e57ec9e3d7d40d30
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++#![warn(clippy::format_push_string)]
++
++fn main() {
++    let mut string = String::new();
++    string += &format!("{:?}", 1234);
++    string.push_str(&format!("{:?}", 5678));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..953784bcc0684748ba65d296931fb27fb8015c04
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++error: `format!(..)` appended to existing `String`
++  --> $DIR/format_push_string.rs:5:5
++   |
++LL |     string += &format!("{:?}", 1234);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::format-push-string` implied by `-D warnings`
++   = help: consider using `write!` to avoid the extra allocation
++
++error: `format!(..)` appended to existing `String`
++  --> $DIR/format_push_string.rs:6:5
++   |
++LL |     string.push_str(&format!("{:?}", 5678));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using `write!` to avoid the extra allocation
++
++error: aborting due to 2 previous errors
++
index edc3fe1aec13a556fa0c8c2121a1639553c57952,0000000000000000000000000000000000000000..fec54d00ccb4b8869b90a7c4cdb55e510f8ff543
mode 100644,000000..100644
--- /dev/null
@@@ -1,78 -1,0 +1,110 @@@
-         self.0.push_str(&format!("{}", other));
++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)
 +    }
 +}
 +
 +#[allow(
 +    clippy::eq_op,
 +    clippy::no_effect,
 +    clippy::unnecessary_operation,
 +    clippy::op_ref,
 +    clippy::double_parens
 +)]
 +#[warn(clippy::identity_op)]
 +#[rustfmt::skip]
 +fn main() {
 +    let x = 0;
 +
 +    x + 0;
 +    x + (1 - 1);
 +    x + 1;
 +    0 + x;
 +    1 + x;
 +    x - ZERO; //no error, as we skip lookups (for now)
 +    x | (0);
 +    ((ZERO)) | x; //no error, as we skip lookups (for now)
 +
 +    x * 1;
 +    1 * x;
 +    x / ONE; //no error, as we skip lookups (for now)
 +
 +    x / 2; //no false positive
 +
 +    x & NEG_ONE; //no error, as we skip lookups (for now)
 +    -1 & x;
 +
 +    let u: u8 = 0;
 +    u & 255;
 +
 +    1 << 0; // no error, this case is allowed, see issue 3430
 +    42 << 0;
 +    1 >> 0;
 +    42 >> 0;
 +    &x >> 0;
 +    x >> &0;
 +
 +    let mut a = A("".into());
 +    let b = a << 0; // no error: non-integer
 +
 +    1 * Meter; // no error: non-integer
 +
 +    2 % 3;
 +    -2 % 3;
 +    2 % -3 + x;
 +    -2 % -3 + x;
 +    x + 1 % 3;
 +    (x + 1) % 3; // no error
 +    4 % 3; // no error
 +    4 % -3; // no error
++
++    // See #8724
++    let a = 0;
++    let b = true;
++    0 + if b { 1 } else { 2 };
++    0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; // no error
++    0 + match a { 0 => 10, _ => 20 };
++    0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; // no error
++    0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; // no error
++    0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; // no error
++    
++    0 + if b { 0 + 1 } else { 2 };
++    0 + match a { 0 =>  0 + 10, _ => 20 };
++    0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 };
++
++    let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 };
++    let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1  { 0 => 30, _ => 40 };
++
++    0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0;
++    
++    0 + { a } + 3; // no error
++    0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; // no error
++    
++    fn f(_: i32) {
++        todo!();
++    }
++    f(1 * a + { 8 * 5 });
++    f(0 + if b { 1 } else { 2 } + 3); // no error
++    const _: i32 = { 2 * 4 } + 0 + 3;
++    const _: i32 = 0 + { 1 + 2 * 3 } + 3; // no error
 +}
index 706f01a3dd6c403b245ca8568cdb624def8f943a,0000000000000000000000000000000000000000..d8cb65839cbba5595b6c1cc422427ca30603983b
mode 100644,000000..100644
--- /dev/null
@@@ -1,112 -1,0 +1,202 @@@
-   --> $DIR/identity_op.rs:37:5
 +error: the operation is ineffective. Consider reducing it to `x`
-   --> $DIR/identity_op.rs:38:5
++  --> $DIR/identity_op.rs:39:5
 +   |
 +LL |     x + 0;
 +   |     ^^^^^
 +   |
 +   = note: `-D clippy::identity-op` implied by `-D warnings`
 +
 +error: the operation is ineffective. Consider reducing it to `x`
-   --> $DIR/identity_op.rs:40:5
++  --> $DIR/identity_op.rs:40:5
 +   |
 +LL |     x + (1 - 1);
 +   |     ^^^^^^^^^^^
 +
 +error: the operation is ineffective. Consider reducing it to `x`
-   --> $DIR/identity_op.rs:43:5
++  --> $DIR/identity_op.rs:42:5
 +   |
 +LL |     0 + x;
 +   |     ^^^^^
 +
 +error: the operation is ineffective. Consider reducing it to `x`
-   --> $DIR/identity_op.rs:46:5
++  --> $DIR/identity_op.rs:45:5
 +   |
 +LL |     x | (0);
 +   |     ^^^^^^^
 +
 +error: the operation is ineffective. Consider reducing it to `x`
-   --> $DIR/identity_op.rs:47:5
++  --> $DIR/identity_op.rs:48:5
 +   |
 +LL |     x * 1;
 +   |     ^^^^^
 +
 +error: the operation is ineffective. Consider reducing it to `x`
-   --> $DIR/identity_op.rs:53:5
++  --> $DIR/identity_op.rs:49:5
 +   |
 +LL |     1 * x;
 +   |     ^^^^^
 +
 +error: the operation is ineffective. Consider reducing it to `x`
-   --> $DIR/identity_op.rs:56:5
++  --> $DIR/identity_op.rs:55:5
 +   |
 +LL |     -1 & x;
 +   |     ^^^^^^
 +
 +error: the operation is ineffective. Consider reducing it to `u`
-   --> $DIR/identity_op.rs:59:5
++  --> $DIR/identity_op.rs:58:5
 +   |
 +LL |     u & 255;
 +   |     ^^^^^^^
 +
 +error: the operation is ineffective. Consider reducing it to `42`
-   --> $DIR/identity_op.rs:60:5
++  --> $DIR/identity_op.rs:61:5
 +   |
 +LL |     42 << 0;
 +   |     ^^^^^^^
 +
 +error: the operation is ineffective. Consider reducing it to `1`
-   --> $DIR/identity_op.rs:61:5
++  --> $DIR/identity_op.rs:62:5
 +   |
 +LL |     1 >> 0;
 +   |     ^^^^^^
 +
 +error: the operation is ineffective. Consider reducing it to `42`
-   --> $DIR/identity_op.rs:62:5
++  --> $DIR/identity_op.rs:63:5
 +   |
 +LL |     42 >> 0;
 +   |     ^^^^^^^
 +
 +error: the operation is ineffective. Consider reducing it to `&x`
-   --> $DIR/identity_op.rs:63:5
++  --> $DIR/identity_op.rs:64:5
 +   |
 +LL |     &x >> 0;
 +   |     ^^^^^^^
 +
 +error: the operation is ineffective. Consider reducing it to `x`
-   --> $DIR/identity_op.rs:70:5
++  --> $DIR/identity_op.rs:65:5
 +   |
 +LL |     x >> &0;
 +   |     ^^^^^^^
 +
 +error: the operation is ineffective. Consider reducing it to `2`
-   --> $DIR/identity_op.rs:71:5
++  --> $DIR/identity_op.rs:72:5
 +   |
 +LL |     2 % 3;
 +   |     ^^^^^
 +
 +error: the operation is ineffective. Consider reducing it to `-2`
-   --> $DIR/identity_op.rs:72:5
++  --> $DIR/identity_op.rs:73:5
 +   |
 +LL |     -2 % 3;
 +   |     ^^^^^^
 +
 +error: the operation is ineffective. Consider reducing it to `2`
-   --> $DIR/identity_op.rs:73:5
++  --> $DIR/identity_op.rs:74:5
 +   |
 +LL |     2 % -3 + x;
 +   |     ^^^^^^
 +
 +error: the operation is ineffective. Consider reducing it to `-2`
-   --> $DIR/identity_op.rs:74:9
++  --> $DIR/identity_op.rs:75:5
 +   |
 +LL |     -2 % -3 + x;
 +   |     ^^^^^^^
 +
 +error: the operation is ineffective. Consider reducing it to `1`
- error: aborting due to 18 previous errors
++  --> $DIR/identity_op.rs:76:9
 +   |
 +LL |     x + 1 % 3;
 +   |         ^^^^^
 +
++error: the operation is ineffective. Consider reducing it to `if b { 1 } else { 2 }`
++  --> $DIR/identity_op.rs:84:5
++   |
++LL |     0 + if b { 1 } else { 2 };
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the operation is ineffective. Consider reducing it to `match a { 0 => 10, _ => 20 }`
++  --> $DIR/identity_op.rs:86:5
++   |
++LL |     0 + match a { 0 => 10, _ => 20 };
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the operation is ineffective. Consider reducing it to `if b { 0 + 1 } else { 2 }`
++  --> $DIR/identity_op.rs:91:5
++   |
++LL |     0 + if b { 0 + 1 } else { 2 };
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the operation is ineffective. Consider reducing it to `1`
++  --> $DIR/identity_op.rs:91:16
++   |
++LL |     0 + if b { 0 + 1 } else { 2 };
++   |                ^^^^^
++
++error: the operation is ineffective. Consider reducing it to `match a { 0 =>  0 + 10, _ => 20 }`
++  --> $DIR/identity_op.rs:92:5
++   |
++LL |     0 + match a { 0 =>  0 + 10, _ => 20 };
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the operation is ineffective. Consider reducing it to `10`
++  --> $DIR/identity_op.rs:92:25
++   |
++LL |     0 + match a { 0 =>  0 + 10, _ => 20 };
++   |                         ^^^^^^
++
++error: the operation is ineffective. Consider reducing it to `1`
++  --> $DIR/identity_op.rs:93:16
++   |
++LL |     0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 };
++   |                ^^^^^
++
++error: the operation is ineffective. Consider reducing it to `30`
++  --> $DIR/identity_op.rs:93:52
++   |
++LL |     0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 };
++   |                                                    ^^^^^^
++
++error: the operation is ineffective. Consider reducing it to `1`
++  --> $DIR/identity_op.rs:95:20
++   |
++LL |     let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 };
++   |                    ^^^^^
++
++error: the operation is ineffective. Consider reducing it to `1`
++  --> $DIR/identity_op.rs:95:52
++   |
++LL |     let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 };
++   |                                                    ^^^^^
++
++error: the operation is ineffective. Consider reducing it to `1`
++  --> $DIR/identity_op.rs:96:23
++   |
++LL |     let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1  { 0 => 30, _ => 40 };
++   |                       ^^^^^
++
++error: the operation is ineffective. Consider reducing it to `1`
++  --> $DIR/identity_op.rs:96:58
++   |
++LL |     let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1  { 0 => 30, _ => 40 };
++   |                                                          ^^^^^
++
++error: the operation is ineffective. Consider reducing it to `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }`
++  --> $DIR/identity_op.rs:98:5
++   |
++LL |     0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the operation is ineffective. Consider reducing it to `a`
++  --> $DIR/identity_op.rs:106:7
++   |
++LL |     f(1 * a + { 8 * 5 });
++   |       ^^^^^
++
++error: the operation is ineffective. Consider reducing it to `{ 2 * 4 }`
++  --> $DIR/identity_op.rs:108:20
++   |
++LL |     const _: i32 = { 2 * 4 } + 0 + 3;
++   |                    ^^^^^^^^^^^^^
++
++error: aborting due to 33 previous errors
 +
index 39443775015365d7197d1fb036b4b35bd66bdb4f,0000000000000000000000000000000000000000..aea52a852f987d6ab86d899240c9ed81cd3cddc3
mode 100644,000000..100644
--- /dev/null
@@@ -1,67 -1,0 +1,67 @@@
- #![allow(dead_code)]
++#![allow(dead_code, clippy::extra_unused_lifetimes)]
 +#![warn(clippy::multiple_inherent_impl)]
 +
 +struct MyStruct;
 +
 +impl MyStruct {
 +    fn first() {}
 +}
 +
 +impl MyStruct {
 +    fn second() {}
 +}
 +
 +impl<'a> MyStruct {
 +    fn lifetimed() {}
 +}
 +
 +mod submod {
 +    struct MyStruct;
 +    impl MyStruct {
 +        fn other() {}
 +    }
 +
 +    impl super::MyStruct {
 +        fn third() {}
 +    }
 +}
 +
 +use std::fmt;
 +impl fmt::Debug for MyStruct {
 +    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 +        write!(f, "MyStruct {{ }}")
 +    }
 +}
 +
 +// issue #5772
 +struct WithArgs<T>(T);
 +impl WithArgs<u32> {
 +    fn f1() {}
 +}
 +impl WithArgs<u64> {
 +    fn f2() {}
 +}
 +impl WithArgs<u64> {
 +    fn f3() {}
 +}
 +
 +// Ok, the struct is allowed to have multiple impls.
 +#[allow(clippy::multiple_inherent_impl)]
 +struct Allowed;
 +impl Allowed {}
 +impl Allowed {}
 +impl Allowed {}
 +
 +struct AllowedImpl;
 +#[allow(clippy::multiple_inherent_impl)]
 +impl AllowedImpl {}
 +// Ok, the first block is skipped by this lint.
 +impl AllowedImpl {}
 +
 +struct OneAllowedImpl;
 +impl OneAllowedImpl {}
 +#[allow(clippy::multiple_inherent_impl)]
 +impl OneAllowedImpl {}
 +impl OneAllowedImpl {} // Lint, only one of the three blocks is allowed.
 +
 +fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c0ba647d707918cbef380c7d9a0cbb7fe02e83d0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++// run-rustfix
++
++#![warn(clippy::is_digit_ascii_radix)]
++
++const TEN: u32 = 10;
++
++fn main() {
++    let c: char = '6';
++
++    // Should trigger the lint.
++    let _ = c.is_ascii_digit();
++    let _ = c.is_ascii_hexdigit();
++    let _ = c.is_ascii_hexdigit();
++
++    // Should not trigger the lint.
++    let _ = c.is_digit(11);
++    let _ = c.is_digit(TEN);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..68e3f3243d96dabb8f47af2cd321a3ad04d66989
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++// run-rustfix
++
++#![warn(clippy::is_digit_ascii_radix)]
++
++const TEN: u32 = 10;
++
++fn main() {
++    let c: char = '6';
++
++    // Should trigger the lint.
++    let _ = c.is_digit(10);
++    let _ = c.is_digit(16);
++    let _ = c.is_digit(0x10);
++
++    // Should not trigger the lint.
++    let _ = c.is_digit(11);
++    let _ = c.is_digit(TEN);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dc5cb2913ae151c463aba06fb78cf21da04b3914
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: use of `char::is_digit` with literal radix of 10
++  --> $DIR/is_digit_ascii_radix.rs:11:13
++   |
++LL |     let _ = c.is_digit(10);
++   |             ^^^^^^^^^^^^^^ help: try: `c.is_ascii_digit()`
++   |
++   = note: `-D clippy::is-digit-ascii-radix` implied by `-D warnings`
++
++error: use of `char::is_digit` with literal radix of 16
++  --> $DIR/is_digit_ascii_radix.rs:12:13
++   |
++LL |     let _ = c.is_digit(16);
++   |             ^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()`
++
++error: use of `char::is_digit` with literal radix of 16
++  --> $DIR/is_digit_ascii_radix.rs:13:13
++   |
++LL |     let _ = c.is_digit(0x10);
++   |             ^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()`
++
++error: aborting due to 3 previous errors
++
index 56761ebbcb80bb0e5dcd595ba3ca0194b41829e2,0000000000000000000000000000000000000000..7c2b05d837ba89828bff9c637927f78e54cbc2b6
mode 100644,000000..100644
--- /dev/null
@@@ -1,51 -1,0 +1,51 @@@
- #![allow(dead_code)]
 +// run-rustfix
 +#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
++#![allow(dead_code, clippy::let_unit_value)]
 +
 +fn main() {
 +    let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()];
 +
 +    let _: Option<String> = vec.iter().last().cloned();
 +
 +    let _: Option<String> = vec.iter().chain(vec.iter()).next().cloned();
 +
 +    let _: usize = vec.iter().filter(|x| x == &"2").count();
 +
 +    let _: Vec<_> = vec.iter().take(2).cloned().collect();
 +
 +    let _: Vec<_> = vec.iter().skip(2).cloned().collect();
 +
 +    let _ = vec.iter().filter(|x| x == &"2").nth(2).cloned();
 +
 +    let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))]
 +        .iter().flatten().cloned();
 +
 +    // Not implemented yet
 +    let _ = vec.iter().cloned().filter(|x| x.starts_with('2'));
 +
 +    // Not implemented yet
 +    let _ = vec.iter().cloned().map(|x| x.len());
 +
 +    // This would fail if changed.
 +    let _ = vec.iter().cloned().map(|x| x + "2");
 +
 +    // Not implemented yet
 +    let _ = vec.iter().cloned().find(|x| x == "2");
 +
 +    // Not implemented yet
 +    let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty()));
 +
 +    // Not implemented yet
 +    let _ = vec.iter().cloned().all(|x| x.len() == 1);
 +
 +    // Not implemented yet
 +    let _ = vec.iter().cloned().any(|x| x.len() == 1);
 +
 +    // Should probably stay as it is.
 +    let _ = [0, 1, 2, 3, 4].iter().cloned().take(10);
 +}
 +
 +// #8527
 +fn cloned_flatten(x: Option<&Option<String>>) -> Option<String> {
 +    x.cloned().flatten()
 +}
index 98321d889b58273fd78a023d8933ad3af3f53074,0000000000000000000000000000000000000000..f2d0b155d2c2aa20553caee5e59618d6fc1e923f
mode 100644,000000..100644
--- /dev/null
@@@ -1,53 -1,0 +1,53 @@@
- #![allow(dead_code)]
 +// run-rustfix
 +#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
++#![allow(dead_code, clippy::let_unit_value)]
 +
 +fn main() {
 +    let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()];
 +
 +    let _: Option<String> = vec.iter().cloned().last();
 +
 +    let _: Option<String> = vec.iter().chain(vec.iter()).cloned().next();
 +
 +    let _: usize = vec.iter().filter(|x| x == &"2").cloned().count();
 +
 +    let _: Vec<_> = vec.iter().cloned().take(2).collect();
 +
 +    let _: Vec<_> = vec.iter().cloned().skip(2).collect();
 +
 +    let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2);
 +
 +    let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))]
 +        .iter()
 +        .cloned()
 +        .flatten();
 +
 +    // Not implemented yet
 +    let _ = vec.iter().cloned().filter(|x| x.starts_with('2'));
 +
 +    // Not implemented yet
 +    let _ = vec.iter().cloned().map(|x| x.len());
 +
 +    // This would fail if changed.
 +    let _ = vec.iter().cloned().map(|x| x + "2");
 +
 +    // Not implemented yet
 +    let _ = vec.iter().cloned().find(|x| x == "2");
 +
 +    // Not implemented yet
 +    let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty()));
 +
 +    // Not implemented yet
 +    let _ = vec.iter().cloned().all(|x| x.len() == 1);
 +
 +    // Not implemented yet
 +    let _ = vec.iter().cloned().any(|x| x.len() == 1);
 +
 +    // Should probably stay as it is.
 +    let _ = [0, 1, 2, 3, 4].iter().cloned().take(10);
 +}
 +
 +// #8527
 +fn cloned_flatten(x: Option<&Option<String>>) -> Option<String> {
 +    x.cloned().flatten()
 +}
index aea4dba9dd5a0166a422fb921b3efffb183c4684,0000000000000000000000000000000000000000..0330d5549264a122a1c43deee1dfbe41dcea55a3
mode 100644,000000..100644
--- /dev/null
@@@ -1,56 -1,0 +1,65 @@@
 +// run-rustfix
 +// will emits unused mut warnings after fixing
 +#![allow(unused_mut)]
 +// will emits needless collect warnings after fixing
 +#![allow(clippy::needless_collect)]
 +#![warn(clippy::iter_with_drain)]
 +use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
 +
 +fn full() {
 +    let mut a = vec!["aaa".to_string(), "bbb".to_string()];
 +    let mut a: BinaryHeap<_> = a.into_iter().collect();
 +    let mut a: HashSet<_> = a.drain().collect();
 +    let mut a: VecDeque<_> = a.drain().collect();
 +    let mut a: Vec<_> = a.into_iter().collect();
 +    let mut a: HashMap<_, _> = a.into_iter().map(|x| (x.clone(), x)).collect();
 +    let _: Vec<(String, String)> = a.drain().collect();
 +}
 +
 +fn closed() {
 +    let mut a = vec!["aaa".to_string(), "bbb".to_string()];
 +    let mut a: BinaryHeap<_> = a.into_iter().collect();
 +    let mut a: HashSet<_> = a.drain().collect();
 +    let mut a: VecDeque<_> = a.drain().collect();
 +    let mut a: Vec<_> = a.into_iter().collect();
 +    let mut a: HashMap<_, _> = a.into_iter().map(|x| (x.clone(), x)).collect();
 +    let _: Vec<(String, String)> = a.drain().collect();
 +}
 +
 +fn should_not_help() {
 +    let mut a = vec!["aaa".to_string(), "bbb".to_string()];
 +    let mut a: BinaryHeap<_> = a.drain(1..).collect();
 +    let mut a: HashSet<_> = a.drain().collect();
 +    let mut a: VecDeque<_> = a.drain().collect();
 +    let mut a: Vec<_> = a.drain(..a.len() - 1).collect();
 +    let mut a: HashMap<_, _> = a.drain(1..a.len() - 1).map(|x| (x.clone(), x)).collect();
 +    let _: Vec<(String, String)> = a.drain().collect();
 +
 +    let mut b = vec!["aaa".to_string(), "bbb".to_string()];
 +    let _: Vec<_> = b.drain(0..a.len()).collect();
 +}
 +
++fn _closed_range(mut x: Vec<String>) {
++    let _: Vec<String> = x.drain(0..=x.len()).collect();
++}
++
++fn _with_mut(x: &mut Vec<String>, y: &mut VecDeque<String>) {
++    let _: Vec<String> = x.drain(..).collect();
++    let _: Vec<String> = y.drain(..).collect();
++}
++
 +#[derive(Default)]
 +struct Bomb {
 +    fire: Vec<u8>,
 +}
 +
 +fn should_not_help_0(bomb: &mut Bomb) {
 +    let _: Vec<u8> = bomb.fire.drain(..).collect();
 +}
 +
 +fn main() {
 +    full();
 +    closed();
 +    should_not_help();
 +    should_not_help_0(&mut Bomb::default());
 +}
index 271878cffb44ccc8fcd7f1644ec24bbc6bc92dd7,0000000000000000000000000000000000000000..993936fb8de3d5b345ebcd2ef233e27c24f08420
mode 100644,000000..100644
--- /dev/null
@@@ -1,56 -1,0 +1,65 @@@
 +// run-rustfix
 +// will emits unused mut warnings after fixing
 +#![allow(unused_mut)]
 +// will emits needless collect warnings after fixing
 +#![allow(clippy::needless_collect)]
 +#![warn(clippy::iter_with_drain)]
 +use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
 +
 +fn full() {
 +    let mut a = vec!["aaa".to_string(), "bbb".to_string()];
 +    let mut a: BinaryHeap<_> = a.drain(..).collect();
 +    let mut a: HashSet<_> = a.drain().collect();
 +    let mut a: VecDeque<_> = a.drain().collect();
 +    let mut a: Vec<_> = a.drain(..).collect();
 +    let mut a: HashMap<_, _> = a.drain(..).map(|x| (x.clone(), x)).collect();
 +    let _: Vec<(String, String)> = a.drain().collect();
 +}
 +
 +fn closed() {
 +    let mut a = vec!["aaa".to_string(), "bbb".to_string()];
 +    let mut a: BinaryHeap<_> = a.drain(0..).collect();
 +    let mut a: HashSet<_> = a.drain().collect();
 +    let mut a: VecDeque<_> = a.drain().collect();
 +    let mut a: Vec<_> = a.drain(..a.len()).collect();
 +    let mut a: HashMap<_, _> = a.drain(0..a.len()).map(|x| (x.clone(), x)).collect();
 +    let _: Vec<(String, String)> = a.drain().collect();
 +}
 +
 +fn should_not_help() {
 +    let mut a = vec!["aaa".to_string(), "bbb".to_string()];
 +    let mut a: BinaryHeap<_> = a.drain(1..).collect();
 +    let mut a: HashSet<_> = a.drain().collect();
 +    let mut a: VecDeque<_> = a.drain().collect();
 +    let mut a: Vec<_> = a.drain(..a.len() - 1).collect();
 +    let mut a: HashMap<_, _> = a.drain(1..a.len() - 1).map(|x| (x.clone(), x)).collect();
 +    let _: Vec<(String, String)> = a.drain().collect();
 +
 +    let mut b = vec!["aaa".to_string(), "bbb".to_string()];
 +    let _: Vec<_> = b.drain(0..a.len()).collect();
 +}
 +
++fn _closed_range(mut x: Vec<String>) {
++    let _: Vec<String> = x.drain(0..=x.len()).collect();
++}
++
++fn _with_mut(x: &mut Vec<String>, y: &mut VecDeque<String>) {
++    let _: Vec<String> = x.drain(..).collect();
++    let _: Vec<String> = y.drain(..).collect();
++}
++
 +#[derive(Default)]
 +struct Bomb {
 +    fire: Vec<u8>,
 +}
 +
 +fn should_not_help_0(bomb: &mut Bomb) {
 +    let _: Vec<u8> = bomb.fire.drain(..).collect();
 +}
 +
 +fn main() {
 +    full();
 +    closed();
 +    should_not_help();
 +    should_not_help_0(&mut Bomb::default());
 +}
index 50744f81c3cf84893697292fc190bb496b7a8f60,0000000000000000000000000000000000000000..11b50492ab29055ec6223164a2a768a53b9083a8
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,28 @@@
 +#![warn(clippy::let_underscore_drop)]
++#![allow(clippy::let_unit_value)]
 +
 +struct Droppable;
 +
 +impl Drop for Droppable {
 +    fn drop(&mut self) {}
 +}
 +
 +fn main() {
 +    let unit = ();
 +    let boxed = Box::new(());
 +    let droppable = Droppable;
 +    let optional = Some(Droppable);
 +
 +    let _ = ();
 +    let _ = Box::new(());
 +    let _ = Droppable;
 +    let _ = Some(Droppable);
 +
 +    // no lint for reference
 +    let _ = droppable_ref();
 +}
 +
 +#[must_use]
 +fn droppable_ref() -> &'static mut Droppable {
 +    unimplemented!()
 +}
index 66069e0c5e13f00a8d524c99351a2f7363da0c89,0000000000000000000000000000000000000000..ee7bbe995f1684e28bbf064d9cd79cdb2e6c4257
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,27 @@@
-   --> $DIR/let_underscore_drop.rs:16:5
 +error: non-binding `let` on a type that implements `Drop`
-   --> $DIR/let_underscore_drop.rs:17:5
++  --> $DIR/let_underscore_drop.rs:17:5
 +   |
 +LL |     let _ = Box::new(());
 +   |     ^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::let-underscore-drop` implied by `-D warnings`
 +   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 +
 +error: non-binding `let` on a type that implements `Drop`
-   --> $DIR/let_underscore_drop.rs:18:5
++  --> $DIR/let_underscore_drop.rs:18:5
 +   |
 +LL |     let _ = Droppable;
 +   |     ^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 +
 +error: non-binding `let` on a type that implements `Drop`
++  --> $DIR/let_underscore_drop.rs:19:5
 +   |
 +LL |     let _ = Some(Droppable);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 +
 +error: aborting due to 3 previous errors
 +
index f398edc23cb5e0bdd019bfea348570872fb5d39d,0000000000000000000000000000000000000000..e72b746232551b87861e2783cf5ce3d7d76dfd21
mode 100644,000000..100644
--- /dev/null
@@@ -1,63 -1,0 +1,115 @@@
 +// run-rustfix
 +
 +#![warn(clippy::let_unit_value)]
 +#![allow(clippy::no_effect)]
 +#![allow(unused_variables)]
 +
 +macro_rules! let_and_return {
 +    ($n:expr) => {{
 +        let ret = $n;
 +    }};
 +}
 +
 +fn main() {
 +    println!("x");
 +    let _y = 1; // this is fine
 +    let _z = ((), 1); // this as well
 +    if true {
 +        ();
 +    }
 +
 +    consume_units_with_for_loop(); // should be fine as well
 +
 +    multiline_sugg();
 +
 +    let_and_return!(()) // should be fine
 +}
 +
 +// Related to issue #1964
 +fn consume_units_with_for_loop() {
 +    // `for_let_unit` lint should not be triggered by consuming them using for loop.
 +    let v = vec![(), (), ()];
 +    let mut count = 0;
 +    for _ in v {
 +        count += 1;
 +    }
 +    assert_eq!(count, 3);
 +
 +    // Same for consuming from some other Iterator<Item = ()>.
 +    let (tx, rx) = ::std::sync::mpsc::channel();
 +    tx.send(()).unwrap();
 +    drop(tx);
 +
 +    count = 0;
 +    for _ in rx.iter() {
 +        count += 1;
 +    }
 +    assert_eq!(count, 1);
 +}
 +
 +fn multiline_sugg() {
 +    let v: Vec<u8> = vec![2];
 +
 +    v
 +        .into_iter()
 +        .map(|i| i * 2)
 +        .filter(|i| i % 2 == 0)
 +        .map(|_| ())
 +        .next()
 +        .unwrap();
 +}
 +
 +#[derive(Copy, Clone)]
 +pub struct ContainsUnit(()); // should be fine
++
++fn _returns_generic() {
++    fn f<T>() -> T {
++        unimplemented!()
++    }
++    fn f2<T, U>(_: T) -> U {
++        unimplemented!()
++    }
++    fn f3<T>(x: T) -> T {
++        x
++    }
++    fn f4<T>(mut x: Vec<T>) -> T {
++        x.pop().unwrap()
++    }
++
++    let _: () = f(); // Ok
++    let _: () = f(); // Lint.
++
++    let _: () = f2(0i32); // Ok
++    let _: () = f2(0i32); // Lint.
++
++    f3(()); // Lint
++    f3(()); // Lint
++
++    f4(vec![()]); // Lint
++    f4(vec![()]); // Lint
++
++    // Ok
++    let _: () = {
++        let x = 5;
++        f2(x)
++    };
++
++    let _: () = if true { f() } else { f2(0) }; // Ok
++    let _: () = if true { f() } else { f2(0) }; // Lint
++
++    // Ok
++    let _: () = match Some(0) {
++        None => f2(1),
++        Some(0) => f(),
++        Some(1) => f2(3),
++        Some(_) => f2('x'),
++    };
++
++    // Lint
++    match Some(0) {
++        None => f2(1),
++        Some(0) => f(),
++        Some(1) => f2(3),
++        Some(_) => (),
++    };
++}
index af5b1fb2ac7e4d526eb6eaa47025d75db8a7992d,0000000000000000000000000000000000000000..47ee0a76724792445b8da0bd6465355b43fafb73
mode 100644,000000..100644
--- /dev/null
@@@ -1,63 -1,0 +1,115 @@@
 +// run-rustfix
 +
 +#![warn(clippy::let_unit_value)]
 +#![allow(clippy::no_effect)]
 +#![allow(unused_variables)]
 +
 +macro_rules! let_and_return {
 +    ($n:expr) => {{
 +        let ret = $n;
 +    }};
 +}
 +
 +fn main() {
 +    let _x = println!("x");
 +    let _y = 1; // this is fine
 +    let _z = ((), 1); // this as well
 +    if true {
 +        let _a = ();
 +    }
 +
 +    consume_units_with_for_loop(); // should be fine as well
 +
 +    multiline_sugg();
 +
 +    let_and_return!(()) // should be fine
 +}
 +
 +// Related to issue #1964
 +fn consume_units_with_for_loop() {
 +    // `for_let_unit` lint should not be triggered by consuming them using for loop.
 +    let v = vec![(), (), ()];
 +    let mut count = 0;
 +    for _ in v {
 +        count += 1;
 +    }
 +    assert_eq!(count, 3);
 +
 +    // Same for consuming from some other Iterator<Item = ()>.
 +    let (tx, rx) = ::std::sync::mpsc::channel();
 +    tx.send(()).unwrap();
 +    drop(tx);
 +
 +    count = 0;
 +    for _ in rx.iter() {
 +        count += 1;
 +    }
 +    assert_eq!(count, 1);
 +}
 +
 +fn multiline_sugg() {
 +    let v: Vec<u8> = vec![2];
 +
 +    let _ = v
 +        .into_iter()
 +        .map(|i| i * 2)
 +        .filter(|i| i % 2 == 0)
 +        .map(|_| ())
 +        .next()
 +        .unwrap();
 +}
 +
 +#[derive(Copy, Clone)]
 +pub struct ContainsUnit(()); // should be fine
++
++fn _returns_generic() {
++    fn f<T>() -> T {
++        unimplemented!()
++    }
++    fn f2<T, U>(_: T) -> U {
++        unimplemented!()
++    }
++    fn f3<T>(x: T) -> T {
++        x
++    }
++    fn f4<T>(mut x: Vec<T>) -> T {
++        x.pop().unwrap()
++    }
++
++    let _: () = f(); // Ok
++    let x: () = f(); // Lint.
++
++    let _: () = f2(0i32); // Ok
++    let x: () = f2(0i32); // Lint.
++
++    let _: () = f3(()); // Lint
++    let x: () = f3(()); // Lint
++
++    let _: () = f4(vec![()]); // Lint
++    let x: () = f4(vec![()]); // Lint
++
++    // Ok
++    let _: () = {
++        let x = 5;
++        f2(x)
++    };
++
++    let _: () = if true { f() } else { f2(0) }; // Ok
++    let x: () = if true { f() } else { f2(0) }; // Lint
++
++    // Ok
++    let _: () = match Some(0) {
++        None => f2(1),
++        Some(0) => f(),
++        Some(1) => f2(3),
++        Some(_) => f2('x'),
++    };
++
++    // Lint
++    let _: () = match Some(0) {
++        None => f2(1),
++        Some(0) => f(),
++        Some(1) => f2(3),
++        Some(_) => (),
++    };
++}
index f2600c6c22883ef6f28434f10efa95a7c8268eea,0000000000000000000000000000000000000000..13ec11a6d33e9a9c12cc06f9a8e37facaab385f3
mode 100644,000000..100644
--- /dev/null
@@@ -1,38 -1,0 +1,107 @@@
- error: aborting due to 3 previous errors
 +error: this let-binding has unit value
 +  --> $DIR/let_unit.rs:14:5
 +   |
 +LL |     let _x = println!("x");
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `println!("x");`
 +   |
 +   = note: `-D clippy::let-unit-value` implied by `-D warnings`
 +
 +error: this let-binding has unit value
 +  --> $DIR/let_unit.rs:18:9
 +   |
 +LL |         let _a = ();
 +   |         ^^^^^^^^^^^^ help: omit the `let` binding: `();`
 +
 +error: this let-binding has unit value
 +  --> $DIR/let_unit.rs:53:5
 +   |
 +LL | /     let _ = v
 +LL | |         .into_iter()
 +LL | |         .map(|i| i * 2)
 +LL | |         .filter(|i| i % 2 == 0)
 +LL | |         .map(|_| ())
 +LL | |         .next()
 +LL | |         .unwrap();
 +   | |__________________^
 +   |
 +help: omit the `let` binding
 +   |
 +LL ~     v
 +LL +         .into_iter()
 +LL +         .map(|i| i * 2)
 +LL +         .filter(|i| i % 2 == 0)
 +LL +         .map(|_| ())
 +LL +         .next()
 + ...
 +
++error: this let-binding has unit value
++  --> $DIR/let_unit.rs:80:5
++   |
++LL |     let x: () = f(); // Lint.
++   |     ^^^^-^^^^^^^^^^^
++   |         |
++   |         help: use a wild (`_`) binding: `_`
++
++error: this let-binding has unit value
++  --> $DIR/let_unit.rs:83:5
++   |
++LL |     let x: () = f2(0i32); // Lint.
++   |     ^^^^-^^^^^^^^^^^^^^^^
++   |         |
++   |         help: use a wild (`_`) binding: `_`
++
++error: this let-binding has unit value
++  --> $DIR/let_unit.rs:85:5
++   |
++LL |     let _: () = f3(()); // Lint
++   |     ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());`
++
++error: this let-binding has unit value
++  --> $DIR/let_unit.rs:86:5
++   |
++LL |     let x: () = f3(()); // Lint
++   |     ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());`
++
++error: this let-binding has unit value
++  --> $DIR/let_unit.rs:88:5
++   |
++LL |     let _: () = f4(vec![()]); // Lint
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);`
++
++error: this let-binding has unit value
++  --> $DIR/let_unit.rs:89:5
++   |
++LL |     let x: () = f4(vec![()]); // Lint
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);`
++
++error: this let-binding has unit value
++  --> $DIR/let_unit.rs:98:5
++   |
++LL |     let x: () = if true { f() } else { f2(0) }; // Lint
++   |     ^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |         |
++   |         help: use a wild (`_`) binding: `_`
++
++error: this let-binding has unit value
++  --> $DIR/let_unit.rs:109:5
++   |
++LL | /     let _: () = match Some(0) {
++LL | |         None => f2(1),
++LL | |         Some(0) => f(),
++LL | |         Some(1) => f2(3),
++LL | |         Some(_) => (),
++LL | |     };
++   | |______^
++   |
++help: omit the `let` binding
++   |
++LL ~     match Some(0) {
++LL +         None => f2(1),
++LL +         Some(0) => f(),
++LL +         Some(1) => f2(3),
++LL +         Some(_) => (),
++LL +     };
++   |
++
++error: aborting due to 11 previous errors
 +
index 4f1b19b75b8a1014d388b2a7097b3e34d7dc8b8c,0000000000000000000000000000000000000000..386360dbdcdb8af4e1b91f9039343a26e7d5601e
mode 100644,000000..100644
--- /dev/null
@@@ -1,48 -1,0 +1,59 @@@
- #![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)]
 +// run-rustfix
 +
 +#![warn(clippy::manual_bits)]
-     i8::BITS;
-     i16::BITS;
-     i32::BITS;
-     i64::BITS;
-     i128::BITS;
-     isize::BITS;
-     u8::BITS;
-     u16::BITS;
-     u32::BITS;
-     u64::BITS;
-     u128::BITS;
-     usize::BITS;
-     i8::BITS;
-     i16::BITS;
-     i32::BITS;
-     i64::BITS;
-     i128::BITS;
-     isize::BITS;
-     u8::BITS;
-     u16::BITS;
-     u32::BITS;
-     u64::BITS;
-     u128::BITS;
-     usize::BITS;
++#![allow(
++    clippy::no_effect,
++    clippy::useless_conversion,
++    path_statements,
++    unused_must_use,
++    clippy::unnecessary_operation
++)]
 +
 +use std::mem::{size_of, size_of_val};
 +
 +fn main() {
-     Word::BITS;
++    i8::BITS as usize;
++    i16::BITS as usize;
++    i32::BITS as usize;
++    i64::BITS as usize;
++    i128::BITS as usize;
++    isize::BITS as usize;
++
++    u8::BITS as usize;
++    u16::BITS as usize;
++    u32::BITS as usize;
++    u64::BITS as usize;
++    u128::BITS as usize;
++    usize::BITS as usize;
++
++    i8::BITS as usize;
++    i16::BITS as usize;
++    i32::BITS as usize;
++    i64::BITS as usize;
++    i128::BITS as usize;
++    isize::BITS as usize;
++
++    u8::BITS as usize;
++    u16::BITS as usize;
++    u32::BITS as usize;
++    u64::BITS as usize;
++    u128::BITS as usize;
++    usize::BITS as usize;
 +
 +    size_of::<usize>() * 4;
 +    4 * size_of::<usize>();
 +    size_of::<bool>() * 8;
 +    8 * size_of::<bool>();
 +
 +    size_of_val(&0u32) * 8;
 +
 +    type Word = u32;
++    Word::BITS as usize;
 +    type Bool = bool;
 +    size_of::<Bool>() * 8;
++
++    let _: u32 = u128::BITS as u32;
++    let _: u32 = u128::BITS.try_into().unwrap();
++    let _ = (u128::BITS as usize).pow(5);
++    let _ = &(u128::BITS as usize);
 +}
index f8a01313e6ad02117bfb9c3a7bb69a7430878d73,0000000000000000000000000000000000000000..62638f047eb015b299d9b8e45afd4489b9b9a364
mode 100644,000000..100644
--- /dev/null
@@@ -1,48 -1,0 +1,59 @@@
- #![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)]
 +// run-rustfix
 +
 +#![warn(clippy::manual_bits)]
++#![allow(
++    clippy::no_effect,
++    clippy::useless_conversion,
++    path_statements,
++    unused_must_use,
++    clippy::unnecessary_operation
++)]
 +
 +use std::mem::{size_of, size_of_val};
 +
 +fn main() {
 +    size_of::<i8>() * 8;
 +    size_of::<i16>() * 8;
 +    size_of::<i32>() * 8;
 +    size_of::<i64>() * 8;
 +    size_of::<i128>() * 8;
 +    size_of::<isize>() * 8;
 +
 +    size_of::<u8>() * 8;
 +    size_of::<u16>() * 8;
 +    size_of::<u32>() * 8;
 +    size_of::<u64>() * 8;
 +    size_of::<u128>() * 8;
 +    size_of::<usize>() * 8;
 +
 +    8 * size_of::<i8>();
 +    8 * size_of::<i16>();
 +    8 * size_of::<i32>();
 +    8 * size_of::<i64>();
 +    8 * size_of::<i128>();
 +    8 * size_of::<isize>();
 +
 +    8 * size_of::<u8>();
 +    8 * size_of::<u16>();
 +    8 * size_of::<u32>();
 +    8 * size_of::<u64>();
 +    8 * size_of::<u128>();
 +    8 * size_of::<usize>();
 +
 +    size_of::<usize>() * 4;
 +    4 * size_of::<usize>();
 +    size_of::<bool>() * 8;
 +    8 * size_of::<bool>();
 +
 +    size_of_val(&0u32) * 8;
 +
 +    type Word = u32;
 +    size_of::<Word>() * 8;
 +    type Bool = bool;
 +    size_of::<Bool>() * 8;
++
++    let _: u32 = (size_of::<u128>() * 8) as u32;
++    let _: u32 = (size_of::<u128>() * 8).try_into().unwrap();
++    let _ = (size_of::<u128>() * 8).pow(5);
++    let _ = &(size_of::<u128>() * 8);
 +}
index c4f5af2dcb0ec464192b497776482ac0f36ecc2b,0000000000000000000000000000000000000000..69c591a203d3f2c03eb3ccda6690f35192977126
mode 100644,000000..100644
--- /dev/null
@@@ -1,154 -1,0 +1,178 @@@
-   --> $DIR/manual_bits.rs:9:5
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS`
++  --> $DIR/manual_bits.rs:15:5
 +   |
 +LL |     size_of::<i8>() * 8;
-   --> $DIR/manual_bits.rs:10:5
++   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize`
 +   |
 +   = note: `-D clippy::manual-bits` implied by `-D warnings`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS`
++  --> $DIR/manual_bits.rs:16:5
 +   |
 +LL |     size_of::<i16>() * 8;
-   --> $DIR/manual_bits.rs:11:5
++   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS`
++  --> $DIR/manual_bits.rs:17:5
 +   |
 +LL |     size_of::<i32>() * 8;
-   --> $DIR/manual_bits.rs:12:5
++   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS`
++  --> $DIR/manual_bits.rs:18:5
 +   |
 +LL |     size_of::<i64>() * 8;
-   --> $DIR/manual_bits.rs:13:5
++   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS`
++  --> $DIR/manual_bits.rs:19:5
 +   |
 +LL |     size_of::<i128>() * 8;
-   --> $DIR/manual_bits.rs:14:5
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS`
++  --> $DIR/manual_bits.rs:20:5
 +   |
 +LL |     size_of::<isize>() * 8;
-   --> $DIR/manual_bits.rs:16:5
++   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS`
++  --> $DIR/manual_bits.rs:22:5
 +   |
 +LL |     size_of::<u8>() * 8;
-   --> $DIR/manual_bits.rs:17:5
++   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS`
++  --> $DIR/manual_bits.rs:23:5
 +   |
 +LL |     size_of::<u16>() * 8;
-   --> $DIR/manual_bits.rs:18:5
++   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS`
++  --> $DIR/manual_bits.rs:24:5
 +   |
 +LL |     size_of::<u32>() * 8;
-   --> $DIR/manual_bits.rs:19:5
++   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS`
++  --> $DIR/manual_bits.rs:25:5
 +   |
 +LL |     size_of::<u64>() * 8;
-   --> $DIR/manual_bits.rs:20:5
++   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS`
++  --> $DIR/manual_bits.rs:26:5
 +   |
 +LL |     size_of::<u128>() * 8;
-   --> $DIR/manual_bits.rs:21:5
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS`
++  --> $DIR/manual_bits.rs:27:5
 +   |
 +LL |     size_of::<usize>() * 8;
-   --> $DIR/manual_bits.rs:23:5
++   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS`
++  --> $DIR/manual_bits.rs:29:5
 +   |
 +LL |     8 * size_of::<i8>();
-   --> $DIR/manual_bits.rs:24:5
++   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS`
++  --> $DIR/manual_bits.rs:30:5
 +   |
 +LL |     8 * size_of::<i16>();
-   --> $DIR/manual_bits.rs:25:5
++   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS`
++  --> $DIR/manual_bits.rs:31:5
 +   |
 +LL |     8 * size_of::<i32>();
-   --> $DIR/manual_bits.rs:26:5
++   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS`
++  --> $DIR/manual_bits.rs:32:5
 +   |
 +LL |     8 * size_of::<i64>();
-   --> $DIR/manual_bits.rs:27:5
++   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS`
++  --> $DIR/manual_bits.rs:33:5
 +   |
 +LL |     8 * size_of::<i128>();
-   --> $DIR/manual_bits.rs:28:5
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS`
++  --> $DIR/manual_bits.rs:34:5
 +   |
 +LL |     8 * size_of::<isize>();
-   --> $DIR/manual_bits.rs:30:5
++   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS`
++  --> $DIR/manual_bits.rs:36:5
 +   |
 +LL |     8 * size_of::<u8>();
-   --> $DIR/manual_bits.rs:31:5
++   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS`
++  --> $DIR/manual_bits.rs:37:5
 +   |
 +LL |     8 * size_of::<u16>();
-   --> $DIR/manual_bits.rs:32:5
++   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS`
++  --> $DIR/manual_bits.rs:38:5
 +   |
 +LL |     8 * size_of::<u32>();
-   --> $DIR/manual_bits.rs:33:5
++   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS`
++  --> $DIR/manual_bits.rs:39:5
 +   |
 +LL |     8 * size_of::<u64>();
-   --> $DIR/manual_bits.rs:34:5
++   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS`
++  --> $DIR/manual_bits.rs:40:5
 +   |
 +LL |     8 * size_of::<u128>();
-   --> $DIR/manual_bits.rs:35:5
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS`
++  --> $DIR/manual_bits.rs:41:5
 +   |
 +LL |     8 * size_of::<usize>();
-   --> $DIR/manual_bits.rs:45:5
++   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize`
 +
 +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-    |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS`
++  --> $DIR/manual_bits.rs:51:5
 +   |
 +LL |     size_of::<Word>() * 8;
- error: aborting due to 25 previous errors
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS as usize`
++
++error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
++  --> $DIR/manual_bits.rs:55:18
++   |
++LL |     let _: u32 = (size_of::<u128>() * 8) as u32;
++   |                  ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS`
++
++error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
++  --> $DIR/manual_bits.rs:56:18
++   |
++LL |     let _: u32 = (size_of::<u128>() * 8).try_into().unwrap();
++   |                  ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS`
++
++error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
++  --> $DIR/manual_bits.rs:57:13
++   |
++LL |     let _ = (size_of::<u128>() * 8).pow(5);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)`
++
++error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
++  --> $DIR/manual_bits.rs:58:14
++   |
++LL |     let _ = &(size_of::<u128>() * 8);
++   |              ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)`
 +
++error: aborting due to 29 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f23c6d69b4c6a4142c008597588e656711c4e2bd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,78 @@@
++#![warn(clippy::manual_non_exhaustive)]
++#![allow(unused)]
++
++enum E {
++    A,
++    B,
++    #[doc(hidden)]
++    _C,
++}
++
++// user forgot to remove the marker
++#[non_exhaustive]
++enum Ep {
++    A,
++    B,
++    #[doc(hidden)]
++    _C,
++}
++
++// marker variant does not have doc hidden attribute, should be ignored
++enum NoDocHidden {
++    A,
++    B,
++    _C,
++}
++
++// name of variant with doc hidden does not start with underscore, should be ignored
++enum NoUnderscore {
++    A,
++    B,
++    #[doc(hidden)]
++    C,
++}
++
++// variant with doc hidden is not unit, should be ignored
++enum NotUnit {
++    A,
++    B,
++    #[doc(hidden)]
++    _C(bool),
++}
++
++// variant with doc hidden is the only one, should be ignored
++enum OnlyMarker {
++    #[doc(hidden)]
++    _A,
++}
++
++// variant with multiple markers, should be ignored
++enum MultipleMarkers {
++    A,
++    #[doc(hidden)]
++    _B,
++    #[doc(hidden)]
++    _C,
++}
++
++// already non_exhaustive and no markers, should be ignored
++#[non_exhaustive]
++enum NonExhaustive {
++    A,
++    B,
++}
++
++// marked is used, don't lint
++enum UsedHidden {
++    #[doc(hidden)]
++    _A,
++    B,
++    C,
++}
++fn foo(x: &mut UsedHidden) {
++    if matches!(*x, UsedHidden::B) {
++        *x = UsedHidden::_A;
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..317a45d2cbd596ab306cae15880279b1c49d8dff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++error: this seems like a manual implementation of the non-exhaustive pattern
++  --> $DIR/manual_non_exhaustive_enum.rs:4:1
++   |
++LL |   enum E {
++   |   ^-----
++   |   |
++   |  _help: add the attribute: `#[non_exhaustive] enum E`
++   | |
++LL | |     A,
++LL | |     B,
++LL | |     #[doc(hidden)]
++LL | |     _C,
++LL | | }
++   | |_^
++   |
++   = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
++help: remove this variant
++  --> $DIR/manual_non_exhaustive_enum.rs:8:5
++   |
++LL |     _C,
++   |     ^^
++
++error: this seems like a manual implementation of the non-exhaustive pattern
++  --> $DIR/manual_non_exhaustive_enum.rs:13:1
++   |
++LL | / enum Ep {
++LL | |     A,
++LL | |     B,
++LL | |     #[doc(hidden)]
++LL | |     _C,
++LL | | }
++   | |_^
++   |
++help: remove this variant
++  --> $DIR/manual_non_exhaustive_enum.rs:17:5
++   |
++LL |     _C,
++   |     ^^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..498eee4447b8841cf275d462abf3d957e3c8403f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,74 @@@
++#![warn(clippy::manual_non_exhaustive)]
++#![allow(unused)]
++
++mod structs {
++    struct S {
++        pub a: i32,
++        pub b: i32,
++        _c: (),
++    }
++
++    // user forgot to remove the private field
++    #[non_exhaustive]
++    struct Sp {
++        pub a: i32,
++        pub b: i32,
++        _c: (),
++    }
++
++    // some other fields are private, should be ignored
++    struct PrivateFields {
++        a: i32,
++        pub b: i32,
++        _c: (),
++    }
++
++    // private field name does not start with underscore, should be ignored
++    struct NoUnderscore {
++        pub a: i32,
++        pub b: i32,
++        c: (),
++    }
++
++    // private field is not unit type, should be ignored
++    struct NotUnit {
++        pub a: i32,
++        pub b: i32,
++        _c: i32,
++    }
++
++    // private field is the only field, should be ignored
++    struct OnlyMarker {
++        _a: (),
++    }
++
++    // already non exhaustive and no private fields, should be ignored
++    #[non_exhaustive]
++    struct NonExhaustive {
++        pub a: i32,
++        pub b: i32,
++    }
++}
++
++mod tuple_structs {
++    struct T(pub i32, pub i32, ());
++
++    // user forgot to remove the private field
++    #[non_exhaustive]
++    struct Tp(pub i32, pub i32, ());
++
++    // some other fields are private, should be ignored
++    struct PrivateFields(pub i32, i32, ());
++
++    // private field is not unit type, should be ignored
++    struct NotUnit(pub i32, pub i32, i32);
++
++    // private field is the only field, should be ignored
++    struct OnlyMarker(());
++
++    // already non exhaustive and no private fields, should be ignored
++    #[non_exhaustive]
++    struct NonExhaustive(pub i32, pub i32);
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e0766c17b758038c5cbf7a4ef007a3005c91f5c9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,65 @@@
++error: this seems like a manual implementation of the non-exhaustive pattern
++  --> $DIR/manual_non_exhaustive_struct.rs:5:5
++   |
++LL |       struct S {
++   |       ^-------
++   |       |
++   |  _____help: add the attribute: `#[non_exhaustive] struct S`
++   | |
++LL | |         pub a: i32,
++LL | |         pub b: i32,
++LL | |         _c: (),
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
++help: remove this field
++  --> $DIR/manual_non_exhaustive_struct.rs:8:9
++   |
++LL |         _c: (),
++   |         ^^^^^^
++
++error: this seems like a manual implementation of the non-exhaustive pattern
++  --> $DIR/manual_non_exhaustive_struct.rs:13:5
++   |
++LL | /     struct Sp {
++LL | |         pub a: i32,
++LL | |         pub b: i32,
++LL | |         _c: (),
++LL | |     }
++   | |_____^
++   |
++help: remove this field
++  --> $DIR/manual_non_exhaustive_struct.rs:16:9
++   |
++LL |         _c: (),
++   |         ^^^^^^
++
++error: this seems like a manual implementation of the non-exhaustive pattern
++  --> $DIR/manual_non_exhaustive_struct.rs:54:5
++   |
++LL |     struct T(pub i32, pub i32, ());
++   |     --------^^^^^^^^^^^^^^^^^^^^^^^
++   |     |
++   |     help: add the attribute: `#[non_exhaustive] struct T`
++   |
++help: remove this field
++  --> $DIR/manual_non_exhaustive_struct.rs:54:32
++   |
++LL |     struct T(pub i32, pub i32, ());
++   |                                ^^
++
++error: this seems like a manual implementation of the non-exhaustive pattern
++  --> $DIR/manual_non_exhaustive_struct.rs:58:5
++   |
++LL |     struct Tp(pub i32, pub i32, ());
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: remove this field
++  --> $DIR/manual_non_exhaustive_struct.rs:58:33
++   |
++LL |     struct Tp(pub i32, pub i32, ());
++   |                                 ^^
++
++error: aborting due to 4 previous errors
++
index d5113df569a086c4b99b514bb59e9f0f461b3865,0000000000000000000000000000000000000000..c7ca770434a318c53308c073bbb72ecc3e540749
mode 100644,000000..100644
--- /dev/null
@@@ -1,56 -1,0 +1,147 @@@
- #![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)]
 +// run-rustfix
 +
 +#![feature(custom_inner_attributes)]
 +#![warn(clippy::manual_split_once)]
-     let _ = Some("key=value".split_once('=').map_or("key=value", |x| x.0));
++#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
 +
 +extern crate itertools;
 +
 +#[allow(unused_imports)]
 +use itertools::Itertools;
 +
 +fn main() {
-     let _ = "key=value".split_once('=').map_or("key=value", |x| x.0);
-     let _ = "key=value".split_once('=').map_or("key=value", |x| x.0);
 +    let _ = "key=value".splitn(2, '=').nth(2);
-     let _ = s.split_once('=').map_or(&*s, |x| x.0);
 +    let _ = "key=value".split_once('=').unwrap().1;
 +    let _ = "key=value".split_once('=').unwrap().1;
 +    let (_, _) = "key=value".split_once('=').unwrap();
 +
 +    let s = String::from("key=value");
-     let _ = s.split_once('=').map_or(&*s, |x| x.0);
++    let _ = s.split_once('=').unwrap().1;
 +
 +    let s = Box::<str>::from("key=value");
-     let _ = s.split_once('=').map_or(*s, |x| x.0);
++    let _ = s.split_once('=').unwrap().1;
 +
 +    let s = &"key=value";
-         let _ = s.split_once("key=value").map_or(s, |x| x.0);
-         let _ = s.split_once("key=value")?.1;
-         let _ = s.split_once("key=value")?.1;
++    let _ = s.split_once('=').unwrap().1;
 +
 +    fn _f(s: &str) -> Option<&str> {
-     let _ = "key=value".rsplitn(2, '=').next().unwrap();
++        let _ = s.split_once('=')?.1;
++        let _ = s.split_once('=')?.1;
++        let _ = s.rsplit_once('=')?.0;
++        let _ = s.rsplit_once('=')?.0;
 +        None
 +    }
 +
 +    // Don't lint, slices don't have `split_once`
 +    let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
 +
 +    // `rsplitn` gives the results in the reverse order of `rsplit_once`
-     let _ = "key=value".rsplit_once('=').map(|x| x.1);
 +    let _ = "key=value".rsplit_once('=').unwrap().0;
-     // `str::split_once` was stabilized in 1.16. Do not lint this
 +    let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap();
++    let _ = s.rsplit_once('=').map(|x| x.0);
++}
++
++fn indirect() -> Option<()> {
++    let (l, r) = "a.b.c".split_once('.').unwrap();
++    
++    
++
++    let (l, r) = "a.b.c".split_once('.')?;
++    
++    
++
++    let (l, r) = "a.b.c".rsplit_once('.').unwrap();
++    
++    
++
++    let (l, r) = "a.b.c".rsplit_once('.')?;
++    
++    
++
++    // could lint, currently doesn't
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let other = 1;
++    let l = iter.next()?;
++    let r = iter.next()?;
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let mut mut_binding = iter.next()?;
++    let r = iter.next()?;
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let tuple = (iter.next()?, iter.next()?);
++
++    // should not lint
++
++    let mut missing_unwrap = "a.b.c".splitn(2, '.');
++    let l = missing_unwrap.next();
++    let r = missing_unwrap.next();
++
++    let mut mixed_unrap = "a.b.c".splitn(2, '.');
++    let unwrap = mixed_unrap.next().unwrap();
++    let question_mark = mixed_unrap.next()?;
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let same_name = iter.next()?;
++    let same_name = iter.next()?;
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let shadows_existing = "d";
++    let shadows_existing = iter.next()?;
++    let r = iter.next()?;
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let becomes_shadowed = iter.next()?;
++    let becomes_shadowed = "d";
++    let r = iter.next()?;
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let l = iter.next()?;
++    let r = iter.next()?;
++    let third_usage = iter.next()?;
++
++    let mut n_three = "a.b.c".splitn(3, '.');
++    let l = n_three.next()?;
++    let r = n_three.next()?;
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    {
++        let in_block = iter.next()?;
++    }
++    let r = iter.next()?;
++
++    let mut lacks_binding = "a.b.c".splitn(2, '.');
++    let _ = lacks_binding.next()?;
++    let r = lacks_binding.next()?;
++
++    let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~");
++    let l = iter.next()?;
++    let r = iter.next()?;
++
++    let mut assigned = "";
++    let mut iter = "a.b.c".splitn(2, '.');
++    let l = iter.next()?;
++    assigned = iter.next()?;
++
++    None
 +}
 +
 +fn _msrv_1_51() {
 +    #![clippy::msrv = "1.51"]
++    // `str::split_once` was stabilized in 1.52. Do not lint this
 +    let _ = "key=value".splitn(2, '=').nth(1).unwrap();
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let a = iter.next().unwrap();
++    let b = iter.next().unwrap();
 +}
 +
 +fn _msrv_1_52() {
 +    #![clippy::msrv = "1.52"]
 +    let _ = "key=value".split_once('=').unwrap().1;
++
++    let (a, b) = "a.b.c".split_once('.').unwrap();
++    
++    
 +}
index 80e02952dbd07f8adf537d03f093a9eb580fb92e,0000000000000000000000000000000000000000..ee2848a251ee3b2e666c49644a7768597e41782d
mode 100644,000000..100644
--- /dev/null
@@@ -1,56 -1,0 +1,147 @@@
- #![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)]
 +// run-rustfix
 +
 +#![feature(custom_inner_attributes)]
 +#![warn(clippy::manual_split_once)]
-     let _ = "key=value".splitn(2, '=').next();
++#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
 +
 +extern crate itertools;
 +
 +#[allow(unused_imports)]
 +use itertools::Itertools;
 +
 +fn main() {
-     let _ = "key=value".splitn(2, '=').next().unwrap();
-     let _ = "key=value".splitn(2, '=').nth(0).unwrap();
 +    let _ = "key=value".splitn(2, '=').nth(2);
-     let _ = s.splitn(2, '=').next().unwrap();
 +    let _ = "key=value".splitn(2, '=').nth(1).unwrap();
 +    let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
 +    let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
 +
 +    let s = String::from("key=value");
-     let _ = s.splitn(2, '=').nth(0).unwrap();
++    let _ = s.splitn(2, '=').nth(1).unwrap();
 +
 +    let s = Box::<str>::from("key=value");
-     let _ = s.splitn(2, '=').skip(0).next().unwrap();
++    let _ = s.splitn(2, '=').nth(1).unwrap();
 +
 +    let s = &"key=value";
-         let _ = s.splitn(2, "key=value").next()?;
-         let _ = s.splitn(2, "key=value").nth(1)?;
-         let _ = s.splitn(2, "key=value").skip(1).next()?;
++    let _ = s.splitn(2, '=').skip(1).next().unwrap();
 +
 +    fn _f(s: &str) -> Option<&str> {
-     let _ = "key=value".rsplitn(2, '=').next().unwrap();
++        let _ = s.splitn(2, '=').nth(1)?;
++        let _ = s.splitn(2, '=').skip(1).next()?;
++        let _ = s.rsplitn(2, '=').nth(1)?;
++        let _ = s.rsplitn(2, '=').skip(1).next()?;
 +        None
 +    }
 +
 +    // Don't lint, slices don't have `split_once`
 +    let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
 +
 +    // `rsplitn` gives the results in the reverse order of `rsplit_once`
-     let _ = "key=value".rsplitn(2, '=').nth(0);
 +    let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
-     // `str::split_once` was stabilized in 1.16. Do not lint this
 +    let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
++    let _ = s.rsplitn(2, '=').nth(1);
++}
++
++fn indirect() -> Option<()> {
++    let mut iter = "a.b.c".splitn(2, '.');
++    let l = iter.next().unwrap();
++    let r = iter.next().unwrap();
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let l = iter.next()?;
++    let r = iter.next()?;
++
++    let mut iter = "a.b.c".rsplitn(2, '.');
++    let r = iter.next().unwrap();
++    let l = iter.next().unwrap();
++
++    let mut iter = "a.b.c".rsplitn(2, '.');
++    let r = iter.next()?;
++    let l = iter.next()?;
++
++    // could lint, currently doesn't
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let other = 1;
++    let l = iter.next()?;
++    let r = iter.next()?;
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let mut mut_binding = iter.next()?;
++    let r = iter.next()?;
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let tuple = (iter.next()?, iter.next()?);
++
++    // should not lint
++
++    let mut missing_unwrap = "a.b.c".splitn(2, '.');
++    let l = missing_unwrap.next();
++    let r = missing_unwrap.next();
++
++    let mut mixed_unrap = "a.b.c".splitn(2, '.');
++    let unwrap = mixed_unrap.next().unwrap();
++    let question_mark = mixed_unrap.next()?;
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let same_name = iter.next()?;
++    let same_name = iter.next()?;
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let shadows_existing = "d";
++    let shadows_existing = iter.next()?;
++    let r = iter.next()?;
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let becomes_shadowed = iter.next()?;
++    let becomes_shadowed = "d";
++    let r = iter.next()?;
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let l = iter.next()?;
++    let r = iter.next()?;
++    let third_usage = iter.next()?;
++
++    let mut n_three = "a.b.c".splitn(3, '.');
++    let l = n_three.next()?;
++    let r = n_three.next()?;
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    {
++        let in_block = iter.next()?;
++    }
++    let r = iter.next()?;
++
++    let mut lacks_binding = "a.b.c".splitn(2, '.');
++    let _ = lacks_binding.next()?;
++    let r = lacks_binding.next()?;
++
++    let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~");
++    let l = iter.next()?;
++    let r = iter.next()?;
++
++    let mut assigned = "";
++    let mut iter = "a.b.c".splitn(2, '.');
++    let l = iter.next()?;
++    assigned = iter.next()?;
++
++    None
 +}
 +
 +fn _msrv_1_51() {
 +    #![clippy::msrv = "1.51"]
++    // `str::split_once` was stabilized in 1.52. Do not lint this
 +    let _ = "key=value".splitn(2, '=').nth(1).unwrap();
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let a = iter.next().unwrap();
++    let b = iter.next().unwrap();
 +}
 +
 +fn _msrv_1_52() {
 +    #![clippy::msrv = "1.52"]
 +    let _ = "key=value".splitn(2, '=').nth(1).unwrap();
++
++    let mut iter = "a.b.c".splitn(2, '.');
++    let a = iter.next().unwrap();
++    let b = iter.next().unwrap();
 +}
index af9c7a2d41bff248f287da4e9f022b06a886b02a,0000000000000000000000000000000000000000..2563a6904b77c928bd94e48fa1fc41473e23bd21
mode 100644,000000..100644
--- /dev/null
@@@ -1,100 -1,0 +1,213 @@@
-   --> $DIR/manual_split_once.rs:13:13
 +error: manual implementation of `split_once`
- LL |     let _ = "key=value".splitn(2, '=').next();
-    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some("key=value".split_once('=').map_or("key=value", |x| x.0))`
++  --> $DIR/manual_split_once.rs:14:13
 +   |
- LL |     let _ = "key=value".splitn(2, '=').next().unwrap();
-    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)`
- error: manual implementation of `split_once`
-   --> $DIR/manual_split_once.rs:16:13
-    |
- LL |     let _ = "key=value".splitn(2, '=').nth(0).unwrap();
-    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)`
- error: manual implementation of `split_once`
-   --> $DIR/manual_split_once.rs:17:13
-    |
- LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
-    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
- error: manual implementation of `split_once`
-   --> $DIR/manual_split_once.rs:18:13
-    |
++LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
 +   |
 +   = note: `-D clippy::manual-split-once` implied by `-D warnings`
 +
 +error: manual implementation of `split_once`
 +  --> $DIR/manual_split_once.rs:15:13
 +   |
-   --> $DIR/manual_split_once.rs:19:18
 +LL |     let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
 +
 +error: manual implementation of `split_once`
- LL |     let _ = s.splitn(2, '=').next().unwrap();
-    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)`
++  --> $DIR/manual_split_once.rs:16:18
 +   |
 +LL |     let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
 +   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')`
 +
++error: manual implementation of `split_once`
++  --> $DIR/manual_split_once.rs:19:13
++   |
++LL |     let _ = s.splitn(2, '=').nth(1).unwrap();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
++
 +error: manual implementation of `split_once`
 +  --> $DIR/manual_split_once.rs:22:13
 +   |
- LL |     let _ = s.splitn(2, '=').nth(0).unwrap();
-    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)`
++LL |     let _ = s.splitn(2, '=').nth(1).unwrap();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
 +
 +error: manual implementation of `split_once`
 +  --> $DIR/manual_split_once.rs:25:13
 +   |
-   --> $DIR/manual_split_once.rs:28:13
++LL |     let _ = s.splitn(2, '=').skip(1).next().unwrap();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
 +
 +error: manual implementation of `split_once`
- LL |     let _ = s.splitn(2, '=').skip(0).next().unwrap();
-    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(*s, |x| x.0)`
++  --> $DIR/manual_split_once.rs:28:17
 +   |
-   --> $DIR/manual_split_once.rs:31:17
++LL |         let _ = s.splitn(2, '=').nth(1)?;
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1`
 +
 +error: manual implementation of `split_once`
- LL |         let _ = s.splitn(2, "key=value").next()?;
-    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value").map_or(s, |x| x.0)`
++  --> $DIR/manual_split_once.rs:29:17
 +   |
- error: manual implementation of `split_once`
-   --> $DIR/manual_split_once.rs:32:17
++LL |         let _ = s.splitn(2, '=').skip(1).next()?;
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1`
 +
- LL |         let _ = s.splitn(2, "key=value").nth(1)?;
-    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
++error: manual implementation of `rsplit_once`
++  --> $DIR/manual_split_once.rs:30:17
 +   |
- error: manual implementation of `split_once`
-   --> $DIR/manual_split_once.rs:33:17
++LL |         let _ = s.rsplitn(2, '=').nth(1)?;
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0`
 +
- LL |         let _ = s.splitn(2, "key=value").skip(1).next()?;
-    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
++error: manual implementation of `rsplit_once`
++  --> $DIR/manual_split_once.rs:31:17
 +   |
-   --> $DIR/manual_split_once.rs:42:13
++LL |         let _ = s.rsplitn(2, '=').skip(1).next()?;
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0`
 +
 +error: manual implementation of `rsplit_once`
-   --> $DIR/manual_split_once.rs:43:13
++  --> $DIR/manual_split_once.rs:39:13
 +   |
 +LL |     let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0`
 +
 +error: manual implementation of `rsplit_once`
- LL |     let _ = "key=value".rsplitn(2, '=').nth(0);
-    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|x| x.1)`
++  --> $DIR/manual_split_once.rs:40:18
 +   |
-   --> $DIR/manual_split_once.rs:44:18
++LL |     let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
++   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))`
 +
 +error: manual implementation of `rsplit_once`
- LL |     let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
-    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))`
++  --> $DIR/manual_split_once.rs:41:13
 +   |
-   --> $DIR/manual_split_once.rs:55:13
++LL |     let _ = s.rsplitn(2, '=').nth(1);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=').map(|x| x.0)`
++
++error: manual implementation of `split_once`
++  --> $DIR/manual_split_once.rs:45:5
++   |
++LL |     let mut iter = "a.b.c".splitn(2, '.');
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL |     let l = iter.next().unwrap();
++   |     ----------------------------- first usage here
++LL |     let r = iter.next().unwrap();
++   |     ----------------------------- second usage here
++   |
++help: try `split_once`
++   |
++LL |     let (l, r) = "a.b.c".split_once('.').unwrap();
++   |
++help: remove the `iter` usages
++   |
++LL -     let l = iter.next().unwrap();
++LL +     
++   | 
++help: remove the `iter` usages
++   |
++LL -     let r = iter.next().unwrap();
++LL +     
++   | 
 +
 +error: manual implementation of `split_once`
- error: aborting due to 16 previous errors
++  --> $DIR/manual_split_once.rs:49:5
++   |
++LL |     let mut iter = "a.b.c".splitn(2, '.');
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL |     let l = iter.next()?;
++   |     --------------------- first usage here
++LL |     let r = iter.next()?;
++   |     --------------------- second usage here
++   |
++help: try `split_once`
++   |
++LL |     let (l, r) = "a.b.c".split_once('.')?;
++   |
++help: remove the `iter` usages
++   |
++LL -     let l = iter.next()?;
++LL +     
++   | 
++help: remove the `iter` usages
++   |
++LL -     let r = iter.next()?;
++LL +     
++   | 
++
++error: manual implementation of `rsplit_once`
++  --> $DIR/manual_split_once.rs:53:5
++   |
++LL |     let mut iter = "a.b.c".rsplitn(2, '.');
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL |     let r = iter.next().unwrap();
++   |     ----------------------------- first usage here
++LL |     let l = iter.next().unwrap();
++   |     ----------------------------- second usage here
++   |
++help: try `rsplit_once`
++   |
++LL |     let (l, r) = "a.b.c".rsplit_once('.').unwrap();
++   |
++help: remove the `iter` usages
++   |
++LL -     let r = iter.next().unwrap();
++LL +     
++   | 
++help: remove the `iter` usages
++   |
++LL -     let l = iter.next().unwrap();
++LL +     
++   | 
++
++error: manual implementation of `rsplit_once`
++  --> $DIR/manual_split_once.rs:57:5
++   |
++LL |     let mut iter = "a.b.c".rsplitn(2, '.');
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL |     let r = iter.next()?;
++   |     --------------------- first usage here
++LL |     let l = iter.next()?;
++   |     --------------------- second usage here
++   |
++help: try `rsplit_once`
++   |
++LL |     let (l, r) = "a.b.c".rsplit_once('.')?;
++   |
++help: remove the `iter` usages
++   |
++LL -     let r = iter.next()?;
++LL +     
++   | 
++help: remove the `iter` usages
++   |
++LL -     let l = iter.next()?;
++LL +     
++   | 
++
++error: manual implementation of `split_once`
++  --> $DIR/manual_split_once.rs:142:13
 +   |
 +LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
 +
++error: manual implementation of `split_once`
++  --> $DIR/manual_split_once.rs:144:5
++   |
++LL |     let mut iter = "a.b.c".splitn(2, '.');
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL |     let a = iter.next().unwrap();
++   |     ----------------------------- first usage here
++LL |     let b = iter.next().unwrap();
++   |     ----------------------------- second usage here
++   |
++help: try `split_once`
++   |
++LL |     let (a, b) = "a.b.c".split_once('.').unwrap();
++   |
++help: remove the `iter` usages
++   |
++LL -     let a = iter.next().unwrap();
++LL +     
++   | 
++help: remove the `iter` usages
++   |
++LL -     let b = iter.next().unwrap();
++LL +     
++   | 
++
++error: aborting due to 19 previous errors
 +
index 5d57638af43495bea12bce75b930aa600ae51343,0000000000000000000000000000000000000000..a7b36d53cd26c7cc1011553d0a1ceb2fd88268ae
mode 100644,000000..100644
--- /dev/null
@@@ -1,29 -1,0 +1,43 @@@
-     clippy::inconsistent_digit_grouping
 +// run-rustfix
 +
 +#![allow(
 +    dead_code,
 +    unused_variables,
 +    overflowing_literals,
 +    clippy::excessive_precision,
++    clippy::inconsistent_digit_grouping,
++    clippy::unusual_byte_groupings
 +)]
 +
 +fn main() {
 +    let fail14 = 2_i32;
 +    let fail15 = 4_i64;
 +    let fail16 = 7_i8; //
 +    let fail17 = 23_i16; //
 +    let ok18 = 23_128;
 +
 +    let fail20 = 2_i8; //
 +    let fail21 = 4_i16; //
 +
 +    let ok24 = 12.34_64;
 +    let fail25 = 1E2_f32;
 +    let fail26 = 43E7_f64;
 +    let fail27 = 243E17_f32;
 +    let fail28 = 241_251_235E723_f64;
 +    let ok29 = 42279.911_32;
 +
++    // testing that the suggestion actually fits in its type
++    let fail30 = 127_i8; // should be i8
++    let fail31 = 240_u8; // should be u8
++    let ok32 = 360_8; // doesnt fit in either, should be ignored
++    let fail33 = 0x1234_i16;
++    let fail34 = 0xABCD_u16;
++    let ok35 = 0x12345_16;
++    let fail36 = 0xFFFF_FFFF_FFFF_FFFF_u64; // u64
++
++    // issue #6129
++    let ok37 = 123_32.123;
++    let ok38 = 124_64.0;
++
 +    let _ = 1.123_45E1_f32;
 +}
index 12171452885d2b009bdb0e0373abe765a0c29a04,0000000000000000000000000000000000000000..c97b31965c75a7c4cd1f3030dccd3b042f23fd7c
mode 100644,000000..100644
--- /dev/null
@@@ -1,29 -1,0 +1,43 @@@
-     clippy::inconsistent_digit_grouping
 +// run-rustfix
 +
 +#![allow(
 +    dead_code,
 +    unused_variables,
 +    overflowing_literals,
 +    clippy::excessive_precision,
++    clippy::inconsistent_digit_grouping,
++    clippy::unusual_byte_groupings
 +)]
 +
 +fn main() {
 +    let fail14 = 2_32;
 +    let fail15 = 4_64;
 +    let fail16 = 7_8; //
 +    let fail17 = 23_16; //
 +    let ok18 = 23_128;
 +
 +    let fail20 = 2__8; //
 +    let fail21 = 4___16; //
 +
 +    let ok24 = 12.34_64;
 +    let fail25 = 1E2_32;
 +    let fail26 = 43E7_64;
 +    let fail27 = 243E17_32;
 +    let fail28 = 241251235E723_64;
 +    let ok29 = 42279.911_32;
 +
++    // testing that the suggestion actually fits in its type
++    let fail30 = 127_8; // should be i8
++    let fail31 = 240_8; // should be u8
++    let ok32 = 360_8; // doesnt fit in either, should be ignored
++    let fail33 = 0x1234_16;
++    let fail34 = 0xABCD_16;
++    let ok35 = 0x12345_16;
++    let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64
++
++    // issue #6129
++    let ok37 = 123_32.123;
++    let ok38 = 124_64.0;
++
 +    let _ = 1.12345E1_32;
 +}
index d24543c26e4b0d7e48cbb4647f03ebcd2abf3cb0,0000000000000000000000000000000000000000..fb761d9bde45255085e56a861d791d370516c251
mode 100644,000000..100644
--- /dev/null
@@@ -1,70 -1,0 +1,100 @@@
-   --> $DIR/mistyped_literal_suffix.rs:12:18
 +error: mistyped literal suffix
-   --> $DIR/mistyped_literal_suffix.rs:13:18
++  --> $DIR/mistyped_literal_suffix.rs:13:18
 +   |
 +LL |     let fail14 = 2_32;
 +   |                  ^^^^ help: did you mean to write: `2_i32`
 +   |
 +   = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default
 +
 +error: mistyped literal suffix
-   --> $DIR/mistyped_literal_suffix.rs:14:18
++  --> $DIR/mistyped_literal_suffix.rs:14:18
 +   |
 +LL |     let fail15 = 4_64;
 +   |                  ^^^^ help: did you mean to write: `4_i64`
 +
 +error: mistyped literal suffix
-   --> $DIR/mistyped_literal_suffix.rs:15:18
++  --> $DIR/mistyped_literal_suffix.rs:15:18
 +   |
 +LL |     let fail16 = 7_8; //
 +   |                  ^^^ help: did you mean to write: `7_i8`
 +
 +error: mistyped literal suffix
-   --> $DIR/mistyped_literal_suffix.rs:18:18
++  --> $DIR/mistyped_literal_suffix.rs:16:18
 +   |
 +LL |     let fail17 = 23_16; //
 +   |                  ^^^^^ help: did you mean to write: `23_i16`
 +
 +error: mistyped literal suffix
-   --> $DIR/mistyped_literal_suffix.rs:19:18
++  --> $DIR/mistyped_literal_suffix.rs:19:18
 +   |
 +LL |     let fail20 = 2__8; //
 +   |                  ^^^^ help: did you mean to write: `2_i8`
 +
 +error: mistyped literal suffix
-   --> $DIR/mistyped_literal_suffix.rs:22:18
++  --> $DIR/mistyped_literal_suffix.rs:20:18
 +   |
 +LL |     let fail21 = 4___16; //
 +   |                  ^^^^^^ help: did you mean to write: `4_i16`
 +
 +error: mistyped literal suffix
-   --> $DIR/mistyped_literal_suffix.rs:23:18
++  --> $DIR/mistyped_literal_suffix.rs:23:18
 +   |
 +LL |     let fail25 = 1E2_32;
 +   |                  ^^^^^^ help: did you mean to write: `1E2_f32`
 +
 +error: mistyped literal suffix
-   --> $DIR/mistyped_literal_suffix.rs:24:18
++  --> $DIR/mistyped_literal_suffix.rs:24:18
 +   |
 +LL |     let fail26 = 43E7_64;
 +   |                  ^^^^^^^ help: did you mean to write: `43E7_f64`
 +
 +error: mistyped literal suffix
-   --> $DIR/mistyped_literal_suffix.rs:25:18
++  --> $DIR/mistyped_literal_suffix.rs:25:18
 +   |
 +LL |     let fail27 = 243E17_32;
 +   |                  ^^^^^^^^^ help: did you mean to write: `243E17_f32`
 +
 +error: mistyped literal suffix
-   --> $DIR/mistyped_literal_suffix.rs:28:13
++  --> $DIR/mistyped_literal_suffix.rs:26:18
 +   |
 +LL |     let fail28 = 241251235E723_64;
 +   |                  ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64`
 +
 +error: mistyped literal suffix
- error: aborting due to 11 previous errors
++  --> $DIR/mistyped_literal_suffix.rs:30:18
++   |
++LL |     let fail30 = 127_8; // should be i8
++   |                  ^^^^^ help: did you mean to write: `127_i8`
++
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:31:18
++   |
++LL |     let fail31 = 240_8; // should be u8
++   |                  ^^^^^ help: did you mean to write: `240_u8`
++
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:33:18
++   |
++LL |     let fail33 = 0x1234_16;
++   |                  ^^^^^^^^^ help: did you mean to write: `0x1234_i16`
++
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:34:18
++   |
++LL |     let fail34 = 0xABCD_16;
++   |                  ^^^^^^^^^ help: did you mean to write: `0xABCD_u16`
++
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:36:18
++   |
++LL |     let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64
++   |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to write: `0xFFFF_FFFF_FFFF_FFFF_u64`
++
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:42:13
 +   |
 +LL |     let _ = 1.12345E1_32;
 +   |             ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32`
 +
++error: aborting due to 16 previous errors
 +
index a9a04c8f56b945ca7c0e94b35a211119f7799a2d,0000000000000000000000000000000000000000..370dbd5882161c837c4ffd14a531c99aa7145f0d
mode 100644,000000..100644
--- /dev/null
@@@ -1,46 -1,0 +1,54 @@@
-         unimplemented!()
 +#![allow(unused)]
 +#![warn(clippy::mut_from_ref)]
 +
 +struct Foo;
 +
 +impl Foo {
 +    fn this_wont_hurt_a_bit(&self) -> &mut Foo {
-         unimplemented!()
++        unsafe { unimplemented!() }
 +    }
 +}
 +
 +trait Ouch {
 +    fn ouch(x: &Foo) -> &mut Foo;
 +}
 +
 +impl Ouch for Foo {
 +    fn ouch(x: &Foo) -> &mut Foo {
-     unimplemented!()
++        unsafe { unimplemented!() }
 +    }
 +}
 +
 +fn fail(x: &u32) -> &mut u16 {
-     unimplemented!()
++    unsafe { unimplemented!() }
 +}
 +
 +fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 {
-     unimplemented!()
++    unsafe { unimplemented!() }
 +}
 +
 +fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
-     unimplemented!()
++    unsafe { unimplemented!() }
 +}
 +
 +// this is OK, because the result borrows y
 +fn works<'a>(x: &u32, y: &'a mut u32) -> &'a mut u32 {
++    unsafe { unimplemented!() }
 +}
 +
 +// this is also OK, because the result could borrow y
 +fn also_works<'a>(x: &'a u32, y: &'a mut u32) -> &'a mut u32 {
++    unsafe { unimplemented!() }
++}
++
++unsafe fn also_broken(x: &u32) -> &mut u32 {
++    unimplemented!()
++}
++
++fn without_unsafe(x: &u32) -> &mut u32 {
 +    unimplemented!()
 +}
 +
 +fn main() {
 +    //TODO
 +}
index 4787999920bc219fde94225fd530ba3fba2c3253,0000000000000000000000000000000000000000..b76d6a13ffb9cf312d2285886671a976549d18b6
mode 100644,000000..100644
--- /dev/null
@@@ -1,63 -1,0 +1,75 @@@
- error: aborting due to 5 previous errors
 +error: mutable borrow from immutable input(s)
 +  --> $DIR/mut_from_ref.rs:7:39
 +   |
 +LL |     fn this_wont_hurt_a_bit(&self) -> &mut Foo {
 +   |                                       ^^^^^^^^
 +   |
 +   = note: `-D clippy::mut-from-ref` implied by `-D warnings`
 +note: immutable borrow here
 +  --> $DIR/mut_from_ref.rs:7:29
 +   |
 +LL |     fn this_wont_hurt_a_bit(&self) -> &mut Foo {
 +   |                             ^^^^^
 +
 +error: mutable borrow from immutable input(s)
 +  --> $DIR/mut_from_ref.rs:13:25
 +   |
 +LL |     fn ouch(x: &Foo) -> &mut Foo;
 +   |                         ^^^^^^^^
 +   |
 +note: immutable borrow here
 +  --> $DIR/mut_from_ref.rs:13:16
 +   |
 +LL |     fn ouch(x: &Foo) -> &mut Foo;
 +   |                ^^^^
 +
 +error: mutable borrow from immutable input(s)
 +  --> $DIR/mut_from_ref.rs:22:21
 +   |
 +LL | fn fail(x: &u32) -> &mut u16 {
 +   |                     ^^^^^^^^
 +   |
 +note: immutable borrow here
 +  --> $DIR/mut_from_ref.rs:22:12
 +   |
 +LL | fn fail(x: &u32) -> &mut u16 {
 +   |            ^^^^
 +
 +error: mutable borrow from immutable input(s)
 +  --> $DIR/mut_from_ref.rs:26:50
 +   |
 +LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 {
 +   |                                                  ^^^^^^^^^^^
 +   |
 +note: immutable borrow here
 +  --> $DIR/mut_from_ref.rs:26:25
 +   |
 +LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 {
 +   |                         ^^^^^^^
 +
 +error: mutable borrow from immutable input(s)
 +  --> $DIR/mut_from_ref.rs:30:67
 +   |
 +LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
 +   |                                                                   ^^^^^^^^^^^
 +   |
 +note: immutable borrow here
 +  --> $DIR/mut_from_ref.rs:30:27
 +   |
 +LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
 +   |                           ^^^^^^^     ^^^^^^^
 +
++error: mutable borrow from immutable input(s)
++  --> $DIR/mut_from_ref.rs:44:35
++   |
++LL | unsafe fn also_broken(x: &u32) -> &mut u32 {
++   |                                   ^^^^^^^^
++   |
++note: immutable borrow here
++  --> $DIR/mut_from_ref.rs:44:26
++   |
++LL | unsafe fn also_broken(x: &u32) -> &mut u32 {
++   |                          ^^^^
++
++error: aborting due to 6 previous errors
 +
index f00f9ee4c331b716bd32ad7049f09968ebee88fa,0000000000000000000000000000000000000000..c1685f7b6d7adeba6f5783c4cb7320d669c61904
mode 100644,000000..100644
--- /dev/null
@@@ -1,113 -1,0 +1,118 @@@
- #![allow(unused, clippy::needless_return, clippy::match_single_binding)]
 +// run-rustfix
 +#![warn(clippy::needless_for_each)]
++#![allow(
++    unused,
++    clippy::needless_return,
++    clippy::match_single_binding,
++    clippy::let_unit_value
++)]
 +
 +use std::collections::HashMap;
 +
 +fn should_lint() {
 +    let v: Vec<i32> = Vec::new();
 +    let mut acc = 0;
 +    for elem in v.iter() {
 +        acc += elem;
 +    }
 +    for elem in v.into_iter() {
 +        acc += elem;
 +    }
 +
 +    for elem in [1, 2, 3].iter() {
 +        acc += elem;
 +    }
 +
 +    let mut hash_map: HashMap<i32, i32> = HashMap::new();
 +    for (k, v) in hash_map.iter() {
 +        acc += k + v;
 +    }
 +    for (k, v) in hash_map.iter_mut() {
 +        acc += *k + *v;
 +    }
 +    for k in hash_map.keys() {
 +        acc += k;
 +    }
 +    for v in hash_map.values() {
 +        acc += v;
 +    }
 +
 +    fn my_vec() -> Vec<i32> {
 +        Vec::new()
 +    }
 +    for elem in my_vec().iter() {
 +        acc += elem;
 +    }
 +}
 +
 +fn should_not_lint() {
 +    let v: Vec<i32> = Vec::new();
 +    let mut acc = 0;
 +
 +    // `for_each` argument is not closure.
 +    fn print(x: &i32) {
 +        println!("{}", x);
 +    }
 +    v.iter().for_each(print);
 +
 +    // User defined type.
 +    struct MyStruct {
 +        v: Vec<i32>,
 +    }
 +    impl MyStruct {
 +        fn iter(&self) -> impl Iterator<Item = &i32> {
 +            self.v.iter()
 +        }
 +    }
 +    let s = MyStruct { v: Vec::new() };
 +    s.iter().for_each(|elem| {
 +        acc += elem;
 +    });
 +
 +    // `for_each` follows long iterator chain.
 +    v.iter().chain(v.iter()).for_each(|v| {
 +        acc += v;
 +    });
 +    v.as_slice().iter().for_each(|v| {
 +        acc += v;
 +    });
 +    s.v.iter().for_each(|v| {
 +        acc += v;
 +    });
 +
 +    // `return` is used in `Loop` of the closure.
 +    v.iter().for_each(|v| {
 +        for i in 0..*v {
 +            if i == 10 {
 +                return;
 +            } else {
 +                println!("{}", v);
 +            }
 +        }
 +        if *v == 20 {
 +            return;
 +        } else {
 +            println!("{}", v);
 +        }
 +    });
 +
 +    // Previously transformed iterator variable.
 +    let it = v.iter();
 +    it.chain(v.iter()).for_each(|elem| {
 +        acc += elem;
 +    });
 +
 +    // `for_each` is not directly in a statement.
 +    match 1 {
 +        _ => v.iter().for_each(|elem| {
 +            acc += elem;
 +        }),
 +    }
 +
 +    // `for_each` is in a let bingind.
 +    let _ = v.iter().for_each(|elem| {
 +        acc += elem;
 +    });
 +}
 +
 +fn main() {}
index 1bd400d348ba92ce635f45d8b2dea743cbf6918d,0000000000000000000000000000000000000000..ad17b0956fa93149d2fe436ae3e22755ba1edeb2
mode 100644,000000..100644
--- /dev/null
@@@ -1,113 -1,0 +1,118 @@@
- #![allow(unused, clippy::needless_return, clippy::match_single_binding)]
 +// run-rustfix
 +#![warn(clippy::needless_for_each)]
++#![allow(
++    unused,
++    clippy::needless_return,
++    clippy::match_single_binding,
++    clippy::let_unit_value
++)]
 +
 +use std::collections::HashMap;
 +
 +fn should_lint() {
 +    let v: Vec<i32> = Vec::new();
 +    let mut acc = 0;
 +    v.iter().for_each(|elem| {
 +        acc += elem;
 +    });
 +    v.into_iter().for_each(|elem| {
 +        acc += elem;
 +    });
 +
 +    [1, 2, 3].iter().for_each(|elem| {
 +        acc += elem;
 +    });
 +
 +    let mut hash_map: HashMap<i32, i32> = HashMap::new();
 +    hash_map.iter().for_each(|(k, v)| {
 +        acc += k + v;
 +    });
 +    hash_map.iter_mut().for_each(|(k, v)| {
 +        acc += *k + *v;
 +    });
 +    hash_map.keys().for_each(|k| {
 +        acc += k;
 +    });
 +    hash_map.values().for_each(|v| {
 +        acc += v;
 +    });
 +
 +    fn my_vec() -> Vec<i32> {
 +        Vec::new()
 +    }
 +    my_vec().iter().for_each(|elem| {
 +        acc += elem;
 +    });
 +}
 +
 +fn should_not_lint() {
 +    let v: Vec<i32> = Vec::new();
 +    let mut acc = 0;
 +
 +    // `for_each` argument is not closure.
 +    fn print(x: &i32) {
 +        println!("{}", x);
 +    }
 +    v.iter().for_each(print);
 +
 +    // User defined type.
 +    struct MyStruct {
 +        v: Vec<i32>,
 +    }
 +    impl MyStruct {
 +        fn iter(&self) -> impl Iterator<Item = &i32> {
 +            self.v.iter()
 +        }
 +    }
 +    let s = MyStruct { v: Vec::new() };
 +    s.iter().for_each(|elem| {
 +        acc += elem;
 +    });
 +
 +    // `for_each` follows long iterator chain.
 +    v.iter().chain(v.iter()).for_each(|v| {
 +        acc += v;
 +    });
 +    v.as_slice().iter().for_each(|v| {
 +        acc += v;
 +    });
 +    s.v.iter().for_each(|v| {
 +        acc += v;
 +    });
 +
 +    // `return` is used in `Loop` of the closure.
 +    v.iter().for_each(|v| {
 +        for i in 0..*v {
 +            if i == 10 {
 +                return;
 +            } else {
 +                println!("{}", v);
 +            }
 +        }
 +        if *v == 20 {
 +            return;
 +        } else {
 +            println!("{}", v);
 +        }
 +    });
 +
 +    // Previously transformed iterator variable.
 +    let it = v.iter();
 +    it.chain(v.iter()).for_each(|elem| {
 +        acc += elem;
 +    });
 +
 +    // `for_each` is not directly in a statement.
 +    match 1 {
 +        _ => v.iter().for_each(|elem| {
 +            acc += elem;
 +        }),
 +    }
 +
 +    // `for_each` is in a let bingind.
 +    let _ = v.iter().for_each(|elem| {
 +        acc += elem;
 +    });
 +}
 +
 +fn main() {}
index 6487e57266c71a7da8a65bc3f722cfffa30884b5,0000000000000000000000000000000000000000..08e995851d7a5c9717bbb5e7c9a7239a80003ae9
mode 100644,000000..100644
--- /dev/null
@@@ -1,123 -1,0 +1,123 @@@
-   --> $DIR/needless_for_each_fixable.rs:10:5
 +error: needless use of `for_each`
-   --> $DIR/needless_for_each_fixable.rs:13:5
++  --> $DIR/needless_for_each_fixable.rs:15:5
 +   |
 +LL | /     v.iter().for_each(|elem| {
 +LL | |         acc += elem;
 +LL | |     });
 +   | |_______^
 +   |
 +   = note: `-D clippy::needless-for-each` implied by `-D warnings`
 +help: try
 +   |
 +LL ~     for elem in v.iter() {
 +LL +         acc += elem;
 +LL +     }
 +   |
 +
 +error: needless use of `for_each`
-   --> $DIR/needless_for_each_fixable.rs:17:5
++  --> $DIR/needless_for_each_fixable.rs:18:5
 +   |
 +LL | /     v.into_iter().for_each(|elem| {
 +LL | |         acc += elem;
 +LL | |     });
 +   | |_______^
 +   |
 +help: try
 +   |
 +LL ~     for elem in v.into_iter() {
 +LL +         acc += elem;
 +LL +     }
 +   |
 +
 +error: needless use of `for_each`
-   --> $DIR/needless_for_each_fixable.rs:22:5
++  --> $DIR/needless_for_each_fixable.rs:22:5
 +   |
 +LL | /     [1, 2, 3].iter().for_each(|elem| {
 +LL | |         acc += elem;
 +LL | |     });
 +   | |_______^
 +   |
 +help: try
 +   |
 +LL ~     for elem in [1, 2, 3].iter() {
 +LL +         acc += elem;
 +LL +     }
 +   |
 +
 +error: needless use of `for_each`
-   --> $DIR/needless_for_each_fixable.rs:25:5
++  --> $DIR/needless_for_each_fixable.rs:27:5
 +   |
 +LL | /     hash_map.iter().for_each(|(k, v)| {
 +LL | |         acc += k + v;
 +LL | |     });
 +   | |_______^
 +   |
 +help: try
 +   |
 +LL ~     for (k, v) in hash_map.iter() {
 +LL +         acc += k + v;
 +LL +     }
 +   |
 +
 +error: needless use of `for_each`
-   --> $DIR/needless_for_each_fixable.rs:28:5
++  --> $DIR/needless_for_each_fixable.rs:30:5
 +   |
 +LL | /     hash_map.iter_mut().for_each(|(k, v)| {
 +LL | |         acc += *k + *v;
 +LL | |     });
 +   | |_______^
 +   |
 +help: try
 +   |
 +LL ~     for (k, v) in hash_map.iter_mut() {
 +LL +         acc += *k + *v;
 +LL +     }
 +   |
 +
 +error: needless use of `for_each`
-   --> $DIR/needless_for_each_fixable.rs:31:5
++  --> $DIR/needless_for_each_fixable.rs:33:5
 +   |
 +LL | /     hash_map.keys().for_each(|k| {
 +LL | |         acc += k;
 +LL | |     });
 +   | |_______^
 +   |
 +help: try
 +   |
 +LL ~     for k in hash_map.keys() {
 +LL +         acc += k;
 +LL +     }
 +   |
 +
 +error: needless use of `for_each`
-   --> $DIR/needless_for_each_fixable.rs:38:5
++  --> $DIR/needless_for_each_fixable.rs:36:5
 +   |
 +LL | /     hash_map.values().for_each(|v| {
 +LL | |         acc += v;
 +LL | |     });
 +   | |_______^
 +   |
 +help: try
 +   |
 +LL ~     for v in hash_map.values() {
 +LL +         acc += v;
 +LL +     }
 +   |
 +
 +error: needless use of `for_each`
++  --> $DIR/needless_for_each_fixable.rs:43:5
 +   |
 +LL | /     my_vec().iter().for_each(|elem| {
 +LL | |         acc += elem;
 +LL | |     });
 +   | |_______^
 +   |
 +help: try
 +   |
 +LL ~     for elem in my_vec().iter() {
 +LL +         acc += elem;
 +LL +     }
 +   |
 +
 +error: aborting due to 8 previous errors
 +
index 89e012c066fe4d9593460873e0049ada3c8a8993,0000000000000000000000000000000000000000..54e66b391b8d8a46068df048cf91c17bf8a5d03c
mode 100644,000000..100644
--- /dev/null
@@@ -1,180 -1,0 +1,231 @@@
- #![allow(unused)]
++#![feature(let_chains)]
++#![allow(unused, clippy::nonminimal_bool, clippy::let_unit_value)]
++
++use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
++use std::rc::Rc;
++
++struct SignificantDrop;
++impl std::ops::Drop for SignificantDrop {
++    fn drop(&mut self) {
++        println!("dropped");
++    }
++}
 +
 +fn main() {
 +    let a;
 +    let n = 1;
 +    match n {
 +        1 => a = "one",
 +        _ => {
 +            a = "two";
 +        },
 +    }
 +
 +    let b;
 +    if n == 3 {
 +        b = "four";
 +    } else {
 +        b = "five"
 +    }
 +
-     let c;
-     if let Some(n) = Some(5) {
-         c = n;
-     } else {
-         c = -50;
-     }
 +    let d;
 +    if true {
 +        let temp = 5;
 +        d = temp;
 +    } else {
 +        d = 15;
 +    }
 +
 +    let e;
 +    if true {
 +        e = format!("{} {}", a, b);
 +    } else {
-         e = format!("{}", c);
++        e = format!("{}", n);
 +    }
 +
 +    let f;
 +    match 1 {
 +        1 => f = "three",
 +        _ => return,
 +    }; // has semi
 +
 +    let g: usize;
 +    if true {
 +        g = 5;
 +    } else {
 +        panic!();
 +    }
 +
-     println!("{}", a);
++    // Drop order only matters if both are significant
++    let x;
++    let y = SignificantDrop;
++    x = 1;
++
++    let x;
++    let y = 1;
++    x = SignificantDrop;
++
++    let x;
++    // types that should be considered insignificant
++    let y = 1;
++    let y = "2";
++    let y = String::new();
++    let y = vec![3.0];
++    let y = HashMap::<usize, usize>::new();
++    let y = BTreeMap::<usize, usize>::new();
++    let y = HashSet::<usize>::new();
++    let y = BTreeSet::<usize>::new();
++    let y = Box::new(4);
++    x = SignificantDrop;
 +}
 +
 +async fn in_async() -> &'static str {
 +    async fn f() -> &'static str {
 +        "one"
 +    }
 +
 +    let a;
 +    let n = 1;
 +    match n {
 +        1 => a = f().await,
 +        _ => {
 +            a = "two";
 +        },
 +    }
 +
 +    a
 +}
 +
 +const fn in_const() -> &'static str {
 +    const fn f() -> &'static str {
 +        "one"
 +    }
 +
 +    let a;
 +    let n = 1;
 +    match n {
 +        1 => a = f(),
 +        _ => {
 +            a = "two";
 +        },
 +    }
 +
 +    a
 +}
 +
 +fn does_not_lint() {
 +    let z;
 +    if false {
 +        z = 1;
 +    }
 +
 +    let x;
 +    let y;
 +    if true {
 +        x = 1;
 +    } else {
 +        y = 1;
 +    }
 +
 +    let mut x;
 +    if true {
 +        x = 5;
 +        x = 10 / x;
 +    } else {
 +        x = 2;
 +    }
 +
 +    let x;
 +    let _ = match 1 {
 +        1 => x = 10,
 +        _ => x = 20,
 +    };
 +
 +    // using tuples would be possible, but not always preferable
 +    let x;
 +    let y;
 +    if true {
 +        x = 1;
 +        y = 2;
 +    } else {
 +        x = 3;
 +        y = 4;
 +    }
 +
 +    // could match with a smarter heuristic to avoid multiple assignments
 +    let x;
 +    if true {
 +        let mut y = 5;
 +        y = 6;
 +        x = y;
 +    } else {
 +        x = 2;
 +    }
 +
 +    let (x, y);
 +    if true {
 +        x = 1;
 +    } else {
 +        x = 2;
 +    }
 +    y = 3;
 +
 +    macro_rules! assign {
 +        ($i:ident) => {
 +            $i = 1;
 +        };
 +    }
 +    let x;
 +    assign!(x);
 +
 +    let x;
 +    if true {
 +        assign!(x);
 +    } else {
 +        x = 2;
 +    }
 +
 +    macro_rules! in_macro {
 +        () => {
 +            let x;
 +            x = 1;
 +
 +            let x;
 +            if true {
 +                x = 1;
 +            } else {
 +                x = 2;
 +            }
 +        };
 +    }
 +    in_macro!();
 +
-     println!("{}", x);
++    // ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613
++    let x;
++    if let Some(n) = Some("v") {
++        x = 1;
++    } else {
++        x = 2;
++    }
++
++    let x;
++    if true && let Some(n) = Some("let chains too") {
++        x = 1;
++    } else {
++        x = 2;
++    }
++
++    // ignore mut bindings
++    // https://github.com/shepmaster/twox-hash/blob/b169c16d86eb8ea4a296b0acb9d00ca7e3c3005f/src/sixty_four.rs#L88-L93
++    // https://github.com/dtolnay/thiserror/blob/21c26903e29cb92ba1a7ff11e82ae2001646b60d/tests/test_generics.rs#L91-L100
++    let mut x: usize;
++    x = 1;
++    x = 2;
++    x = 3;
++
++    // should not move the declaration if `x` has a significant drop, and there
++    // is another binding with a significant drop between it and the first usage
++    let x;
++    let y = SignificantDrop;
++    x = SignificantDrop;
 +}
index ef79e635d2ae623da06255747c8dc3fd66f059e4,0000000000000000000000000000000000000000..d33a117b288cb5482a5bd0cc87dc446e3f091109
mode 100644,000000..100644
--- /dev/null
@@@ -1,186 -1,0 +1,207 @@@
- error: unneeded late initalization
-   --> $DIR/needless_late_init.rs:4:5
++error: unneeded late initialization
++  --> $DIR/needless_late_init.rs:15:5
 +   |
 +LL |     let a;
 +   |     ^^^^^^
 +   |
 +   = note: `-D clippy::needless-late-init` implied by `-D warnings`
 +help: declare `a` here
 +   |
 +LL |     let a = match n {
 +   |     +++++++
 +help: remove the assignments from the `match` arms
 +   |
 +LL ~         1 => "one",
 +LL |         _ => {
 +LL ~             "two"
 +   |
 +help: add a semicolon after the `match` expression
 +   |
 +LL |     };
 +   |      +
 +
- error: unneeded late initalization
-   --> $DIR/needless_late_init.rs:13:5
++error: unneeded late initialization
++  --> $DIR/needless_late_init.rs:24:5
 +   |
 +LL |     let b;
 +   |     ^^^^^^
 +   |
 +help: declare `b` here
 +   |
 +LL |     let b = if n == 3 {
 +   |     +++++++
 +help: remove the assignments from the branches
 +   |
 +LL ~         "four"
 +LL |     } else {
 +LL ~         "five"
 +   |
 +help: add a semicolon after the `if` expression
 +   |
 +LL |     };
 +   |      +
 +
- error: unneeded late initalization
-   --> $DIR/needless_late_init.rs:20:5
-    |
- LL |     let c;
-    |     ^^^^^^
-    |
- help: declare `c` here
-    |
- LL |     let c = if let Some(n) = Some(5) {
-    |     +++++++
- help: remove the assignments from the branches
-    |
- LL ~         n
- LL |     } else {
- LL ~         -50
-    |
- help: add a semicolon after the `if` expression
-    |
- LL |     };
-    |      +
- error: unneeded late initalization
-   --> $DIR/needless_late_init.rs:27:5
++error: unneeded late initialization
++  --> $DIR/needless_late_init.rs:31:5
 +   |
 +LL |     let d;
 +   |     ^^^^^^
 +   |
 +help: declare `d` here
 +   |
 +LL |     let d = if true {
 +   |     +++++++
 +help: remove the assignments from the branches
 +   |
 +LL ~         temp
 +LL |     } else {
 +LL ~         15
 +   |
 +help: add a semicolon after the `if` expression
 +   |
 +LL |     };
 +   |      +
 +
- error: unneeded late initalization
-   --> $DIR/needless_late_init.rs:35:5
++error: unneeded late initialization
++  --> $DIR/needless_late_init.rs:39:5
 +   |
 +LL |     let e;
 +   |     ^^^^^^
 +   |
 +help: declare `e` here
 +   |
 +LL |     let e = if true {
 +   |     +++++++
 +help: remove the assignments from the branches
 +   |
 +LL ~         format!("{} {}", a, b)
 +LL |     } else {
- LL ~         format!("{}", c)
++LL ~         format!("{}", n)
 +   |
 +help: add a semicolon after the `if` expression
 +   |
 +LL |     };
 +   |      +
 +
- error: unneeded late initalization
-   --> $DIR/needless_late_init.rs:42:5
++error: unneeded late initialization
++  --> $DIR/needless_late_init.rs:46:5
 +   |
 +LL |     let f;
 +   |     ^^^^^^
 +   |
 +help: declare `f` here
 +   |
 +LL |     let f = match 1 {
 +   |     +++++++
 +help: remove the assignments from the `match` arms
 +   |
 +LL -         1 => f = "three",
 +LL +         1 => "three",
 +   | 
 +
- error: unneeded late initalization
-   --> $DIR/needless_late_init.rs:48:5
++error: unneeded late initialization
++  --> $DIR/needless_late_init.rs:52:5
 +   |
 +LL |     let g: usize;
 +   |     ^^^^^^^^^^^^^
 +   |
 +help: declare `g` here
 +   |
 +LL |     let g: usize = if true {
 +   |     ++++++++++++++
 +help: remove the assignments from the branches
 +   |
 +LL -         g = 5;
 +LL +         5
 +   | 
 +help: add a semicolon after the `if` expression
 +   |
 +LL |     };
 +   |      +
 +
- error: unneeded late initalization
-   --> $DIR/needless_late_init.rs:63:5
++error: unneeded late initialization
++  --> $DIR/needless_late_init.rs:60:5
++   |
++LL |     let x;
++   |     ^^^^^^ created here
++LL |     let y = SignificantDrop;
++LL |     x = 1;
++   |     ^^^^^ initialised here
++   |
++help: declare `x` here
++   |
++LL |     let x = 1;
++   |     ~~~~~
++
++error: unneeded late initialization
++  --> $DIR/needless_late_init.rs:64:5
++   |
++LL |     let x;
++   |     ^^^^^^ created here
++LL |     let y = 1;
++LL |     x = SignificantDrop;
++   |     ^^^^^^^^^^^^^^^^^^^ initialised here
++   |
++help: declare `x` here
++   |
++LL |     let x = SignificantDrop;
++   |     ~~~~~
++
++error: unneeded late initialization
++  --> $DIR/needless_late_init.rs:68:5
++   |
++LL |     let x;
++   |     ^^^^^^ created here
++...
++LL |     x = SignificantDrop;
++   |     ^^^^^^^^^^^^^^^^^^^ initialised here
++   |
++help: declare `x` here
++   |
++LL |     let x = SignificantDrop;
++   |     ~~~~~
++
++error: unneeded late initialization
++  --> $DIR/needless_late_init.rs:87:5
 +   |
 +LL |     let a;
 +   |     ^^^^^^
 +   |
 +help: declare `a` here
 +   |
 +LL |     let a = match n {
 +   |     +++++++
 +help: remove the assignments from the `match` arms
 +   |
 +LL ~         1 => f().await,
 +LL |         _ => {
 +LL ~             "two"
 +   |
 +help: add a semicolon after the `match` expression
 +   |
 +LL |     };
 +   |      +
 +
- error: unneeded late initalization
-   --> $DIR/needless_late_init.rs:80:5
++error: unneeded late initialization
++  --> $DIR/needless_late_init.rs:104:5
 +   |
 +LL |     let a;
 +   |     ^^^^^^
 +   |
 +help: declare `a` here
 +   |
 +LL |     let a = match n {
 +   |     +++++++
 +help: remove the assignments from the `match` arms
 +   |
 +LL ~         1 => f(),
 +LL |         _ => {
 +LL ~             "two"
 +   |
 +help: add a semicolon after the `match` expression
 +   |
 +LL |     };
 +   |      +
 +
- error: aborting due to 9 previous errors
++error: aborting due to 11 previous errors
 +
index b516f9d86b75b652b0e4f788c85cc16853d573a4,0000000000000000000000000000000000000000..724477e8691df1df790fcdb3efa62cccb49da932
mode 100644,000000..100644
--- /dev/null
@@@ -1,25 -1,0 +1,19 @@@
-     let mut e = 1;
-     e = 2;
-     
-     let h = format!("{}", e);
-     println!("{}", a);
 +// run-rustfix
 +
 +#![allow(unused, clippy::assign_op_pattern)]
 +
 +fn main() {
 +    
 +    let a = "zero";
 +
 +    
 +    
 +    let b = 1;
 +    let c = 2;
 +
 +    
 +    let d: usize = 1;
 +
 +    
++    let e = format!("{}", d);
 +}
index 75a4bc916deacb5cbd52e2f10c9f1fa5643abf5d,0000000000000000000000000000000000000000..3e6bd36367275dd2f2a55118f3da9dd0fd8da22d
mode 100644,000000..100644
--- /dev/null
@@@ -1,25 -1,0 +1,19 @@@
-     let mut e;
-     e = 1;
-     e = 2;
-     let h;
-     h = format!("{}", e);
-     println!("{}", a);
 +// run-rustfix
 +
 +#![allow(unused, clippy::assign_op_pattern)]
 +
 +fn main() {
 +    let a;
 +    a = "zero";
 +
 +    let b;
 +    let c;
 +    b = 1;
 +    c = 2;
 +
 +    let d: usize;
 +    d = 1;
 +
++    let e;
++    e = format!("{}", d);
 +}
index 3f3d4f5286b2b2e088a737288c3759955bbcd1af,0000000000000000000000000000000000000000..8c664309e3e83fe4838fbcfc12d7c5e586109c42
mode 100644,000000..100644
--- /dev/null
@@@ -1,69 -1,0 +1,70 @@@
- error: unneeded late initalization
++error: unneeded late initialization
 +  --> $DIR/needless_late_init_fixable.rs:6:5
 +   |
 +LL |     let a;
-    |     ^^^^^^
++   |     ^^^^^^ created here
++LL |     a = "zero";
++   |     ^^^^^^^^^^ initialised here
 +   |
 +   = note: `-D clippy::needless-late-init` implied by `-D warnings`
 +help: declare `a` here
 +   |
 +LL |     let a = "zero";
 +   |     ~~~~~
 +
- error: unneeded late initalization
++error: unneeded late initialization
 +  --> $DIR/needless_late_init_fixable.rs:9:5
 +   |
 +LL |     let b;
-    |     ^^^^^^
++   |     ^^^^^^ created here
++LL |     let c;
++LL |     b = 1;
++   |     ^^^^^ initialised here
 +   |
 +help: declare `b` here
 +   |
 +LL |     let b = 1;
 +   |     ~~~~~
 +
- error: unneeded late initalization
++error: unneeded late initialization
 +  --> $DIR/needless_late_init_fixable.rs:10:5
 +   |
 +LL |     let c;
-    |     ^^^^^^
++   |     ^^^^^^ created here
++LL |     b = 1;
++LL |     c = 2;
++   |     ^^^^^ initialised here
 +   |
 +help: declare `c` here
 +   |
 +LL |     let c = 2;
 +   |     ~~~~~
 +
- error: unneeded late initalization
++error: unneeded late initialization
 +  --> $DIR/needless_late_init_fixable.rs:14:5
 +   |
 +LL |     let d: usize;
-    |     ^^^^^^^^^^^^^
++   |     ^^^^^^^^^^^^^ created here
++LL |     d = 1;
++   |     ^^^^^ initialised here
 +   |
 +help: declare `d` here
 +   |
 +LL |     let d: usize = 1;
 +   |     ~~~~~~~~~~~~
 +
- error: unneeded late initalization
++error: unneeded late initialization
 +  --> $DIR/needless_late_init_fixable.rs:17:5
 +   |
- LL |     let mut e;
-    |     ^^^^^^^^^^
++LL |     let e;
++   |     ^^^^^^ created here
++LL |     e = format!("{}", d);
++   |     ^^^^^^^^^^^^^^^^^^^^ initialised here
 +   |
 +help: declare `e` here
 +   |
- LL |     let mut e = 1;
-    |     ~~~~~~~~~
- error: unneeded late initalization
-   --> $DIR/needless_late_init_fixable.rs:21:5
-    |
- LL |     let h;
-    |     ^^^^^^
-    |
- help: declare `h` here
-    |
- LL |     let h = format!("{}", e);
++LL |     let e = format!("{}", d);
 +   |     ~~~~~
 +
- error: aborting due to 6 previous errors
++error: aborting due to 5 previous errors
 +
index 9ccccaa1725a65dc8867a1a48e9698c811682886,0000000000000000000000000000000000000000..b997e5316cf3f451442295cc4a776f4bf41a8c3f
mode 100644,000000..100644
--- /dev/null
@@@ -1,197 -1,0 +1,209 @@@
 +// 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
 +    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),
 +        }
 +    }
 +}
 +
 +fn main() {}
index d210ecff7f1633a8a6e157e643cc9d42725457e9,0000000000000000000000000000000000000000..90482775a1eebbf7d1fa1621b3c3a233b3739d5a
mode 100644,000000..100644
--- /dev/null
@@@ -1,234 -1,0 +1,246 @@@
 +// 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
 +    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),
 +        }
 +    }
 +}
 +
 +fn main() {}
index 34c5226f06057611d9eb87ac5c631b4d241850e8,0000000000000000000000000000000000000000..2d679631c6f0d71d9da899d9966bb3bb8e595e3c
mode 100644,000000..100644
--- /dev/null
@@@ -1,113 -1,0 +1,113 @@@
-   --> $DIR/needless_match.rs:110:31
 +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:111:31
++  --> $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:117:21
++  --> $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:156:26
++  --> $DIR/needless_match.rs:129: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:168: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: aborting due to 11 previous errors
 +
index c09b07db3dca9787a2c6d585057dcf188e39b508,0000000000000000000000000000000000000000..acd22c6bb43372e6048a016d656f1bbd6aec2b4f
mode 100644,000000..100644
--- /dev/null
@@@ -1,41 -1,0 +1,55 @@@
 +// run-rustfix
 +
 +#![allow(unused)]
 +#![warn(clippy::needless_option_as_deref)]
 +
 +fn main() {
 +    // should lint
 +    let _: Option<&usize> = Some(&1);
 +    let _: Option<&mut usize> = Some(&mut 1);
 +
 +    let mut y = 0;
 +    let mut x = Some(&mut y);
 +    let _ = x;
 +
 +    // should not lint
 +    let _ = Some(Box::new(1)).as_deref();
 +    let _ = Some(Box::new(1)).as_deref_mut();
 +
++    let mut y = 0;
++    let mut x = Some(&mut y);
++    for _ in 0..3 {
++        let _ = x.as_deref_mut();
++    }
++
++    let mut y = 0;
++    let mut x = Some(&mut y);
++    let mut closure = || {
++        let _ = x.as_deref_mut();
++    };
++    closure();
++    closure();
++
 +    // #7846
 +    let mut i = 0;
 +    let mut opt_vec = vec![Some(&mut i)];
 +    opt_vec[0].as_deref_mut().unwrap();
 +
 +    let mut i = 0;
 +    let x = &mut Some(&mut i);
 +    (*x).as_deref_mut();
 +
 +    // #8047
 +    let mut y = 0;
 +    let mut x = Some(&mut y);
 +    x.as_deref_mut();
 +    dbg!(x);
 +}
 +
 +struct S<'a> {
 +    opt: Option<&'a mut usize>,
 +}
 +
 +fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> {
 +    s.opt.as_deref_mut()
 +}
index c3ba27ecccf22fe8c09ca1327c62944f8b72d75b,0000000000000000000000000000000000000000..61eda5052a21efed8363013d28c364f042943736
mode 100644,000000..100644
--- /dev/null
@@@ -1,41 -1,0 +1,55 @@@
 +// run-rustfix
 +
 +#![allow(unused)]
 +#![warn(clippy::needless_option_as_deref)]
 +
 +fn main() {
 +    // should lint
 +    let _: Option<&usize> = Some(&1).as_deref();
 +    let _: Option<&mut usize> = Some(&mut 1).as_deref_mut();
 +
 +    let mut y = 0;
 +    let mut x = Some(&mut y);
 +    let _ = x.as_deref_mut();
 +
 +    // should not lint
 +    let _ = Some(Box::new(1)).as_deref();
 +    let _ = Some(Box::new(1)).as_deref_mut();
 +
++    let mut y = 0;
++    let mut x = Some(&mut y);
++    for _ in 0..3 {
++        let _ = x.as_deref_mut();
++    }
++
++    let mut y = 0;
++    let mut x = Some(&mut y);
++    let mut closure = || {
++        let _ = x.as_deref_mut();
++    };
++    closure();
++    closure();
++
 +    // #7846
 +    let mut i = 0;
 +    let mut opt_vec = vec![Some(&mut i)];
 +    opt_vec[0].as_deref_mut().unwrap();
 +
 +    let mut i = 0;
 +    let x = &mut Some(&mut i);
 +    (*x).as_deref_mut();
 +
 +    // #8047
 +    let mut y = 0;
 +    let mut x = Some(&mut y);
 +    x.as_deref_mut();
 +    dbg!(x);
 +}
 +
 +struct S<'a> {
 +    opt: Option<&'a mut usize>,
 +}
 +
 +fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> {
 +    s.opt.as_deref_mut()
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..29691e81666f7797bc99428b961545f9249331d7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++// run-rustfix
++
++fn main() {
++    println!("Testing non erroneous option_take_on_temporary");
++    let mut option = Some(1);
++    let _ = Box::new(move || option.take().unwrap());
++
++    println!("Testing non erroneous option_take_on_temporary");
++    let x = Some(3);
++    x.as_ref();
++
++    println!("Testing erroneous option_take_on_temporary");
++    let x = Some(3);
++    x.as_ref();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9f4109eb4635ac15cb91dc720960b906caeabf6e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++// run-rustfix
++
++fn main() {
++    println!("Testing non erroneous option_take_on_temporary");
++    let mut option = Some(1);
++    let _ = Box::new(move || option.take().unwrap());
++
++    println!("Testing non erroneous option_take_on_temporary");
++    let x = Some(3);
++    x.as_ref();
++
++    println!("Testing erroneous option_take_on_temporary");
++    let x = Some(3);
++    x.as_ref().take();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cb3bf015b369d811634894efd63cb298eb171fd6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: called `Option::take()` on a temporary value
++  --> $DIR/needless_option_take.rs:14:5
++   |
++LL |     x.as_ref().take();
++   |     ^^^^^^^^^^^^^^^^^ help: try: `x.as_ref()`
++   |
++   = note: `-D clippy::needless-option-take` implied by `-D warnings`
++
++error: aborting due to previous error
++
index f6a4b2f17d33d81f048a1c828fe3bc2cfb744631,0000000000000000000000000000000000000000..61f5fc4e679edafbb609cfa70dd550ff3df2e39e
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,47 @@@
 +// run-rustfix
 +// edition:2018
 +
 +#![feature(custom_inner_attributes)]
 +#![warn(clippy::needless_splitn)]
 +#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
 +
 +extern crate itertools;
 +
 +#[allow(unused_imports)]
 +use itertools::Itertools;
 +
 +fn main() {
 +    let str = "key=value=end";
 +    let _ = str.split('=').next();
 +    let _ = str.split('=').nth(0);
 +    let _ = str.splitn(2, '=').nth(1);
 +    let (_, _) = str.splitn(2, '=').next_tuple().unwrap();
 +    let (_, _) = str.split('=').next_tuple().unwrap();
 +    let _: Vec<&str> = str.splitn(3, '=').collect();
 +
 +    let _ = str.rsplit('=').next();
 +    let _ = str.rsplit('=').nth(0);
 +    let _ = str.rsplitn(2, '=').nth(1);
 +    let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
 +    let (_, _) = str.rsplit('=').next_tuple().unwrap();
++
++    let _ = str.split('=').next();
++    let _ = str.split('=').nth(3);
++    let _ = str.splitn(5, '=').nth(4);
++    let _ = str.splitn(5, '=').nth(5);
++}
++
++fn _question_mark(s: &str) -> Option<()> {
++    let _ = s.split('=').next()?;
++    let _ = s.split('=').nth(0)?;
++    let _ = s.rsplit('=').next()?;
++    let _ = s.rsplit('=').nth(0)?;
++
++    Some(())
++}
++
++fn _test_msrv() {
++    #![clippy::msrv = "1.51"]
++    // `manual_split_once` MSRV shouldn't apply to `needless_splitn`
++    let _ = "key=value".split('=').nth(0).unwrap();
 +}
index 6ba32255bb2d767da5e6bc6c155cd7b05d22ef0e,0000000000000000000000000000000000000000..71d9a7077faa6f354e9475507b456d8e53e884e9
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,47 @@@
 +// run-rustfix
 +// edition:2018
 +
 +#![feature(custom_inner_attributes)]
 +#![warn(clippy::needless_splitn)]
 +#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
 +
 +extern crate itertools;
 +
 +#[allow(unused_imports)]
 +use itertools::Itertools;
 +
 +fn main() {
 +    let str = "key=value=end";
 +    let _ = str.splitn(2, '=').next();
 +    let _ = str.splitn(2, '=').nth(0);
 +    let _ = str.splitn(2, '=').nth(1);
 +    let (_, _) = str.splitn(2, '=').next_tuple().unwrap();
 +    let (_, _) = str.splitn(3, '=').next_tuple().unwrap();
 +    let _: Vec<&str> = str.splitn(3, '=').collect();
 +
 +    let _ = str.rsplitn(2, '=').next();
 +    let _ = str.rsplitn(2, '=').nth(0);
 +    let _ = str.rsplitn(2, '=').nth(1);
 +    let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
 +    let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
++
++    let _ = str.splitn(5, '=').next();
++    let _ = str.splitn(5, '=').nth(3);
++    let _ = str.splitn(5, '=').nth(4);
++    let _ = str.splitn(5, '=').nth(5);
++}
++
++fn _question_mark(s: &str) -> Option<()> {
++    let _ = s.splitn(2, '=').next()?;
++    let _ = s.splitn(2, '=').nth(0)?;
++    let _ = s.rsplitn(2, '=').next()?;
++    let _ = s.rsplitn(2, '=').nth(0)?;
++
++    Some(())
++}
++
++fn _test_msrv() {
++    #![clippy::msrv = "1.51"]
++    // `manual_split_once` MSRV shouldn't apply to `needless_splitn`
++    let _ = "key=value".splitn(2, '=').nth(0).unwrap();
 +}
index 66de2256554e3ad2ea2ea1b0ddd13891a94e6845,0000000000000000000000000000000000000000..f112b29e7f2066ccb0751f049d479b45d5a79c58
mode 100644,000000..100644
--- /dev/null
@@@ -1,40 -1,0 +1,82 @@@
- error: aborting due to 6 previous errors
 +error: unnecessary use of `splitn`
 +  --> $DIR/needless_splitn.rs:15:13
 +   |
 +LL |     let _ = str.splitn(2, '=').next();
 +   |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
 +   |
 +   = note: `-D clippy::needless-splitn` implied by `-D warnings`
 +
 +error: unnecessary use of `splitn`
 +  --> $DIR/needless_splitn.rs:16:13
 +   |
 +LL |     let _ = str.splitn(2, '=').nth(0);
 +   |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
 +
 +error: unnecessary use of `splitn`
 +  --> $DIR/needless_splitn.rs:19:18
 +   |
 +LL |     let (_, _) = str.splitn(3, '=').next_tuple().unwrap();
 +   |                  ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
 +
 +error: unnecessary use of `rsplitn`
 +  --> $DIR/needless_splitn.rs:22:13
 +   |
 +LL |     let _ = str.rsplitn(2, '=').next();
 +   |             ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
 +
 +error: unnecessary use of `rsplitn`
 +  --> $DIR/needless_splitn.rs:23:13
 +   |
 +LL |     let _ = str.rsplitn(2, '=').nth(0);
 +   |             ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
 +
 +error: unnecessary use of `rsplitn`
 +  --> $DIR/needless_splitn.rs:26:18
 +   |
 +LL |     let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
 +   |                  ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
 +
++error: unnecessary use of `splitn`
++  --> $DIR/needless_splitn.rs:28:13
++   |
++LL |     let _ = str.splitn(5, '=').next();
++   |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
++
++error: unnecessary use of `splitn`
++  --> $DIR/needless_splitn.rs:29:13
++   |
++LL |     let _ = str.splitn(5, '=').nth(3);
++   |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
++
++error: unnecessary use of `splitn`
++  --> $DIR/needless_splitn.rs:35:13
++   |
++LL |     let _ = s.splitn(2, '=').next()?;
++   |             ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')`
++
++error: unnecessary use of `splitn`
++  --> $DIR/needless_splitn.rs:36:13
++   |
++LL |     let _ = s.splitn(2, '=').nth(0)?;
++   |             ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')`
++
++error: unnecessary use of `rsplitn`
++  --> $DIR/needless_splitn.rs:37:13
++   |
++LL |     let _ = s.rsplitn(2, '=').next()?;
++   |             ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')`
++
++error: unnecessary use of `rsplitn`
++  --> $DIR/needless_splitn.rs:38:13
++   |
++LL |     let _ = s.rsplitn(2, '=').nth(0)?;
++   |             ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')`
++
++error: unnecessary use of `splitn`
++  --> $DIR/needless_splitn.rs:46:13
++   |
++LL |     let _ = "key=value".splitn(2, '=').nth(0).unwrap();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split('=')`
++
++error: aborting due to 13 previous errors
 +
index e94f99c95f481145edad86d5c112673dcd72d082,0000000000000000000000000000000000000000..538927b18055a289c771f5cb1cfdc1ba9bfad531
mode 100644,000000..100644
--- /dev/null
@@@ -1,214 -1,0 +1,214 @@@
- #![allow(dead_code, clippy::missing_safety_doc)]
++#![allow(dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes)]
 +#![warn(clippy::new_without_default)]
 +
 +pub struct Foo;
 +
 +impl Foo {
 +    pub fn new() -> Foo {
 +        Foo
 +    }
 +}
 +
 +pub struct Bar;
 +
 +impl Bar {
 +    pub fn new() -> Self {
 +        Bar
 +    }
 +}
 +
 +pub struct Ok;
 +
 +impl Ok {
 +    pub fn new() -> Self {
 +        Ok
 +    }
 +}
 +
 +impl Default for Ok {
 +    fn default() -> Self {
 +        Ok
 +    }
 +}
 +
 +pub struct Params;
 +
 +impl Params {
 +    pub fn new(_: u32) -> Self {
 +        Params
 +    }
 +}
 +
 +pub struct GenericsOk<T> {
 +    bar: T,
 +}
 +
 +impl<U> Default for GenericsOk<U> {
 +    fn default() -> Self {
 +        unimplemented!();
 +    }
 +}
 +
 +impl<'c, V> GenericsOk<V> {
 +    pub fn new() -> GenericsOk<V> {
 +        unimplemented!()
 +    }
 +}
 +
 +pub struct LtOk<'a> {
 +    foo: &'a bool,
 +}
 +
 +impl<'b> Default for LtOk<'b> {
 +    fn default() -> Self {
 +        unimplemented!();
 +    }
 +}
 +
 +impl<'c> LtOk<'c> {
 +    pub fn new() -> LtOk<'c> {
 +        unimplemented!()
 +    }
 +}
 +
 +pub struct LtKo<'a> {
 +    foo: &'a bool,
 +}
 +
 +impl<'c> LtKo<'c> {
 +    pub fn new() -> LtKo<'c> {
 +        unimplemented!()
 +    }
 +    // FIXME: that suggestion is missing lifetimes
 +}
 +
 +struct Private;
 +
 +impl Private {
 +    fn new() -> Private {
 +        unimplemented!()
 +    } // We don't lint private items
 +}
 +
 +struct PrivateStruct;
 +
 +impl PrivateStruct {
 +    pub fn new() -> PrivateStruct {
 +        unimplemented!()
 +    } // We don't lint public items on private structs
 +}
 +
 +pub struct PrivateItem;
 +
 +impl PrivateItem {
 +    fn new() -> PrivateItem {
 +        unimplemented!()
 +    } // We don't lint private items on public structs
 +}
 +
 +struct Const;
 +
 +impl Const {
 +    pub const fn new() -> Const {
 +        Const
 +    } // const fns can't be implemented via Default
 +}
 +
 +pub struct IgnoreGenericNew;
 +
 +impl IgnoreGenericNew {
 +    pub fn new<T>() -> Self {
 +        IgnoreGenericNew
 +    } // the derived Default does not make sense here as the result depends on T
 +}
 +
 +pub trait TraitWithNew: Sized {
 +    fn new() -> Self {
 +        panic!()
 +    }
 +}
 +
 +pub struct IgnoreUnsafeNew;
 +
 +impl IgnoreUnsafeNew {
 +    pub unsafe fn new() -> Self {
 +        IgnoreUnsafeNew
 +    }
 +}
 +
 +#[derive(Default)]
 +pub struct OptionRefWrapper<'a, T>(Option<&'a T>);
 +
 +impl<'a, T> OptionRefWrapper<'a, T> {
 +    pub fn new() -> Self {
 +        OptionRefWrapper(None)
 +    }
 +}
 +
 +pub struct Allow(Foo);
 +
 +impl Allow {
 +    #[allow(clippy::new_without_default)]
 +    pub fn new() -> Self {
 +        unimplemented!()
 +    }
 +}
 +
 +pub struct AllowDerive;
 +
 +impl AllowDerive {
 +    #[allow(clippy::new_without_default)]
 +    pub fn new() -> Self {
 +        unimplemented!()
 +    }
 +}
 +
 +pub struct NewNotEqualToDerive {
 +    foo: i32,
 +}
 +
 +impl NewNotEqualToDerive {
 +    // This `new` implementation is not equal to a derived `Default`, so do not suggest deriving.
 +    pub fn new() -> Self {
 +        NewNotEqualToDerive { foo: 1 }
 +    }
 +}
 +
 +// see #6933
 +pub struct FooGenerics<T>(std::marker::PhantomData<T>);
 +impl<T> FooGenerics<T> {
 +    pub fn new() -> Self {
 +        Self(Default::default())
 +    }
 +}
 +
 +pub struct BarGenerics<T>(std::marker::PhantomData<T>);
 +impl<T: Copy> BarGenerics<T> {
 +    pub fn new() -> Self {
 +        Self(Default::default())
 +    }
 +}
 +
 +pub mod issue7220 {
 +    pub struct Foo<T> {
 +        _bar: *mut T,
 +    }
 +
 +    impl<T> Foo<T> {
 +        pub fn new() -> Self {
 +            todo!()
 +        }
 +    }
 +}
 +
 +// see issue #8152
 +// This should not create any lints
 +pub struct DocHidden;
 +impl DocHidden {
 +    #[doc(hidden)]
 +    pub fn new() -> Self {
 +        DocHidden
 +    }
 +}
 +
 +fn main() {}
index 9937005d68d8ae43092952b1a4f307d8f21f816c,0000000000000000000000000000000000000000..583096ac054a15dcc076570b91eb6991d84c258c
mode 100644,000000..100644
--- /dev/null
@@@ -1,58 -1,0 +1,58 @@@
- #![allow(unused, clippy::println_empty_string, non_snake_case)]
 +#![warn(clippy::all)]
++#![allow(unused, clippy::println_empty_string, non_snake_case, clippy::let_unit_value)]
 +
 +#[derive(Clone, Debug)]
 +enum MaybeInst {
 +    Split,
 +    Split1(usize),
 +    Split2(usize),
 +}
 +
 +struct InstSplit {
 +    uiae: usize,
 +}
 +
 +impl MaybeInst {
 +    fn fill(&mut self) {
 +        #[allow(non_fmt_panics)]
 +        let filled = match *self {
 +            MaybeInst::Split1(goto1) => panic!("1"),
 +            MaybeInst::Split2(goto2) => panic!("2"),
 +            _ => unimplemented!(),
 +        };
 +        unimplemented!()
 +    }
 +}
 +
 +fn underscores_and_numbers() {
 +    let _1 = 1; //~ERROR Consider a more descriptive name
 +    let ____1 = 1; //~ERROR Consider a more descriptive name
 +    let __1___2 = 12; //~ERROR Consider a more descriptive name
 +    let _1_ok = 1;
 +}
 +
 +fn issue2927() {
 +    let args = 1;
 +    format!("{:?}", 2);
 +}
 +
 +fn issue3078() {
 +    #[allow(clippy::single_match)]
 +    match "a" {
 +        stringify!(a) => {},
 +        _ => {},
 +    }
 +}
 +
 +struct Bar;
 +
 +impl Bar {
 +    fn bar() {
 +        let _1 = 1;
 +        let ____1 = 1;
 +        let __1___2 = 12;
 +        let _1_ok = 1;
 +    }
 +}
 +
 +fn main() {}
index 1da97e9687988aa856051a79a1a7b868b6b0d7b6,0000000000000000000000000000000000000000..3710b3e9c81e0ac7263f3df216cbddbe77f00e74
mode 100644,000000..100644
--- /dev/null
@@@ -1,33 -1,0 +1,38 @@@
 +//run-rustfix
 +#![warn(clippy::init_numbered_fields)]
 +
 +#[derive(Default)]
 +struct TupleStruct(u32, u32, u8);
 +
 +// This shouldn't lint because it's in a macro
 +macro_rules! tuple_struct_init {
 +    () => {
 +        TupleStruct { 0: 0, 1: 1, 2: 2 }
 +    };
 +}
 +
 +fn main() {
 +    let tuple_struct = TupleStruct::default();
 +
 +    // This should lint
 +    let _ = TupleStruct(1u32, 42, 23u8);
 +
 +    // This should also lint and order the fields correctly
 +    let _ = TupleStruct(1u32, 3u32, 2u8);
 +
 +    // Ok because of default initializer
 +    let _ = TupleStruct { 0: 42, ..tuple_struct };
 +
 +    let _ = TupleStruct {
 +        1: 23,
 +        ..TupleStruct::default()
 +    };
 +
 +    // Ok because it's in macro
 +    let _ = tuple_struct_init!();
++
++    type Alias = TupleStruct;
++
++    // Aliases can't be tuple constructed #8638
++    let _ = Alias { 0: 0, 1: 1, 2: 2 };
 +}
index 08ec405a5606e2ae8772754dbcf35697ad5b4cdb,0000000000000000000000000000000000000000..2af84bc0642a534683b963c3d51b37e85761eae4
mode 100644,000000..100644
--- /dev/null
@@@ -1,41 -1,0 +1,46 @@@
 +//run-rustfix
 +#![warn(clippy::init_numbered_fields)]
 +
 +#[derive(Default)]
 +struct TupleStruct(u32, u32, u8);
 +
 +// This shouldn't lint because it's in a macro
 +macro_rules! tuple_struct_init {
 +    () => {
 +        TupleStruct { 0: 0, 1: 1, 2: 2 }
 +    };
 +}
 +
 +fn main() {
 +    let tuple_struct = TupleStruct::default();
 +
 +    // This should lint
 +    let _ = TupleStruct {
 +        0: 1u32,
 +        1: 42,
 +        2: 23u8,
 +    };
 +
 +    // This should also lint and order the fields correctly
 +    let _ = TupleStruct {
 +        0: 1u32,
 +        2: 2u8,
 +        1: 3u32,
 +    };
 +
 +    // Ok because of default initializer
 +    let _ = TupleStruct { 0: 42, ..tuple_struct };
 +
 +    let _ = TupleStruct {
 +        1: 23,
 +        ..TupleStruct::default()
 +    };
 +
 +    // Ok because it's in macro
 +    let _ = tuple_struct_init!();
++
++    type Alias = TupleStruct;
++
++    // Aliases can't be tuple constructed #8638
++    let _ = Alias { 0: 0, 1: 1, 2: 2 };
 +}
index 7790c816481d1b78bbc2320c3b1cf57d36a3242b,0000000000000000000000000000000000000000..e12e13a57f1f0b43286aa3b2cf498e385566342e
mode 100644,000000..100644
--- /dev/null
@@@ -1,176 -1,0 +1,181 @@@
- #![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)]
 +// run-rustfix
 +#![warn(clippy::option_if_let_else)]
++#![allow(
++    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();
 +}
index 3d9f76ee4a6b570430f18538518b18cc33a47973,0000000000000000000000000000000000000000..b5206fc26a9e13ae091cfe7cbdb528d56c19901a
mode 100644,000000..100644
--- /dev/null
@@@ -1,205 -1,0 +1,210 @@@
- #![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)]
 +// run-rustfix
 +#![warn(clippy::option_if_let_else)]
++#![allow(
++    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();
 +}
index 546131ceb5b633688ae9113df9a96f41e373fcd1,0000000000000000000000000000000000000000..40aef977b989bfd057c7c7fb1b4b7805fd79e08f
mode 100644,000000..100644
--- /dev/null
@@@ -1,210 -1,0 +1,210 @@@
-   --> $DIR/option_if_let_else.rs:6:5
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:24:13
++  --> $DIR/option_if_let_else.rs:11: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:25:13
++  --> $DIR/option_if_let_else.rs:29: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:26:13
++  --> $DIR/option_if_let_else.rs:30: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
++  --> $DIR/option_if_let_else.rs:31: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:33:13
++  --> $DIR/option_if_let_else.rs:37: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
++  --> $DIR/option_if_let_else.rs:38: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:48:5
++  --> $DIR/option_if_let_else.rs:44: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:61:13
++  --> $DIR/option_if_let_else.rs:53: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:70:13
++  --> $DIR/option_if_let_else.rs:66: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:103:13
++  --> $DIR/option_if_let_else.rs:75: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:127:13
++  --> $DIR/option_if_let_else.rs:108: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:136:13
++  --> $DIR/option_if_let_else.rs:132: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:164:13
++  --> $DIR/option_if_let_else.rs:141: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:168:13
++  --> $DIR/option_if_let_else.rs:169: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:173:13
 +   |
 +LL |       let _ = if let Some(x) = Some(0) {
 +   |  _____________^
 +LL | |         let s = s;
 +LL | |         s.len() + x
 +LL | |     } else {
 +LL | |         1
 +LL | |     };
 +   | |_____^
 +   |
 +help: try
 +   |
 +LL ~     let _ = Some(0).map_or(1, |x| {
 +LL +         let s = s;
 +LL +         s.len() + x
 +LL ~     });
 +   |
 +
 +error: aborting due to 15 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..29691e81666f7797bc99428b961545f9249331d7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++// run-rustfix
++
++fn main() {
++    println!("Testing non erroneous option_take_on_temporary");
++    let mut option = Some(1);
++    let _ = Box::new(move || option.take().unwrap());
++
++    println!("Testing non erroneous option_take_on_temporary");
++    let x = Some(3);
++    x.as_ref();
++
++    println!("Testing erroneous option_take_on_temporary");
++    let x = Some(3);
++    x.as_ref();
++}
index 6e0d5a87f6807b012fcc55428b5b0aee61882301,0000000000000000000000000000000000000000..844cc4b7a09281ebaa23604078f83735625b17c7
mode 100644,000000..100644
--- /dev/null
@@@ -1,52 -1,0 +1,52 @@@
- #![allow(clippy::map_identity)]
 +// run-rustfix
 +
 +#![warn(clippy::or_then_unwrap)]
++#![allow(clippy::map_identity, clippy::let_unit_value)]
 +
 +struct SomeStruct;
 +impl SomeStruct {
 +    fn or(self, _: Option<Self>) -> Self {
 +        self
 +    }
 +    fn unwrap(&self) {}
 +}
 +
 +struct SomeOtherStruct;
 +impl SomeOtherStruct {
 +    fn or(self) -> Self {
 +        self
 +    }
 +    fn unwrap(&self) {}
 +}
 +
 +fn main() {
 +    let option: Option<&str> = None;
 +    let _ = option.unwrap_or("fallback"); // should trigger lint
 +
 +    let result: Result<&str, &str> = Err("Error");
 +    let _ = result.unwrap_or("fallback"); // should trigger lint
 +
 +    // as part of a method chain
 +    let option: Option<&str> = None;
 +    let _ = option.map(|v| v).unwrap_or("fallback").to_string().chars(); // should trigger lint
 +
 +    // Not Option/Result
 +    let instance = SomeStruct {};
 +    let _ = instance.or(Some(SomeStruct {})).unwrap(); // should not trigger lint
 +
 +    // or takes no argument
 +    let instance = SomeOtherStruct {};
 +    let _ = instance.or().unwrap(); // should not trigger lint and should not panic
 +
 +    // None in or
 +    let option: Option<&str> = None;
 +    let _ = option.or(None).unwrap(); // should not trigger lint
 +
 +    // Not Err in or
 +    let result: Result<&str, &str> = Err("Error");
 +    let _ = result.or::<&str>(Err("Other Error")).unwrap(); // should not trigger lint
 +
 +    // other function between
 +    let option: Option<&str> = None;
 +    let _ = option.or(Some("fallback")).map(|v| v).unwrap(); // should not trigger lint
 +}
index e406a71d46d00d377024971cee471851e0c3a063,0000000000000000000000000000000000000000..1528ef9be964de41ba522ee5fef9930ca3a159bb
mode 100644,000000..100644
--- /dev/null
@@@ -1,52 -1,0 +1,52 @@@
- #![allow(clippy::map_identity)]
 +// run-rustfix
 +
 +#![warn(clippy::or_then_unwrap)]
++#![allow(clippy::map_identity, clippy::let_unit_value)]
 +
 +struct SomeStruct;
 +impl SomeStruct {
 +    fn or(self, _: Option<Self>) -> Self {
 +        self
 +    }
 +    fn unwrap(&self) {}
 +}
 +
 +struct SomeOtherStruct;
 +impl SomeOtherStruct {
 +    fn or(self) -> Self {
 +        self
 +    }
 +    fn unwrap(&self) {}
 +}
 +
 +fn main() {
 +    let option: Option<&str> = None;
 +    let _ = option.or(Some("fallback")).unwrap(); // should trigger lint
 +
 +    let result: Result<&str, &str> = Err("Error");
 +    let _ = result.or::<&str>(Ok("fallback")).unwrap(); // should trigger lint
 +
 +    // as part of a method chain
 +    let option: Option<&str> = None;
 +    let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint
 +
 +    // Not Option/Result
 +    let instance = SomeStruct {};
 +    let _ = instance.or(Some(SomeStruct {})).unwrap(); // should not trigger lint
 +
 +    // or takes no argument
 +    let instance = SomeOtherStruct {};
 +    let _ = instance.or().unwrap(); // should not trigger lint and should not panic
 +
 +    // None in or
 +    let option: Option<&str> = None;
 +    let _ = option.or(None).unwrap(); // should not trigger lint
 +
 +    // Not Err in or
 +    let result: Result<&str, &str> = Err("Error");
 +    let _ = result.or::<&str>(Err("Other Error")).unwrap(); // should not trigger lint
 +
 +    // other function between
 +    let option: Option<&str> = None;
 +    let _ = option.or(Some("fallback")).map(|v| v).unwrap(); // should not trigger lint
 +}
index 12a0c776ae2fd6feb90371be57be32ddd59379de,0000000000000000000000000000000000000000..041ef17fa6834240c97e3234601e8432f3f0ff0a
mode 100644,000000..100644
--- /dev/null
@@@ -1,95 -1,0 +1,95 @@@
- #![allow(clippy::assertions_on_constants, clippy::eq_op)]
++#![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::let_unit_value)]
 +#![feature(inline_const)]
 +#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)]
 +
 +extern crate core;
 +
 +const _: () = {
 +    if 1 == 0 {
 +        panic!("A balanced diet means a cupcake in each hand");
 +    }
 +};
 +
 +fn inline_const() {
 +    let _ = const {
 +        if 1 == 0 {
 +            panic!("When nothing goes right, go left")
 +        }
 +    };
 +}
 +
 +fn panic() {
 +    let a = 2;
 +    panic!();
 +    panic!("message");
 +    panic!("{} {}", "panic with", "multiple arguments");
 +    let b = a + 2;
 +}
 +
 +fn todo() {
 +    let a = 2;
 +    todo!();
 +    todo!("message");
 +    todo!("{} {}", "panic with", "multiple arguments");
 +    let b = a + 2;
 +}
 +
 +fn unimplemented() {
 +    let a = 2;
 +    unimplemented!();
 +    unimplemented!("message");
 +    unimplemented!("{} {}", "panic with", "multiple arguments");
 +    let b = a + 2;
 +}
 +
 +fn unreachable() {
 +    let a = 2;
 +    unreachable!();
 +    unreachable!("message");
 +    unreachable!("{} {}", "panic with", "multiple arguments");
 +    let b = a + 2;
 +}
 +
 +fn core_versions() {
 +    use core::{panic, todo, unimplemented, unreachable};
 +    panic!();
 +    todo!();
 +    unimplemented!();
 +    unreachable!();
 +}
 +
 +fn assert() {
 +    assert!(true);
 +    assert_eq!(true, true);
 +    assert_ne!(true, false);
 +}
 +
 +fn assert_msg() {
 +    assert!(true, "this should not panic");
 +    assert_eq!(true, true, "this should not panic");
 +    assert_ne!(true, false, "this should not panic");
 +}
 +
 +fn debug_assert() {
 +    debug_assert!(true);
 +    debug_assert_eq!(true, true);
 +    debug_assert_ne!(true, false);
 +}
 +
 +fn debug_assert_msg() {
 +    debug_assert!(true, "test");
 +    debug_assert_eq!(true, true, "test");
 +    debug_assert_ne!(true, false, "test");
 +}
 +
 +fn main() {
 +    panic();
 +    todo();
 +    unimplemented();
 +    unreachable();
 +    core_versions();
 +    assert();
 +    assert_msg();
 +    debug_assert();
 +    debug_assert_msg();
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..65542bedec7bc36062648d1c9fd5057344661ff5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++#![warn(clippy::pub_use)]
++#![allow(unused_imports)]
++#![no_main]
++
++pub mod outer {
++    mod inner {
++        pub struct Test {}
++    }
++    // should be linted
++    pub use inner::Test;
++}
++
++// should not be linted
++use std::fmt;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9ab710df818ca2a3be3394b1b95ceeb32f5a1812
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++error: using `pub use`
++  --> $DIR/pub_use.rs:10:5
++   |
++LL |     pub use inner::Test;
++   |     ^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::pub-use` implied by `-D warnings`
++   = help: move the exported item to a public module instead
++
++error: aborting due to previous error
++
index 25f2fd061b88ef0a014f078c8752796354766195,0000000000000000000000000000000000000000..106947de68c12802c1c31162e840e970c961c53e
mode 100644,000000..100644
--- /dev/null
@@@ -1,107 -1,0 +1,117 @@@
 +// run-rustfix
 +#![allow(dead_code)]
 +#![warn(clippy::redundant_pub_crate)]
 +
 +mod m1 {
 +    fn f() {}
 +    pub fn g() {} // private due to m1
 +    pub fn h() {}
 +
 +    mod m1_1 {
 +        fn f() {}
 +        pub fn g() {} // private due to m1_1 and m1
 +        pub fn h() {}
 +    }
 +
 +    pub mod m1_2 {
 +        // ^ private due to m1
 +        fn f() {}
 +        pub fn g() {} // private due to m1_2 and m1
 +        pub fn h() {}
 +    }
 +
 +    pub mod m1_3 {
 +        fn f() {}
 +        pub fn g() {} // private due to m1
 +        pub fn h() {}
 +    }
 +}
 +
 +pub(crate) mod m2 {
 +    fn f() {}
 +    pub fn g() {} // already crate visible due to m2
 +    pub fn h() {}
 +
 +    mod m2_1 {
 +        fn f() {}
 +        pub fn g() {} // private due to m2_1
 +        pub fn h() {}
 +    }
 +
 +    pub mod m2_2 {
 +        // ^ already crate visible due to m2
 +        fn f() {}
 +        pub fn g() {} // already crate visible due to m2_2 and m2
 +        pub fn h() {}
 +    }
 +
 +    pub mod m2_3 {
 +        fn f() {}
 +        pub fn g() {} // already crate visible due to m2
 +        pub fn h() {}
 +    }
 +}
 +
 +pub mod m3 {
 +    fn f() {}
 +    pub(crate) fn g() {} // ok: m3 is exported
 +    pub fn h() {}
 +
 +    mod m3_1 {
 +        fn f() {}
 +        pub fn g() {} // private due to m3_1
 +        pub fn h() {}
 +    }
 +
 +    pub(crate) mod m3_2 {
 +        // ^ ok
 +        fn f() {}
 +        pub fn g() {} // already crate visible due to m3_2
 +        pub fn h() {}
 +    }
 +
 +    pub mod m3_3 {
 +        fn f() {}
 +        pub(crate) fn g() {} // ok: m3 and m3_3 are exported
 +        pub fn h() {}
 +    }
 +}
 +
 +mod m4 {
 +    fn f() {}
 +    pub fn g() {} // private: not re-exported by `pub use m4::*`
 +    pub fn h() {}
 +
 +    mod m4_1 {
 +        fn f() {}
 +        pub fn g() {} // private due to m4_1
 +        pub fn h() {}
 +    }
 +
 +    pub mod m4_2 {
 +        // ^ private: not re-exported by `pub use m4::*`
 +        fn f() {}
 +        pub fn g() {} // private due to m4_2
 +        pub fn h() {}
 +    }
 +
 +    pub mod m4_3 {
 +        fn f() {}
 +        pub(crate) fn g() {} // ok: m4_3 is re-exported by `pub use m4::*`
 +        pub fn h() {}
 +    }
 +}
 +
 +pub use m4::*;
 +
++mod issue_8732 {
++    #[allow(unused_macros)]
++    macro_rules! some_macro {
++        () => {};
++    }
++
++    #[allow(unused_imports)]
++    pub(crate) use some_macro; // ok: macro exports are exempt
++}
++
 +fn main() {}
index 616286b4f39f4fb7f04910d92416e8970f0d7c4c,0000000000000000000000000000000000000000..f96cfd3184384c6e09128891da36b02f88057064
mode 100644,000000..100644
--- /dev/null
@@@ -1,107 -1,0 +1,117 @@@
 +// run-rustfix
 +#![allow(dead_code)]
 +#![warn(clippy::redundant_pub_crate)]
 +
 +mod m1 {
 +    fn f() {}
 +    pub(crate) fn g() {} // private due to m1
 +    pub fn h() {}
 +
 +    mod m1_1 {
 +        fn f() {}
 +        pub(crate) fn g() {} // private due to m1_1 and m1
 +        pub fn h() {}
 +    }
 +
 +    pub(crate) mod m1_2 {
 +        // ^ private due to m1
 +        fn f() {}
 +        pub(crate) fn g() {} // private due to m1_2 and m1
 +        pub fn h() {}
 +    }
 +
 +    pub mod m1_3 {
 +        fn f() {}
 +        pub(crate) fn g() {} // private due to m1
 +        pub fn h() {}
 +    }
 +}
 +
 +pub(crate) mod m2 {
 +    fn f() {}
 +    pub(crate) fn g() {} // already crate visible due to m2
 +    pub fn h() {}
 +
 +    mod m2_1 {
 +        fn f() {}
 +        pub(crate) fn g() {} // private due to m2_1
 +        pub fn h() {}
 +    }
 +
 +    pub(crate) mod m2_2 {
 +        // ^ already crate visible due to m2
 +        fn f() {}
 +        pub(crate) fn g() {} // already crate visible due to m2_2 and m2
 +        pub fn h() {}
 +    }
 +
 +    pub mod m2_3 {
 +        fn f() {}
 +        pub(crate) fn g() {} // already crate visible due to m2
 +        pub fn h() {}
 +    }
 +}
 +
 +pub mod m3 {
 +    fn f() {}
 +    pub(crate) fn g() {} // ok: m3 is exported
 +    pub fn h() {}
 +
 +    mod m3_1 {
 +        fn f() {}
 +        pub(crate) fn g() {} // private due to m3_1
 +        pub fn h() {}
 +    }
 +
 +    pub(crate) mod m3_2 {
 +        // ^ ok
 +        fn f() {}
 +        pub(crate) fn g() {} // already crate visible due to m3_2
 +        pub fn h() {}
 +    }
 +
 +    pub mod m3_3 {
 +        fn f() {}
 +        pub(crate) fn g() {} // ok: m3 and m3_3 are exported
 +        pub fn h() {}
 +    }
 +}
 +
 +mod m4 {
 +    fn f() {}
 +    pub(crate) fn g() {} // private: not re-exported by `pub use m4::*`
 +    pub fn h() {}
 +
 +    mod m4_1 {
 +        fn f() {}
 +        pub(crate) fn g() {} // private due to m4_1
 +        pub fn h() {}
 +    }
 +
 +    pub(crate) mod m4_2 {
 +        // ^ private: not re-exported by `pub use m4::*`
 +        fn f() {}
 +        pub(crate) fn g() {} // private due to m4_2
 +        pub fn h() {}
 +    }
 +
 +    pub mod m4_3 {
 +        fn f() {}
 +        pub(crate) fn g() {} // ok: m4_3 is re-exported by `pub use m4::*`
 +        pub fn h() {}
 +    }
 +}
 +
 +pub use m4::*;
 +
++mod issue_8732 {
++    #[allow(unused_macros)]
++    macro_rules! some_macro {
++        () => {};
++    }
++
++    #[allow(unused_imports)]
++    pub(crate) use some_macro; // ok: macro exports are exempt
++}
++
 +fn main() {}
index 24a0c812291982371e67496695f2eb1b8275abe5,0000000000000000000000000000000000000000..9c4079ad6d306a39a07518382598878b349d39f9
mode 100644,000000..100644
--- /dev/null
@@@ -1,71 -1,0 +1,70 @@@
- //! Test for Clippy lint renames.
++// This file was generated by `cargo dev update_lints`.
++// Use that command to update this file and do not edit by hand.
++// Manual edits will be overwritten.
++
 +// run-rustfix
 +
- #![allow(dead_code)]
- // allow the new lint name here, to test if the new name works
- #![allow(clippy::module_name_repetitions)]
- #![allow(clippy::new_without_default)]
++#![allow(clippy::blocks_in_if_conditions)]
++#![allow(clippy::box_collection)]
 +#![allow(clippy::redundant_static_lifetimes)]
 +#![allow(clippy::cognitive_complexity)]
++#![allow(clippy::disallowed_methods)]
++#![allow(clippy::disallowed_types)]
++#![allow(clippy::for_loops_over_fallibles)]
++#![allow(clippy::useless_conversion)]
++#![allow(clippy::match_result_ok)]
++#![allow(clippy::new_without_default)]
 +#![allow(clippy::bind_instead_of_map)]
- #![allow(clippy::box_collection)]
- #![allow(clippy::blocks_in_if_conditions)]
++#![allow(clippy::expect_used)]
 +#![allow(clippy::map_unwrap_or)]
 +#![allow(clippy::unwrap_used)]
- #![allow(clippy::expect_used)]
- #![allow(clippy::for_loops_over_fallibles)]
- #![allow(clippy::useless_conversion)]
- #![allow(clippy::invisible_characters)]
++#![allow(clippy::needless_borrow)]
 +#![allow(clippy::single_char_add_str)]
- #![allow(clippy::match_result_ok)]
- #![allow(clippy::disallowed_types)]
- #![allow(clippy::disallowed_methods)]
++#![allow(clippy::module_name_repetitions)]
 +#![allow(clippy::recursive_format_impl)]
- // uplifted lints
- #![allow(invalid_value)]
- #![allow(array_into_iter)]
- #![allow(unused_labels)]
++#![allow(clippy::invisible_characters)]
 +#![allow(drop_bounds)]
- #![allow(temporary_cstring_as_ptr)]
- #![allow(non_fmt_panics)]
- #![allow(unknown_lints)]
++#![allow(array_into_iter)]
 +#![allow(invalid_atomic_ordering)]
++#![allow(invalid_value)]
 +#![allow(enum_intrinsics_non_enums)]
- // warn for the old lint name here, to test if the renaming worked
- #![warn(clippy::module_name_repetitions)]
- #![warn(clippy::new_without_default)]
++#![allow(non_fmt_panics)]
++#![allow(temporary_cstring_as_ptr)]
++#![allow(unknown_lints)]
++#![allow(unused_labels)]
++#![warn(clippy::blocks_in_if_conditions)]
++#![warn(clippy::blocks_in_if_conditions)]
++#![warn(clippy::box_collection)]
 +#![warn(clippy::redundant_static_lifetimes)]
 +#![warn(clippy::cognitive_complexity)]
++#![warn(clippy::disallowed_methods)]
++#![warn(clippy::disallowed_types)]
++#![warn(clippy::for_loops_over_fallibles)]
++#![warn(clippy::for_loops_over_fallibles)]
++#![warn(clippy::useless_conversion)]
++#![warn(clippy::match_result_ok)]
++#![warn(clippy::new_without_default)]
 +#![warn(clippy::bind_instead_of_map)]
- #![warn(clippy::box_collection)]
- #![warn(clippy::blocks_in_if_conditions)]
- #![warn(clippy::blocks_in_if_conditions)]
- #![warn(clippy::map_unwrap_or)]
++#![warn(clippy::expect_used)]
 +#![warn(clippy::map_unwrap_or)]
 +#![warn(clippy::map_unwrap_or)]
 +#![warn(clippy::unwrap_used)]
- #![warn(clippy::unwrap_used)]
- #![warn(clippy::expect_used)]
++#![warn(clippy::needless_borrow)]
 +#![warn(clippy::expect_used)]
- #![warn(clippy::for_loops_over_fallibles)]
- #![warn(clippy::for_loops_over_fallibles)]
- #![warn(clippy::useless_conversion)]
- #![warn(clippy::invisible_characters)]
++#![warn(clippy::map_unwrap_or)]
++#![warn(clippy::unwrap_used)]
 +#![warn(clippy::single_char_add_str)]
- #![warn(clippy::match_result_ok)]
- #![warn(clippy::disallowed_types)]
- #![warn(clippy::disallowed_methods)]
- #![warn(clippy::needless_borrow)]
++#![warn(clippy::module_name_repetitions)]
 +#![warn(clippy::recursive_format_impl)]
- // uplifted lints
- #![warn(invalid_value)]
- #![warn(array_into_iter)]
- #![warn(unused_labels)]
++#![warn(clippy::invisible_characters)]
 +#![warn(drop_bounds)]
- #![warn(temporary_cstring_as_ptr)]
- #![warn(non_fmt_panics)]
- #![warn(unknown_lints)]
++#![warn(array_into_iter)]
 +#![warn(invalid_atomic_ordering)]
++#![warn(invalid_value)]
 +#![warn(enum_intrinsics_non_enums)]
++#![warn(non_fmt_panics)]
++#![warn(temporary_cstring_as_ptr)]
++#![warn(unknown_lints)]
++#![warn(unused_labels)]
 +
 +fn main() {}
index ea64234c680d37f5ef8b7951100723a5a33a3054,0000000000000000000000000000000000000000..e83e66b7fbd4211dbfc80dd2c903a5fcb807ca94
mode 100644,000000..100644
--- /dev/null
@@@ -1,71 -1,0 +1,70 @@@
- //! Test for Clippy lint renames.
++// This file was generated by `cargo dev update_lints`.
++// Use that command to update this file and do not edit by hand.
++// Manual edits will be overwritten.
++
 +// run-rustfix
 +
- #![allow(dead_code)]
- // allow the new lint name here, to test if the new name works
- #![allow(clippy::module_name_repetitions)]
- #![allow(clippy::new_without_default)]
++#![allow(clippy::blocks_in_if_conditions)]
++#![allow(clippy::box_collection)]
 +#![allow(clippy::redundant_static_lifetimes)]
 +#![allow(clippy::cognitive_complexity)]
++#![allow(clippy::disallowed_methods)]
++#![allow(clippy::disallowed_types)]
++#![allow(clippy::for_loops_over_fallibles)]
++#![allow(clippy::useless_conversion)]
++#![allow(clippy::match_result_ok)]
++#![allow(clippy::new_without_default)]
 +#![allow(clippy::bind_instead_of_map)]
- #![allow(clippy::box_collection)]
- #![allow(clippy::blocks_in_if_conditions)]
++#![allow(clippy::expect_used)]
 +#![allow(clippy::map_unwrap_or)]
 +#![allow(clippy::unwrap_used)]
- #![allow(clippy::expect_used)]
- #![allow(clippy::for_loops_over_fallibles)]
- #![allow(clippy::useless_conversion)]
- #![allow(clippy::invisible_characters)]
++#![allow(clippy::needless_borrow)]
 +#![allow(clippy::single_char_add_str)]
- #![allow(clippy::match_result_ok)]
- #![allow(clippy::disallowed_types)]
- #![allow(clippy::disallowed_methods)]
++#![allow(clippy::module_name_repetitions)]
 +#![allow(clippy::recursive_format_impl)]
- // uplifted lints
- #![allow(invalid_value)]
- #![allow(array_into_iter)]
- #![allow(unused_labels)]
++#![allow(clippy::invisible_characters)]
 +#![allow(drop_bounds)]
- #![allow(temporary_cstring_as_ptr)]
- #![allow(non_fmt_panics)]
- #![allow(unknown_lints)]
++#![allow(array_into_iter)]
 +#![allow(invalid_atomic_ordering)]
++#![allow(invalid_value)]
 +#![allow(enum_intrinsics_non_enums)]
- // warn for the old lint name here, to test if the renaming worked
- #![warn(clippy::stutter)]
- #![warn(clippy::new_without_default_derive)]
++#![allow(non_fmt_panics)]
++#![allow(temporary_cstring_as_ptr)]
++#![allow(unknown_lints)]
++#![allow(unused_labels)]
++#![warn(clippy::block_in_if_condition_expr)]
++#![warn(clippy::block_in_if_condition_stmt)]
++#![warn(clippy::box_vec)]
 +#![warn(clippy::const_static_lifetime)]
 +#![warn(clippy::cyclomatic_complexity)]
++#![warn(clippy::disallowed_method)]
++#![warn(clippy::disallowed_type)]
++#![warn(clippy::for_loop_over_option)]
++#![warn(clippy::for_loop_over_result)]
++#![warn(clippy::identity_conversion)]
++#![warn(clippy::if_let_some_result)]
++#![warn(clippy::new_without_default_derive)]
 +#![warn(clippy::option_and_then_some)]
- #![warn(clippy::box_vec)]
- #![warn(clippy::block_in_if_condition_expr)]
- #![warn(clippy::block_in_if_condition_stmt)]
++#![warn(clippy::option_expect_used)]
 +#![warn(clippy::option_map_unwrap_or)]
 +#![warn(clippy::option_map_unwrap_or_else)]
- #![warn(clippy::result_map_unwrap_or_else)]
 +#![warn(clippy::option_unwrap_used)]
- #![warn(clippy::result_unwrap_used)]
- #![warn(clippy::option_expect_used)]
++#![warn(clippy::ref_in_deref)]
 +#![warn(clippy::result_expect_used)]
- #![warn(clippy::for_loop_over_option)]
- #![warn(clippy::for_loop_over_result)]
- #![warn(clippy::identity_conversion)]
- #![warn(clippy::zero_width_space)]
++#![warn(clippy::result_map_unwrap_or_else)]
++#![warn(clippy::result_unwrap_used)]
 +#![warn(clippy::single_char_push_str)]
- #![warn(clippy::if_let_some_result)]
- #![warn(clippy::disallowed_type)]
- #![warn(clippy::disallowed_method)]
- #![warn(clippy::ref_in_deref)]
++#![warn(clippy::stutter)]
 +#![warn(clippy::to_string_in_display)]
- // uplifted lints
- #![warn(clippy::invalid_ref)]
- #![warn(clippy::into_iter_on_array)]
- #![warn(clippy::unused_label)]
++#![warn(clippy::zero_width_space)]
 +#![warn(clippy::drop_bounds)]
- #![warn(clippy::temporary_cstring_as_ptr)]
- #![warn(clippy::panic_params)]
- #![warn(clippy::unknown_clippy_lints)]
++#![warn(clippy::into_iter_on_array)]
 +#![warn(clippy::invalid_atomic_ordering)]
++#![warn(clippy::invalid_ref)]
 +#![warn(clippy::mem_discriminant_non_enum)]
++#![warn(clippy::panic_params)]
++#![warn(clippy::temporary_cstring_as_ptr)]
++#![warn(clippy::unknown_clippy_lints)]
++#![warn(clippy::unused_label)]
 +
 +fn main() {}
index 8b132a7838470cbd90aba6f1e1c36be73274e8bc,0000000000000000000000000000000000000000..f811b10d017104220a46575d3aa7d4af4ce9f827
mode 100644,000000..100644
--- /dev/null
@@@ -1,208 -1,0 +1,208 @@@
- error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
++error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
 +  --> $DIR/rename.rs:35:9
 +   |
- LL | #![warn(clippy::stutter)]
-    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
++LL | #![warn(clippy::block_in_if_condition_expr)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
 +   |
 +   = note: `-D renamed-and-removed-lints` implied by `-D warnings`
 +
- error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
++error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
 +  --> $DIR/rename.rs:36:9
 +   |
- LL | #![warn(clippy::new_without_default_derive)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
++LL | #![warn(clippy::block_in_if_condition_stmt)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
 +
- error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
++error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
 +  --> $DIR/rename.rs:37:9
 +   |
++LL | #![warn(clippy::box_vec)]
++   |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
++
++error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
++  --> $DIR/rename.rs:38:9
++   |
 +LL | #![warn(clippy::const_static_lifetime)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
 +
 +error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
-   --> $DIR/rename.rs:38:9
++  --> $DIR/rename.rs:39:9
 +   |
 +LL | #![warn(clippy::cyclomatic_complexity)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
 +
- error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
-   --> $DIR/rename.rs:39:9
-    |
- LL | #![warn(clippy::option_and_then_some)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
- error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
++error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
 +  --> $DIR/rename.rs:40:9
 +   |
- LL | #![warn(clippy::box_vec)]
-    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
++LL | #![warn(clippy::disallowed_method)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
 +
- error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
++error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
 +  --> $DIR/rename.rs:41:9
 +   |
- LL | #![warn(clippy::block_in_if_condition_expr)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
++LL | #![warn(clippy::disallowed_type)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
 +
- error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
++error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles`
 +  --> $DIR/rename.rs:42:9
 +   |
- LL | #![warn(clippy::block_in_if_condition_stmt)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
++LL | #![warn(clippy::for_loop_over_option)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
 +
- error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
++error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles`
 +  --> $DIR/rename.rs:43:9
 +   |
- LL | #![warn(clippy::option_map_unwrap_or)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
++LL | #![warn(clippy::for_loop_over_result)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
 +
- error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
++error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
 +  --> $DIR/rename.rs:44:9
 +   |
- LL | #![warn(clippy::option_map_unwrap_or_else)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
++LL | #![warn(clippy::identity_conversion)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
 +
- error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
++error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
 +  --> $DIR/rename.rs:45:9
 +   |
- LL | #![warn(clippy::result_map_unwrap_or_else)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
++LL | #![warn(clippy::if_let_some_result)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
 +
- error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
++error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
 +  --> $DIR/rename.rs:46:9
 +   |
- LL | #![warn(clippy::option_unwrap_used)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
++LL | #![warn(clippy::new_without_default_derive)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
 +
- error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
++error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
 +  --> $DIR/rename.rs:47:9
 +   |
- LL | #![warn(clippy::result_unwrap_used)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
++LL | #![warn(clippy::option_and_then_some)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
 +
 +error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
 +  --> $DIR/rename.rs:48:9
 +   |
 +LL | #![warn(clippy::option_expect_used)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 +
- error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
++error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
 +  --> $DIR/rename.rs:49:9
 +   |
- LL | #![warn(clippy::result_expect_used)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
++LL | #![warn(clippy::option_map_unwrap_or)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 +
- error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles`
++error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
 +  --> $DIR/rename.rs:50:9
 +   |
- LL | #![warn(clippy::for_loop_over_option)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
++LL | #![warn(clippy::option_map_unwrap_or_else)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 +
- error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles`
++error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
 +  --> $DIR/rename.rs:51:9
 +   |
- LL | #![warn(clippy::for_loop_over_result)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
++LL | #![warn(clippy::option_unwrap_used)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 +
- error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
++error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
 +  --> $DIR/rename.rs:52:9
 +   |
- LL | #![warn(clippy::identity_conversion)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
++LL | #![warn(clippy::ref_in_deref)]
++   |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
 +
- error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
++error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
 +  --> $DIR/rename.rs:53:9
 +   |
- LL | #![warn(clippy::zero_width_space)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
++LL | #![warn(clippy::result_expect_used)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 +
- error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
++error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
 +  --> $DIR/rename.rs:54:9
 +   |
- LL | #![warn(clippy::single_char_push_str)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
++LL | #![warn(clippy::result_map_unwrap_or_else)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 +
- error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
++error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
 +  --> $DIR/rename.rs:55:9
 +   |
- LL | #![warn(clippy::if_let_some_result)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
++LL | #![warn(clippy::result_unwrap_used)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 +
- error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
++error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
 +  --> $DIR/rename.rs:56:9
 +   |
- LL | #![warn(clippy::disallowed_type)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
++LL | #![warn(clippy::single_char_push_str)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
 +
- error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
++error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
 +  --> $DIR/rename.rs:57:9
 +   |
- LL | #![warn(clippy::disallowed_method)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
++LL | #![warn(clippy::stutter)]
++   |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
 +
- error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
++error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
 +  --> $DIR/rename.rs:58:9
 +   |
- LL | #![warn(clippy::ref_in_deref)]
-    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
++LL | #![warn(clippy::to_string_in_display)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
 +
- error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
++error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
 +  --> $DIR/rename.rs:59:9
 +   |
- LL | #![warn(clippy::to_string_in_display)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
++LL | #![warn(clippy::zero_width_space)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
 +
- error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
-   --> $DIR/rename.rs:61:9
++error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
++  --> $DIR/rename.rs:60:9
 +   |
- LL | #![warn(clippy::invalid_ref)]
-    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
++LL | #![warn(clippy::drop_bounds)]
++   |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
 +
 +error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
-   --> $DIR/rename.rs:62:9
++  --> $DIR/rename.rs:61:9
 +   |
 +LL | #![warn(clippy::into_iter_on_array)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
 +
- error: lint `clippy::unused_label` has been renamed to `unused_labels`
++error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
++  --> $DIR/rename.rs:62:9
++   |
++LL | #![warn(clippy::invalid_atomic_ordering)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
++
++error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
 +  --> $DIR/rename.rs:63:9
 +   |
- LL | #![warn(clippy::unused_label)]
-    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
++LL | #![warn(clippy::invalid_ref)]
++   |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
 +
- error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
++error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
 +  --> $DIR/rename.rs:64:9
 +   |
- LL | #![warn(clippy::drop_bounds)]
-    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
++LL | #![warn(clippy::mem_discriminant_non_enum)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
 +
- error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
++error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
 +  --> $DIR/rename.rs:65:9
 +   |
- LL | #![warn(clippy::temporary_cstring_as_ptr)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
++LL | #![warn(clippy::panic_params)]
++   |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
 +
- error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
++error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
 +  --> $DIR/rename.rs:66:9
 +   |
- LL | #![warn(clippy::panic_params)]
-    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
++LL | #![warn(clippy::temporary_cstring_as_ptr)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
 +
 +error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
 +  --> $DIR/rename.rs:67:9
 +   |
 +LL | #![warn(clippy::unknown_clippy_lints)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
 +
- error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
++error: lint `clippy::unused_label` has been renamed to `unused_labels`
 +  --> $DIR/rename.rs:68:9
 +   |
- LL | #![warn(clippy::invalid_atomic_ordering)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
- error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
-   --> $DIR/rename.rs:69:9
-    |
- LL | #![warn(clippy::mem_discriminant_non_enum)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
++LL | #![warn(clippy::unused_label)]
++   |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
 +
 +error: aborting due to 34 previous errors
 +
index 38fc9969804fa593e46df30c22ed61ca3072dce6,0000000000000000000000000000000000000000..086331af6b5673fed75858630f90677e24f5e22c
mode 100644,000000..100644
--- /dev/null
@@@ -1,42 -1,0 +1,57 @@@
 +#![warn(clippy::rest_pat_in_fully_bound_structs)]
 +
 +struct A {
 +    a: i32,
 +    b: i64,
 +    c: &'static str,
 +}
 +
 +macro_rules! foo {
 +    ($param:expr) => {
 +        match $param {
 +            A { a: 0, b: 0, c: "", .. } => {},
 +            _ => {},
 +        }
 +    };
 +}
 +
 +fn main() {
 +    let a_struct = A { a: 5, b: 42, c: "A" };
 +
 +    match a_struct {
 +        A { a: 5, b: 42, c: "", .. } => {}, // Lint
 +        A { a: 0, b: 0, c: "", .. } => {},  // Lint
 +        _ => {},
 +    }
 +
 +    match a_struct {
 +        A { a: 5, b: 42, .. } => {},
 +        A { a: 0, b: 0, c: "", .. } => {}, // Lint
 +        _ => {},
 +    }
 +
 +    // No lint
 +    match a_struct {
 +        A { a: 5, .. } => {},
 +        A { a: 0, b: 0, .. } => {},
 +        _ => {},
 +    }
 +
 +    // No lint
 +    foo!(a_struct);
++
++    #[non_exhaustive]
++    struct B {
++        a: u32,
++        b: u32,
++        c: u64,
++    }
++
++    let b_struct = B { a: 5, b: 42, c: 342 };
++
++    match b_struct {
++        B { a: 5, b: 42, .. } => {},
++        B { a: 0, b: 0, c: 128, .. } => {}, // No Lint
++        _ => {},
++    }
 +}
index 7f28f0257904578f9d42b7900eda8df4bc38e530,0000000000000000000000000000000000000000..3d2295912c9fd08827596a2cef226b76f58e17b0
mode 100644,000000..100644
--- /dev/null
@@@ -1,90 -1,0 +1,109 @@@
++#![feature(adt_const_params)]
++#![allow(incomplete_features)]
 +#![warn(clippy::same_functions_in_if_condition)]
 +#![allow(clippy::ifs_same_cond)] // This warning is different from `ifs_same_cond`.
 +#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks
 +
 +fn function() -> bool {
 +    true
 +}
 +
 +fn fn_arg(_arg: u8) -> bool {
 +    true
 +}
 +
 +struct Struct;
 +
 +impl Struct {
 +    fn method(&self) -> bool {
 +        true
 +    }
 +    fn method_arg(&self, _arg: u8) -> bool {
 +        true
 +    }
 +}
 +
 +fn ifs_same_cond_fn() {
 +    let a = 0;
 +    let obj = Struct;
 +
 +    if function() {
 +    } else if function() {
 +        //~ ERROR ifs same condition
 +    }
 +
 +    if fn_arg(a) {
 +    } else if fn_arg(a) {
 +        //~ ERROR ifs same condition
 +    }
 +
 +    if obj.method() {
 +    } else if obj.method() {
 +        //~ ERROR ifs same condition
 +    }
 +
 +    if obj.method_arg(a) {
 +    } else if obj.method_arg(a) {
 +        //~ ERROR ifs same condition
 +    }
 +
 +    let mut v = vec![1];
 +    if v.pop() == None {
 +        //~ ERROR ifs same condition
 +    } else if v.pop() == None {
 +    }
 +
 +    if v.len() == 42 {
 +        //~ ERROR ifs same condition
 +    } else if v.len() == 42 {
 +    }
 +
 +    if v.len() == 1 {
 +        // ok, different conditions
 +    } else if v.len() == 2 {
 +    }
 +
 +    if fn_arg(0) {
 +        // ok, different arguments.
 +    } else if fn_arg(1) {
 +    }
 +
 +    if obj.method_arg(0) {
 +        // ok, different arguments.
 +    } else if obj.method_arg(1) {
 +    }
 +
 +    if a == 1 {
 +        // ok, warning is on `ifs_same_cond` behalf.
 +    } else if a == 1 {
 +    }
 +}
 +
 +fn main() {
 +    // macro as condition (see #6168)
 +    let os = if cfg!(target_os = "macos") {
 +        "macos"
 +    } else if cfg!(target_os = "windows") {
 +        "windows"
 +    } else {
 +        "linux"
 +    };
 +    println!("{}", os);
++
++    #[derive(PartialEq, Eq)]
++    enum E {
++        A,
++        B,
++    }
++    fn generic<const P: E>() -> bool {
++        match P {
++            E::A => true,
++            E::B => false,
++        }
++    }
++    if generic::<{ E::A }>() {
++        println!("A");
++    } else if generic::<{ E::B }>() {
++        println!("B");
++    }
 +}
index 363a03846d236e72ce5b2cd3296e16bb4d7aaf02,0000000000000000000000000000000000000000..71e82910ef7529e6cbef6c67426b334301941ba0
mode 100644,000000..100644
--- /dev/null
@@@ -1,75 -1,0 +1,75 @@@
-   --> $DIR/same_functions_in_if_condition.rs:29:15
 +error: this `if` has the same function call as a previous `if`
-   --> $DIR/same_functions_in_if_condition.rs:28:8
++  --> $DIR/same_functions_in_if_condition.rs:31:15
 +   |
 +LL |     } else if function() {
 +   |               ^^^^^^^^^^
 +   |
 +   = note: `-D clippy::same-functions-in-if-condition` implied by `-D warnings`
 +note: same as this
-   --> $DIR/same_functions_in_if_condition.rs:34:15
++  --> $DIR/same_functions_in_if_condition.rs:30:8
 +   |
 +LL |     if function() {
 +   |        ^^^^^^^^^^
 +
 +error: this `if` has the same function call as a previous `if`
-   --> $DIR/same_functions_in_if_condition.rs:33:8
++  --> $DIR/same_functions_in_if_condition.rs:36:15
 +   |
 +LL |     } else if fn_arg(a) {
 +   |               ^^^^^^^^^
 +   |
 +note: same as this
-   --> $DIR/same_functions_in_if_condition.rs:39:15
++  --> $DIR/same_functions_in_if_condition.rs:35:8
 +   |
 +LL |     if fn_arg(a) {
 +   |        ^^^^^^^^^
 +
 +error: this `if` has the same function call as a previous `if`
-   --> $DIR/same_functions_in_if_condition.rs:38:8
++  --> $DIR/same_functions_in_if_condition.rs:41:15
 +   |
 +LL |     } else if obj.method() {
 +   |               ^^^^^^^^^^^^
 +   |
 +note: same as this
-   --> $DIR/same_functions_in_if_condition.rs:44:15
++  --> $DIR/same_functions_in_if_condition.rs:40:8
 +   |
 +LL |     if obj.method() {
 +   |        ^^^^^^^^^^^^
 +
 +error: this `if` has the same function call as a previous `if`
-   --> $DIR/same_functions_in_if_condition.rs:43:8
++  --> $DIR/same_functions_in_if_condition.rs:46:15
 +   |
 +LL |     } else if obj.method_arg(a) {
 +   |               ^^^^^^^^^^^^^^^^^
 +   |
 +note: same as this
-   --> $DIR/same_functions_in_if_condition.rs:51:15
++  --> $DIR/same_functions_in_if_condition.rs:45:8
 +   |
 +LL |     if obj.method_arg(a) {
 +   |        ^^^^^^^^^^^^^^^^^
 +
 +error: this `if` has the same function call as a previous `if`
-   --> $DIR/same_functions_in_if_condition.rs:49:8
++  --> $DIR/same_functions_in_if_condition.rs:53:15
 +   |
 +LL |     } else if v.pop() == None {
 +   |               ^^^^^^^^^^^^^^^
 +   |
 +note: same as this
-   --> $DIR/same_functions_in_if_condition.rs:56:15
++  --> $DIR/same_functions_in_if_condition.rs:51:8
 +   |
 +LL |     if v.pop() == None {
 +   |        ^^^^^^^^^^^^^^^
 +
 +error: this `if` has the same function call as a previous `if`
-   --> $DIR/same_functions_in_if_condition.rs:54:8
++  --> $DIR/same_functions_in_if_condition.rs:58:15
 +   |
 +LL |     } else if v.len() == 42 {
 +   |               ^^^^^^^^^^^^^
 +   |
 +note: same as this
++  --> $DIR/same_functions_in_if_condition.rs:56:8
 +   |
 +LL |     if v.len() == 42 {
 +   |        ^^^^^^^^^^^^^
 +
 +error: aborting due to 6 previous errors
 +
index 0321f8c4cdf84ba14f107536ba7019033d09d851,0000000000000000000000000000000000000000..a394ef8f25c675b529db3c275eb97308fe835b5b
mode 100644,000000..100644
--- /dev/null
@@@ -1,90 -1,0 +1,91 @@@
 +#![warn(clippy::shadow_same, clippy::shadow_reuse, clippy::shadow_unrelated)]
++#![allow(clippy::let_unit_value)]
 +
 +fn shadow_same() {
 +    let x = 1;
 +    let x = x;
 +    let mut x = &x;
 +    let x = &mut x;
 +    let x = *x;
 +}
 +
 +fn shadow_reuse() -> Option<()> {
 +    let x = ([[0]], ());
 +    let x = x.0;
 +    let x = x[0];
 +    let [x] = x;
 +    let x = Some(x);
 +    let x = foo(x);
 +    let x = || x;
 +    let x = Some(1).map(|_| x)?;
 +    let y = 1;
 +    let y = match y {
 +        1 => 2,
 +        _ => 3,
 +    };
 +    None
 +}
 +
 +fn shadow_unrelated() {
 +    let x = 1;
 +    let x = 2;
 +}
 +
 +fn syntax() {
 +    fn f(x: u32) {
 +        let x = 1;
 +    }
 +    let x = 1;
 +    match Some(1) {
 +        Some(1) => {},
 +        Some(x) => {
 +            let x = 1;
 +        },
 +        _ => {},
 +    }
 +    if let Some(x) = Some(1) {}
 +    while let Some(x) = Some(1) {}
 +    let _ = |[x]: [u32; 1]| {
 +        let x = 1;
 +    };
 +    let y = Some(1);
 +    if let Some(y) = y {}
 +}
 +
 +fn negative() {
 +    match Some(1) {
 +        Some(x) if x == 1 => {},
 +        Some(x) => {},
 +        None => {},
 +    }
 +    match [None, Some(1)] {
 +        [Some(x), None] | [None, Some(x)] => {},
 +        _ => {},
 +    }
 +    if let Some(x) = Some(1) {
 +        let y = 1;
 +    } else {
 +        let x = 1;
 +        let y = 1;
 +    }
 +    let x = 1;
 +    #[allow(clippy::shadow_unrelated)]
 +    let x = 1;
 +}
 +
 +fn foo<T>(_: T) {}
 +
 +fn question_mark() -> Option<()> {
 +    let val = 1;
 +    // `?` expands with a `val` binding
 +    None?;
 +    None
 +}
 +
 +pub async fn foo1(_a: i32) {}
 +
 +pub async fn foo2(_a: i32, _b: i64) {
 +    let _b = _a;
 +}
 +
 +fn main() {}
index f8b9221d555871d7f6fbd4378857a619da51f41f,0000000000000000000000000000000000000000..3bd41d0626049da49e07ce5c0f50ce10c6f2c0d4
mode 100644,000000..100644
--- /dev/null
@@@ -1,269 -1,0 +1,269 @@@
-   --> $DIR/shadow.rs:5:9
 +error: `x` is shadowed by itself in `x`
-   --> $DIR/shadow.rs:4:9
++  --> $DIR/shadow.rs:6:9
 +   |
 +LL |     let x = x;
 +   |         ^
 +   |
 +   = note: `-D clippy::shadow-same` implied by `-D warnings`
 +note: previous binding is here
-   --> $DIR/shadow.rs:6:13
++  --> $DIR/shadow.rs:5:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `mut x` is shadowed by itself in `&x`
-   --> $DIR/shadow.rs:5:9
++  --> $DIR/shadow.rs:7:13
 +   |
 +LL |     let mut x = &x;
 +   |             ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:7:9
++  --> $DIR/shadow.rs:6:9
 +   |
 +LL |     let x = x;
 +   |         ^
 +
 +error: `x` is shadowed by itself in `&mut x`
-   --> $DIR/shadow.rs:6:9
++  --> $DIR/shadow.rs:8:9
 +   |
 +LL |     let x = &mut x;
 +   |         ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:8:9
++  --> $DIR/shadow.rs:7:9
 +   |
 +LL |     let mut x = &x;
 +   |         ^^^^^
 +
 +error: `x` is shadowed by itself in `*x`
-   --> $DIR/shadow.rs:7:9
++  --> $DIR/shadow.rs:9:9
 +   |
 +LL |     let x = *x;
 +   |         ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:13:9
++  --> $DIR/shadow.rs:8:9
 +   |
 +LL |     let x = &mut x;
 +   |         ^
 +
 +error: `x` is shadowed
-   --> $DIR/shadow.rs:12:9
++  --> $DIR/shadow.rs:14:9
 +   |
 +LL |     let x = x.0;
 +   |         ^
 +   |
 +   = note: `-D clippy::shadow-reuse` implied by `-D warnings`
 +note: previous binding is here
-   --> $DIR/shadow.rs:14:9
++  --> $DIR/shadow.rs:13:9
 +   |
 +LL |     let x = ([[0]], ());
 +   |         ^
 +
 +error: `x` is shadowed
-   --> $DIR/shadow.rs:13:9
++  --> $DIR/shadow.rs:15:9
 +   |
 +LL |     let x = x[0];
 +   |         ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:15:10
++  --> $DIR/shadow.rs:14:9
 +   |
 +LL |     let x = x.0;
 +   |         ^
 +
 +error: `x` is shadowed
-   --> $DIR/shadow.rs:14:9
++  --> $DIR/shadow.rs:16:10
 +   |
 +LL |     let [x] = x;
 +   |          ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:16:9
++  --> $DIR/shadow.rs:15:9
 +   |
 +LL |     let x = x[0];
 +   |         ^
 +
 +error: `x` is shadowed
-   --> $DIR/shadow.rs:15:10
++  --> $DIR/shadow.rs:17:9
 +   |
 +LL |     let x = Some(x);
 +   |         ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:17:9
++  --> $DIR/shadow.rs:16:10
 +   |
 +LL |     let [x] = x;
 +   |          ^
 +
 +error: `x` is shadowed
-   --> $DIR/shadow.rs:16:9
++  --> $DIR/shadow.rs:18:9
 +   |
 +LL |     let x = foo(x);
 +   |         ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:18:9
++  --> $DIR/shadow.rs:17:9
 +   |
 +LL |     let x = Some(x);
 +   |         ^
 +
 +error: `x` is shadowed
-   --> $DIR/shadow.rs:17:9
++  --> $DIR/shadow.rs:19:9
 +   |
 +LL |     let x = || x;
 +   |         ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:19:9
++  --> $DIR/shadow.rs:18:9
 +   |
 +LL |     let x = foo(x);
 +   |         ^
 +
 +error: `x` is shadowed
-   --> $DIR/shadow.rs:18:9
++  --> $DIR/shadow.rs:20:9
 +   |
 +LL |     let x = Some(1).map(|_| x)?;
 +   |         ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:21:9
++  --> $DIR/shadow.rs:19:9
 +   |
 +LL |     let x = || x;
 +   |         ^
 +
 +error: `y` is shadowed
-   --> $DIR/shadow.rs:20:9
++  --> $DIR/shadow.rs:22:9
 +   |
 +LL |     let y = match y {
 +   |         ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:30:9
++  --> $DIR/shadow.rs:21:9
 +   |
 +LL |     let y = 1;
 +   |         ^
 +
 +error: `x` shadows a previous, unrelated binding
-   --> $DIR/shadow.rs:29:9
++  --> $DIR/shadow.rs:31:9
 +   |
 +LL |     let x = 2;
 +   |         ^
 +   |
 +   = note: `-D clippy::shadow-unrelated` implied by `-D warnings`
 +note: previous binding is here
-   --> $DIR/shadow.rs:35:13
++  --> $DIR/shadow.rs:30:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `x` shadows a previous, unrelated binding
-   --> $DIR/shadow.rs:34:10
++  --> $DIR/shadow.rs:36:13
 +   |
 +LL |         let x = 1;
 +   |             ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:40:14
++  --> $DIR/shadow.rs:35:10
 +   |
 +LL |     fn f(x: u32) {
 +   |          ^
 +
 +error: `x` shadows a previous, unrelated binding
-   --> $DIR/shadow.rs:37:9
++  --> $DIR/shadow.rs:41:14
 +   |
 +LL |         Some(x) => {
 +   |              ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:41:17
++  --> $DIR/shadow.rs:38:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `x` shadows a previous, unrelated binding
-   --> $DIR/shadow.rs:40:14
++  --> $DIR/shadow.rs:42:17
 +   |
 +LL |             let x = 1;
 +   |                 ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:45:17
++  --> $DIR/shadow.rs:41:14
 +   |
 +LL |         Some(x) => {
 +   |              ^
 +
 +error: `x` shadows a previous, unrelated binding
-   --> $DIR/shadow.rs:37:9
++  --> $DIR/shadow.rs:46:17
 +   |
 +LL |     if let Some(x) = Some(1) {}
 +   |                 ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:46:20
++  --> $DIR/shadow.rs:38:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `x` shadows a previous, unrelated binding
-   --> $DIR/shadow.rs:37:9
++  --> $DIR/shadow.rs:47:20
 +   |
 +LL |     while let Some(x) = Some(1) {}
 +   |                    ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:47:15
++  --> $DIR/shadow.rs:38:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `x` shadows a previous, unrelated binding
-   --> $DIR/shadow.rs:37:9
++  --> $DIR/shadow.rs:48:15
 +   |
 +LL |     let _ = |[x]: [u32; 1]| {
 +   |               ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:48:13
++  --> $DIR/shadow.rs:38:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `x` shadows a previous, unrelated binding
-   --> $DIR/shadow.rs:47:15
++  --> $DIR/shadow.rs:49:13
 +   |
 +LL |         let x = 1;
 +   |             ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:51:17
++  --> $DIR/shadow.rs:48:15
 +   |
 +LL |     let _ = |[x]: [u32; 1]| {
 +   |               ^
 +
 +error: `y` is shadowed
-   --> $DIR/shadow.rs:50:9
++  --> $DIR/shadow.rs:52:17
 +   |
 +LL |     if let Some(y) = y {}
 +   |                 ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:87:9
++  --> $DIR/shadow.rs:51:9
 +   |
 +LL |     let y = Some(1);
 +   |         ^
 +
 +error: `_b` shadows a previous, unrelated binding
-   --> $DIR/shadow.rs:86:28
++  --> $DIR/shadow.rs:88:9
 +   |
 +LL |     let _b = _a;
 +   |         ^^
 +   |
 +note: previous binding is here
++  --> $DIR/shadow.rs:87:28
 +   |
 +LL | pub async fn foo2(_a: i32, _b: i64) {
 +   |                            ^^
 +
 +error: aborting due to 22 previous errors
 +
index 76f6ce9ee6b47f3826824a35e2495c0513d80c23,0000000000000000000000000000000000000000..c21225d153bd6cf7d928592030e8e44a25cb1cc2
mode 100644,000000..100644
--- /dev/null
@@@ -1,120 -1,0 +1,121 @@@
-     clippy::diverging_sub_expression
 +#![warn(clippy::similar_names)]
 +#![allow(
 +    unused,
 +    clippy::println_empty_string,
 +    clippy::empty_loop,
++    clippy::diverging_sub_expression,
++    clippy::let_unit_value
 +)]
 +
 +struct Foo {
 +    apple: i32,
 +    bpple: i32,
 +}
 +
 +fn main() {
 +    let specter: i32;
 +    let spectre: i32;
 +
 +    let apple: i32;
 +
 +    let bpple: i32;
 +
 +    let cpple: i32;
 +
 +    let a_bar: i32;
 +    let b_bar: i32;
 +    let c_bar: i32;
 +
 +    let items = [5];
 +    for item in &items {
 +        loop {}
 +    }
 +
 +    let foo_x: i32;
 +    let foo_y: i32;
 +
 +    let rhs: i32;
 +    let lhs: i32;
 +
 +    let bla_rhs: i32;
 +    let bla_lhs: i32;
 +
 +    let blubrhs: i32;
 +    let blublhs: i32;
 +
 +    let blubx: i32;
 +    let bluby: i32;
 +
 +    let cake: i32;
 +    let cakes: i32;
 +    let coke: i32;
 +
 +    match 5 {
 +        cheese @ 1 => {},
 +        rabbit => panic!(),
 +    }
 +    let cheese: i32;
 +    match (42, 43) {
 +        (cheese1, 1) => {},
 +        (cheese2, 2) => panic!(),
 +        _ => println!(""),
 +    }
 +    let ipv4: i32;
 +    let ipv6: i32;
 +    let abcd1: i32;
 +    let abdc2: i32;
 +    let xyz1abc: i32;
 +    let xyz2abc: i32;
 +    let xyzeabc: i32;
 +
 +    let parser: i32;
 +    let parsed: i32;
 +    let parsee: i32;
 +
 +    let setter: i32;
 +    let getter: i32;
 +    let tx1: i32;
 +    let rx1: i32;
 +    let tx_cake: i32;
 +    let rx_cake: i32;
 +
 +    // names often used in win32 code (for example WindowProc)
 +    let wparam: i32;
 +    let lparam: i32;
 +
 +    let iter: i32;
 +    let item: i32;
 +}
 +
 +fn foo() {
 +    let Foo { apple, bpple } = unimplemented!();
 +    let Foo {
 +        apple: spring,
 +        bpple: sprang,
 +    } = unimplemented!();
 +}
 +
 +// false positive similar_names (#3057, #2651)
 +// clippy claimed total_reg_src_size and total_size and
 +// numb_reg_src_checkouts and total_bin_size were similar
 +#[derive(Debug, Clone)]
 +pub(crate) struct DirSizes {
 +    pub(crate) total_size: u64,
 +    pub(crate) numb_bins: u64,
 +    pub(crate) total_bin_size: u64,
 +    pub(crate) total_reg_size: u64,
 +    pub(crate) total_git_db_size: u64,
 +    pub(crate) total_git_repos_bare_size: u64,
 +    pub(crate) numb_git_repos_bare_repos: u64,
 +    pub(crate) numb_git_checkouts: u64,
 +    pub(crate) total_git_chk_size: u64,
 +    pub(crate) total_reg_cache_size: u64,
 +    pub(crate) total_reg_src_size: u64,
 +    pub(crate) numb_reg_cache_entries: u64,
 +    pub(crate) numb_reg_src_checkouts: u64,
 +}
 +
 +fn ignore_underscore_prefix() {
 +    let hello: ();
 +    let _hello: ();
 +}
index faf572b0c6bc27010c8af554b37cd863e295ab2a,0000000000000000000000000000000000000000..6e77269389737946b636198d686918ec9e8b2f5a
mode 100644,000000..100644
--- /dev/null
@@@ -1,87 -1,0 +1,87 @@@
-   --> $DIR/similar_names.rs:20:9
 +error: binding's name is too similar to existing binding
-   --> $DIR/similar_names.rs:18:9
++  --> $DIR/similar_names.rs:21:9
 +   |
 +LL |     let bpple: i32;
 +   |         ^^^^^
 +   |
 +   = note: `-D clippy::similar-names` implied by `-D warnings`
 +note: existing binding defined here
-   --> $DIR/similar_names.rs:22:9
++  --> $DIR/similar_names.rs:19:9
 +   |
 +LL |     let apple: i32;
 +   |         ^^^^^
 +
 +error: binding's name is too similar to existing binding
-   --> $DIR/similar_names.rs:18:9
++  --> $DIR/similar_names.rs:23:9
 +   |
 +LL |     let cpple: i32;
 +   |         ^^^^^
 +   |
 +note: existing binding defined here
-   --> $DIR/similar_names.rs:46:9
++  --> $DIR/similar_names.rs:19:9
 +   |
 +LL |     let apple: i32;
 +   |         ^^^^^
 +
 +error: binding's name is too similar to existing binding
-   --> $DIR/similar_names.rs:45:9
++  --> $DIR/similar_names.rs:47:9
 +   |
 +LL |     let bluby: i32;
 +   |         ^^^^^
 +   |
 +note: existing binding defined here
-   --> $DIR/similar_names.rs:50:9
++  --> $DIR/similar_names.rs:46:9
 +   |
 +LL |     let blubx: i32;
 +   |         ^^^^^
 +
 +error: binding's name is too similar to existing binding
-   --> $DIR/similar_names.rs:48:9
++  --> $DIR/similar_names.rs:51:9
 +   |
 +LL |     let coke: i32;
 +   |         ^^^^
 +   |
 +note: existing binding defined here
-   --> $DIR/similar_names.rs:68:9
++  --> $DIR/similar_names.rs:49:9
 +   |
 +LL |     let cake: i32;
 +   |         ^^^^
 +
 +error: binding's name is too similar to existing binding
-   --> $DIR/similar_names.rs:66:9
++  --> $DIR/similar_names.rs:69:9
 +   |
 +LL |     let xyzeabc: i32;
 +   |         ^^^^^^^
 +   |
 +note: existing binding defined here
-   --> $DIR/similar_names.rs:72:9
++  --> $DIR/similar_names.rs:67:9
 +   |
 +LL |     let xyz1abc: i32;
 +   |         ^^^^^^^
 +
 +error: binding's name is too similar to existing binding
-   --> $DIR/similar_names.rs:70:9
++  --> $DIR/similar_names.rs:73:9
 +   |
 +LL |     let parsee: i32;
 +   |         ^^^^^^
 +   |
 +note: existing binding defined here
-   --> $DIR/similar_names.rs:93:16
++  --> $DIR/similar_names.rs:71:9
 +   |
 +LL |     let parser: i32;
 +   |         ^^^^^^
 +
 +error: binding's name is too similar to existing binding
-   --> $DIR/similar_names.rs:92:16
++  --> $DIR/similar_names.rs:94:16
 +   |
 +LL |         bpple: sprang,
 +   |                ^^^^^^
 +   |
 +note: existing binding defined here
++  --> $DIR/similar_names.rs:93:16
 +   |
 +LL |         apple: spring,
 +   |                ^^^^^^
 +
 +error: aborting due to 7 previous errors
 +
index 261d8bc7260ca5deab6ab1519d4d9387eebaac87,0000000000000000000000000000000000000000..69c5b236f7cf8dea4d221d6c444cc7fcb12e058e
mode 100644,000000..100644
--- /dev/null
@@@ -1,43 -1,0 +1,44 @@@
 +#![warn(clippy::single_char_lifetime_names)]
++#![allow(clippy::let_unit_value)]
 +
 +// Lifetimes should only be linted when they're introduced
 +struct DiagnosticCtx<'a, 'b>
 +where
 +    'a: 'b,
 +{
 +    _source: &'a str,
 +    _unit: &'b (),
 +}
 +
 +// Only the lifetimes on the `impl`'s generics should be linted
 +impl<'a, 'b> DiagnosticCtx<'a, 'b> {
 +    fn new(source: &'a str, unit: &'b ()) -> DiagnosticCtx<'a, 'b> {
 +        Self {
 +            _source: source,
 +            _unit: unit,
 +        }
 +    }
 +}
 +
 +// No lifetimes should be linted here
 +impl<'src, 'unit> DiagnosticCtx<'src, 'unit> {
 +    fn new_pass(source: &'src str, unit: &'unit ()) -> DiagnosticCtx<'src, 'unit> {
 +        Self {
 +            _source: source,
 +            _unit: unit,
 +        }
 +    }
 +}
 +
 +// Only 'a should be linted here
 +fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) {
 +    base.split_once(other)
 +        .map(|(left, right)| (left, Some(right)))
 +        .unwrap_or((base, None))
 +}
 +
 +fn main() {
 +    let src = "loop {}";
 +    let unit = ();
 +    DiagnosticCtx::new(src, &unit);
 +}
index 013b64f46a8ce1d2795a288c6864a8603dec0c6b,0000000000000000000000000000000000000000..1438b3999dba85ad1b4ddc810854958fb0296dfb
mode 100644,000000..100644
--- /dev/null
@@@ -1,43 -1,0 +1,43 @@@
-   --> $DIR/single_char_lifetime_names.rs:4:22
 +error: single-character lifetime names are likely uninformative
-   --> $DIR/single_char_lifetime_names.rs:4:26
++  --> $DIR/single_char_lifetime_names.rs:5:22
 +   |
 +LL | struct DiagnosticCtx<'a, 'b>
 +   |                      ^^
 +   |
 +   = note: `-D clippy::single-char-lifetime-names` implied by `-D warnings`
 +   = help: use a more informative name
 +
 +error: single-character lifetime names are likely uninformative
-   --> $DIR/single_char_lifetime_names.rs:13:6
++  --> $DIR/single_char_lifetime_names.rs:5:26
 +   |
 +LL | struct DiagnosticCtx<'a, 'b>
 +   |                          ^^
 +   |
 +   = help: use a more informative name
 +
 +error: single-character lifetime names are likely uninformative
-   --> $DIR/single_char_lifetime_names.rs:13:10
++  --> $DIR/single_char_lifetime_names.rs:14:6
 +   |
 +LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> {
 +   |      ^^
 +   |
 +   = help: use a more informative name
 +
 +error: single-character lifetime names are likely uninformative
-   --> $DIR/single_char_lifetime_names.rs:33:15
++  --> $DIR/single_char_lifetime_names.rs:14:10
 +   |
 +LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> {
 +   |          ^^
 +   |
 +   = help: use a more informative name
 +
 +error: single-character lifetime names are likely uninformative
++  --> $DIR/single_char_lifetime_names.rs:34:15
 +   |
 +LL | fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) {
 +   |               ^^
 +   |
 +   = help: use a more informative name
 +
 +error: aborting due to 5 previous errors
 +
index b624a41a29b2da10375b212d321afbabac572c6a,0000000000000000000000000000000000000000..82387f3d80b7780a0781dd116dfbbd341d47a3b8
mode 100644,000000..100644
--- /dev/null
@@@ -1,86 -1,0 +1,100 @@@
-     match ExprNode::Butterflies {
++// aux-build: proc_macro_with_span.rs
++
 +#![warn(clippy::single_match_else)]
 +#![allow(clippy::needless_return)]
 +#![allow(clippy::no_effect)]
 +
++extern crate proc_macro_with_span;
++use proc_macro_with_span::with_span;
++
 +enum ExprNode {
 +    ExprAddrOf,
 +    Butterflies,
 +    Unicorns,
 +}
 +
 +static NODE: ExprNode = ExprNode::Unicorns;
 +
 +fn unwrap_addr() -> Option<&'static ExprNode> {
-     }
++    let _ = match ExprNode::Butterflies {
++        ExprNode::ExprAddrOf => Some(&NODE),
++        _ => {
++            let x = 5;
++            None
++        },
++    };
++
++    // Don't lint
++    with_span!(span match ExprNode::Butterflies {
 +        ExprNode::ExprAddrOf => Some(&NODE),
 +        _ => {
 +            let x = 5;
 +            None
 +        },
++    })
 +}
 +
 +macro_rules! unwrap_addr {
 +    ($expression:expr) => {
 +        match $expression {
 +            ExprNode::ExprAddrOf => Some(&NODE),
 +            _ => {
 +                let x = 5;
 +                None
 +            },
 +        }
 +    };
 +}
 +
 +#[rustfmt::skip]
 +fn main() {
 +    unwrap_addr!(ExprNode::Unicorns);
 +
 +    //
 +    // don't lint single exprs/statements
 +    //
 +
 +    // don't lint here
 +    match Some(1) {
 +        Some(a) => println!("${:?}", a),
 +        None => return,
 +    }
 +
 +    // don't lint here
 +    match Some(1) {
 +        Some(a) => println!("${:?}", a),
 +        None => {
 +            return
 +        },
 +    }
 +
 +    // don't lint here
 +    match Some(1) {
 +        Some(a) => println!("${:?}", a),
 +        None => {
 +            return;
 +        },
 +    }
 +
 +    //
 +    // lint multiple exprs/statements "else" blocks
 +    //
 +
 +    // lint here
 +    match Some(1) {
 +        Some(a) => println!("${:?}", a),
 +        None => {
 +            println!("else block");
 +            return
 +        },
 +    }
 +
 +    // lint here
 +    match Some(1) {
 +        Some(a) => println!("${:?}", a),
 +        None => {
 +            println!("else block");
 +            return;
 +        },
 +    }
 +}
index 21ea704b62ab5c9134fcccdcda1d853a27af1c42,0000000000000000000000000000000000000000..7756c6f204e67eb016ecf792a3d6cfb7b70efc66
mode 100644,000000..100644
--- /dev/null
@@@ -1,23 -1,0 +1,24 @@@
-   --> $DIR/single_match_else.rs:14:5
 +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
- LL | /     match ExprNode::Butterflies {
++  --> $DIR/single_match_else.rs:19:13
 +   |
- LL | |     }
++LL |       let _ = match ExprNode::Butterflies {
++   |  _____________^
 +LL | |         ExprNode::ExprAddrOf => Some(&NODE),
 +LL | |         _ => {
 +LL | |             let x = 5;
 +LL | |             None
 +LL | |         },
- LL ~     if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else {
++LL | |     };
 +   | |_____^
 +   |
 +   = note: `-D clippy::single-match-else` implied by `-D warnings`
 +help: try this
 +   |
- LL +     }
++LL ~     let _ = if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else {
 +LL +         let x = 5;
 +LL +         None
++LL ~     };
 +   |
 +
 +error: aborting due to previous error
 +
index b8d22ed250467b533789ed47155142fcc5ec2d00,0000000000000000000000000000000000000000..c35e0c22ae8994a4cced07921e44d0b281f306c6
mode 100644,000000..100644
--- /dev/null
@@@ -1,59 -1,0 +1,59 @@@
-    = note: an unstable sort would perform faster without any observable difference for this data type
 +error: used `sort` on primitive type `i32`
 +  --> $DIR/stable_sort_primitive.rs:7:5
 +   |
 +LL |     vec.sort();
 +   |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
 +   |
 +   = note: `-D clippy::stable-sort-primitive` implied by `-D warnings`
-    = note: an unstable sort would perform faster without any observable difference for this data type
++   = note: an unstable sort typically performs faster without any observable difference for this data type
 +
 +error: used `sort` on primitive type `bool`
 +  --> $DIR/stable_sort_primitive.rs:9:5
 +   |
 +LL |     vec.sort();
 +   |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
 +   |
-    = note: an unstable sort would perform faster without any observable difference for this data type
++   = note: an unstable sort typically performs faster without any observable difference for this data type
 +
 +error: used `sort` on primitive type `char`
 +  --> $DIR/stable_sort_primitive.rs:11:5
 +   |
 +LL |     vec.sort();
 +   |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
 +   |
-    = note: an unstable sort would perform faster without any observable difference for this data type
++   = note: an unstable sort typically performs faster without any observable difference for this data type
 +
 +error: used `sort` on primitive type `str`
 +  --> $DIR/stable_sort_primitive.rs:13:5
 +   |
 +LL |     vec.sort();
 +   |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
 +   |
-    = note: an unstable sort would perform faster without any observable difference for this data type
++   = note: an unstable sort typically performs faster without any observable difference for this data type
 +
 +error: used `sort` on primitive type `tuple`
 +  --> $DIR/stable_sort_primitive.rs:15:5
 +   |
 +LL |     vec.sort();
 +   |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
 +   |
-    = note: an unstable sort would perform faster without any observable difference for this data type
++   = note: an unstable sort typically performs faster without any observable difference for this data type
 +
 +error: used `sort` on primitive type `array`
 +  --> $DIR/stable_sort_primitive.rs:17:5
 +   |
 +LL |     vec.sort();
 +   |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
 +   |
-    = note: an unstable sort would perform faster without any observable difference for this data type
++   = note: an unstable sort typically performs faster without any observable difference for this data type
 +
 +error: used `sort` on primitive type `i32`
 +  --> $DIR/stable_sort_primitive.rs:19:5
 +   |
 +LL |     arr.sort();
 +   |     ^^^^^^^^^^ help: try: `arr.sort_unstable()`
 +   |
++   = note: an unstable sort typically performs faster without any observable difference for this data type
 +
 +error: aborting due to 7 previous errors
 +
index fcd827a91c7f62d843bfd47a30016a58e9dbbf2d,0000000000000000000000000000000000000000..21753e5dc6a47c0546f43f6210392062821d9c9a
mode 100644,000000..100644
--- /dev/null
@@@ -1,115 -1,0 +1,115 @@@
- #![allow(clippy::if_same_then_else)]
 +// aux-build:proc_macro_suspicious_else_formatting.rs
 +
 +#![warn(clippy::suspicious_else_formatting)]
++#![allow(clippy::if_same_then_else, clippy::let_unit_value)]
 +
 +extern crate proc_macro_suspicious_else_formatting;
 +use proc_macro_suspicious_else_formatting::DeriveBadSpan;
 +
 +fn foo() -> bool {
 +    true
 +}
 +
 +#[rustfmt::skip]
 +fn main() {
 +    // weird `else` formatting:
 +    if foo() {
 +    } {
 +    }
 +
 +    if foo() {
 +    } if foo() {
 +    }
 +
 +    let _ = { // if as the last expression
 +        let _ = 0;
 +
 +        if foo() {
 +        } if foo() {
 +        }
 +        else {
 +        }
 +    };
 +
 +    let _ = { // if in the middle of a block
 +        if foo() {
 +        } if foo() {
 +        }
 +        else {
 +        }
 +
 +        let _ = 0;
 +    };
 +
 +    if foo() {
 +    } else
 +    {
 +    }
 +
 +    // This is fine, though weird. Allman style braces on the else.
 +    if foo() {
 +    }
 +    else
 +    {
 +    }
 +
 +    if foo() {
 +    } else
 +    if foo() { // the span of the above error should continue here
 +    }
 +
 +    if foo() {
 +    }
 +    else
 +    if foo() { // the span of the above error should continue here
 +    }
 +
 +    // those are ok:
 +    if foo() {
 +    }
 +    {
 +    }
 +
 +    if foo() {
 +    } else {
 +    }
 +
 +    if foo() {
 +    }
 +    else {
 +    }
 +
 +    if foo() {
 +    }
 +    if foo() {
 +    }
 +
 +    // Almost Allman style braces. Lint these.
 +    if foo() {
 +    }
 +
 +    else
 +    {
 +
 +    }
 +
 +    if foo() {
 +    }
 +    else
 +
 +    {
 +
 +    }
 +
 +    // #3864 - Allman style braces
 +    if foo()
 +    {
 +    }
 +    else
 +    {
 +    }
 +}
 +
 +// #7650 - Don't lint. Proc-macro using bad spans for `if` expressions.
 +#[derive(DeriveBadSpan)]
 +struct _Foo(u32, u32);
index 19184df0becb579fc6a505bf5924fc089aeb52e9,0000000000000000000000000000000000000000..3c5e9642714657676c3a950ca78c4ac51af3e687
mode 100644,000000..100644
--- /dev/null
@@@ -1,11 -1,0 +1,11 @@@
-     let _ = d.is_digit(10);
-     let _ = char::is_digit(c, 10);
 +//run-rustfix
 +
 +#![warn(clippy::to_digit_is_some)]
 +
 +fn main() {
 +    let c = 'x';
 +    let d = &c;
 +
++    let _ = d.is_digit(8);
++    let _ = char::is_digit(c, 8);
 +}
index 45a6728ebf57812f6facc06f55a104780bc4972b,0000000000000000000000000000000000000000..4f247c06ceedaba747c89d5ff4b602e55c5653cb
mode 100644,000000..100644
--- /dev/null
@@@ -1,11 -1,0 +1,11 @@@
-     let _ = d.to_digit(10).is_some();
-     let _ = char::to_digit(c, 10).is_some();
 +//run-rustfix
 +
 +#![warn(clippy::to_digit_is_some)]
 +
 +fn main() {
 +    let c = 'x';
 +    let d = &c;
 +
++    let _ = d.to_digit(8).is_some();
++    let _ = char::to_digit(c, 8).is_some();
 +}
index 177d3ccd3e23d7a4f507df4dc3dd053161ee1d8d,0000000000000000000000000000000000000000..10a1b393a3906fced44d5ec028e9e9666282312c
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,16 @@@
- LL |     let _ = d.to_digit(10).is_some();
-    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(10)`
 +error: use of `.to_digit(..).is_some()`
 +  --> $DIR/to_digit_is_some.rs:9:13
 +   |
- LL |     let _ = char::to_digit(c, 10).is_some();
-    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 10)`
++LL |     let _ = d.to_digit(8).is_some();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(8)`
 +   |
 +   = note: `-D clippy::to-digit-is-some` implied by `-D warnings`
 +
 +error: use of `.to_digit(..).is_some()`
 +  --> $DIR/to_digit_is_some.rs:10:13
 +   |
++LL |     let _ = char::to_digit(c, 8).is_some();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 8)`
 +
 +error: aborting due to 2 previous errors
 +
index f5ca91143af25683625d19ba51efe22d070e5233,0000000000000000000000000000000000000000..a21d4c5d637daec8678ce052656d1d86bff631aa
mode 100644,000000..100644
--- /dev/null
@@@ -1,98 -1,0 +1,101 @@@
 +#![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>,
 +    {
 +    }
 +}
 +
++// This should not lint
++fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
++
 +fn main() {}
index 6f8c8e47dfbf1fe56589a06f80017513e69167e8,0000000000000000000000000000000000000000..d0a4cfb88370e2060620137f7e015632a96ff3e7
mode 100644,000000..100644
--- /dev/null
@@@ -1,71 -1,0 +1,79 @@@
- error: aborting due to 8 previous errors
 +error: this trait bound is already specified in the where clause
 +  --> $DIR/trait_duplication_in_bounds.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.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.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.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.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.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.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.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.rs:93: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:99:23
++   |
++LL | fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
++   |                       ^^^^^^^^^^
++   |
++   = help: consider removing this trait bound
++
++error: aborting due to 9 previous errors
 +
index cd5a7127791a86784fe8332d91c2589e9ba10d49,0000000000000000000000000000000000000000..5a431bee04a45c03d8b54ee438a1b4b003aa611a
mode 100644,000000..100644
--- /dev/null
@@@ -1,47 -1,0 +1,50 @@@
- use std::mem::transmute;
 +#![warn(clippy::unsound_collection_transmute)]
 +
 +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
++use std::mem::{transmute, MaybeUninit};
 +
 +fn main() {
 +    unsafe {
 +        // wrong size
 +        let _ = transmute::<_, Vec<u32>>(vec![0u8]);
 +        // wrong layout
 +        let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]);
 +
 +        // wrong size
 +        let _ = transmute::<_, VecDeque<u32>>(VecDeque::<u8>::new());
 +        // wrong layout
 +        let _ = transmute::<_, VecDeque<u32>>(VecDeque::<[u8; 4]>::new());
 +
 +        // wrong size
 +        let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<u8>::new());
 +        // wrong layout
 +        let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<[u8; 4]>::new());
 +
 +        // wrong size
 +        let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<u8>::new());
 +        // wrong layout
 +        let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<[u8; 4]>::new());
 +
 +        // wrong size
 +        let _ = transmute::<_, HashSet<u32>>(HashSet::<u8>::new());
 +        // wrong layout
 +        let _ = transmute::<_, HashSet<u32>>(HashSet::<[u8; 4]>::new());
 +
 +        // wrong size
 +        let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, u8>::new());
 +        let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u32, u32>::new());
 +        // wrong layout
 +        let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, [u8; 4]>::new());
 +        let _ = transmute::<_, BTreeMap<u32, u32>>(BTreeMap::<[u8; 4], u32>::new());
 +
 +        // wrong size
 +        let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, u8>::new());
 +        let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u32, u32>::new());
 +        // wrong layout
 +        let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, [u8; 4]>::new());
 +        let _ = transmute::<_, HashMap<u32, u32>>(HashMap::<[u8; 4], u32>::new());
++
++        let _ = transmute::<_, Vec<u8>>(Vec::<MaybeUninit<u8>>::new());
++        let _ = transmute::<_, Vec<*mut u32>>(Vec::<Box<u32>>::new());
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e4d352f7367e4502e095b390c1e833f533e64518
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,91 @@@
++// run-rustfix
++#![warn(clippy::trim_split_whitespace)]
++#![allow(clippy::let_unit_value)]
++
++struct Custom;
++impl Custom {
++    fn trim(self) -> Self {
++        self
++    }
++    fn split_whitespace(self) {}
++}
++
++struct DerefStr(&'static str);
++impl std::ops::Deref for DerefStr {
++    type Target = str;
++    fn deref(&self) -> &Self::Target {
++        self.0
++    }
++}
++
++struct DerefStrAndCustom(&'static str);
++impl std::ops::Deref for DerefStrAndCustom {
++    type Target = str;
++    fn deref(&self) -> &Self::Target {
++        self.0
++    }
++}
++impl DerefStrAndCustom {
++    fn trim(self) -> Self {
++        self
++    }
++    fn split_whitespace(self) {}
++}
++
++struct DerefStrAndCustomSplit(&'static str);
++impl std::ops::Deref for DerefStrAndCustomSplit {
++    type Target = str;
++    fn deref(&self) -> &Self::Target {
++        self.0
++    }
++}
++impl DerefStrAndCustomSplit {
++    #[allow(dead_code)]
++    fn split_whitespace(self) {}
++}
++
++struct DerefStrAndCustomTrim(&'static str);
++impl std::ops::Deref for DerefStrAndCustomTrim {
++    type Target = str;
++    fn deref(&self) -> &Self::Target {
++        self.0
++    }
++}
++impl DerefStrAndCustomTrim {
++    fn trim(self) -> Self {
++        self
++    }
++}
++
++fn main() {
++    // &str
++    let _ = " A B C ".split_whitespace(); // should trigger lint
++    let _ = " A B C ".split_whitespace(); // should trigger lint
++    let _ = " A B C ".split_whitespace(); // should trigger lint
++
++    // String
++    let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint
++    let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint
++    let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint
++
++    // Custom
++    let _ = Custom.trim().split_whitespace(); // should not trigger lint
++
++    // Deref<Target=str>
++    let s = DerefStr(" A B C ");
++    let _ = s.split_whitespace(); // should trigger lint
++
++    // Deref<Target=str> + custom impl
++    let s = DerefStrAndCustom(" A B C ");
++    let _ = s.trim().split_whitespace(); // should not trigger lint
++
++    // Deref<Target=str> + only custom split_ws() impl
++    let s = DerefStrAndCustomSplit(" A B C ");
++    let _ = s.split_whitespace(); // should trigger lint
++    // Expl: trim() is called on str (deref) and returns &str.
++    //       Thus split_ws() is called on str as well and the custom impl on S is unused
++
++    // Deref<Target=str> + only custom trim() impl
++    let s = DerefStrAndCustomTrim(" A B C ");
++    let _ = s.trim().split_whitespace(); // should not trigger lint
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f98451a983712c552019b4b9122f3c347eae72d6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,91 @@@
++// run-rustfix
++#![warn(clippy::trim_split_whitespace)]
++#![allow(clippy::let_unit_value)]
++
++struct Custom;
++impl Custom {
++    fn trim(self) -> Self {
++        self
++    }
++    fn split_whitespace(self) {}
++}
++
++struct DerefStr(&'static str);
++impl std::ops::Deref for DerefStr {
++    type Target = str;
++    fn deref(&self) -> &Self::Target {
++        self.0
++    }
++}
++
++struct DerefStrAndCustom(&'static str);
++impl std::ops::Deref for DerefStrAndCustom {
++    type Target = str;
++    fn deref(&self) -> &Self::Target {
++        self.0
++    }
++}
++impl DerefStrAndCustom {
++    fn trim(self) -> Self {
++        self
++    }
++    fn split_whitespace(self) {}
++}
++
++struct DerefStrAndCustomSplit(&'static str);
++impl std::ops::Deref for DerefStrAndCustomSplit {
++    type Target = str;
++    fn deref(&self) -> &Self::Target {
++        self.0
++    }
++}
++impl DerefStrAndCustomSplit {
++    #[allow(dead_code)]
++    fn split_whitespace(self) {}
++}
++
++struct DerefStrAndCustomTrim(&'static str);
++impl std::ops::Deref for DerefStrAndCustomTrim {
++    type Target = str;
++    fn deref(&self) -> &Self::Target {
++        self.0
++    }
++}
++impl DerefStrAndCustomTrim {
++    fn trim(self) -> Self {
++        self
++    }
++}
++
++fn main() {
++    // &str
++    let _ = " A B C ".trim().split_whitespace(); // should trigger lint
++    let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint
++    let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint
++
++    // String
++    let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint
++    let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint
++    let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint
++
++    // Custom
++    let _ = Custom.trim().split_whitespace(); // should not trigger lint
++
++    // Deref<Target=str>
++    let s = DerefStr(" A B C ");
++    let _ = s.trim().split_whitespace(); // should trigger lint
++
++    // Deref<Target=str> + custom impl
++    let s = DerefStrAndCustom(" A B C ");
++    let _ = s.trim().split_whitespace(); // should not trigger lint
++
++    // Deref<Target=str> + only custom split_ws() impl
++    let s = DerefStrAndCustomSplit(" A B C ");
++    let _ = s.trim().split_whitespace(); // should trigger lint
++    // Expl: trim() is called on str (deref) and returns &str.
++    //       Thus split_ws() is called on str as well and the custom impl on S is unused
++
++    // Deref<Target=str> + only custom trim() impl
++    let s = DerefStrAndCustomTrim(" A B C ");
++    let _ = s.trim().split_whitespace(); // should not trigger lint
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5ae7849e27d2ba6f6df63978ef93730140ba7f79
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++error: found call to `str::trim` before `str::split_whitespace`
++  --> $DIR/trim_split_whitespace.rs:62:23
++   |
++LL |     let _ = " A B C ".trim().split_whitespace(); // should trigger lint
++   |                       ^^^^^^^ help: remove `trim()`
++   |
++   = note: `-D clippy::trim-split-whitespace` implied by `-D warnings`
++
++error: found call to `str::trim_start` before `str::split_whitespace`
++  --> $DIR/trim_split_whitespace.rs:63:23
++   |
++LL |     let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint
++   |                       ^^^^^^^^^^^^^ help: remove `trim_start()`
++
++error: found call to `str::trim_end` before `str::split_whitespace`
++  --> $DIR/trim_split_whitespace.rs:64:23
++   |
++LL |     let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint
++   |                       ^^^^^^^^^^^ help: remove `trim_end()`
++
++error: found call to `str::trim` before `str::split_whitespace`
++  --> $DIR/trim_split_whitespace.rs:67:37
++   |
++LL |     let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint
++   |                                     ^^^^^^^ help: remove `trim()`
++
++error: found call to `str::trim_start` before `str::split_whitespace`
++  --> $DIR/trim_split_whitespace.rs:68:37
++   |
++LL |     let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint
++   |                                     ^^^^^^^^^^^^^ help: remove `trim_start()`
++
++error: found call to `str::trim_end` before `str::split_whitespace`
++  --> $DIR/trim_split_whitespace.rs:69:37
++   |
++LL |     let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint
++   |                                     ^^^^^^^^^^^ help: remove `trim_end()`
++
++error: found call to `str::trim` before `str::split_whitespace`
++  --> $DIR/trim_split_whitespace.rs:76:15
++   |
++LL |     let _ = s.trim().split_whitespace(); // should trigger lint
++   |               ^^^^^^^ help: remove `trim()`
++
++error: found call to `str::trim` before `str::split_whitespace`
++  --> $DIR/trim_split_whitespace.rs:84:15
++   |
++LL |     let _ = s.trim().split_whitespace(); // should trigger lint
++   |               ^^^^^^^ help: remove `trim()`
++
++error: aborting due to 8 previous errors
++
index fc740ee11d6ab7b815ab274dd283c76b44055da6,0000000000000000000000000000000000000000..d11432f9046111d4ff77f7279250bc7f10083975
mode 100644,000000..100644
--- /dev/null
@@@ -1,82 -1,0 +1,85 @@@
 +#![deny(clippy::type_repetition_in_bounds)]
 +
 +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
 +
 +pub fn foo<T>(_t: T)
 +where
 +    T: Copy,
 +    T: Clone,
 +{
 +    unimplemented!();
 +}
 +
 +pub fn bar<T, U>(_t: T, _u: U)
 +where
 +    T: Copy,
 +    U: Clone,
 +{
 +    unimplemented!();
 +}
 +
 +// Threshold test (see #4380)
 +trait LintBounds
 +where
 +    Self: Clone,
 +    Self: Copy + Default + Ord,
 +    Self: Add<Output = Self> + AddAssign + Sub<Output = Self> + SubAssign,
 +    Self: Mul<Output = Self> + MulAssign + Div<Output = Self> + DivAssign,
 +{
 +}
 +
 +trait LotsOfBounds
 +where
 +    Self: Clone + Copy + Default + Ord,
 +    Self: Add<Output = Self> + AddAssign + Sub<Output = Self> + SubAssign,
 +    Self: Mul<Output = Self> + MulAssign + Div<Output = Self> + DivAssign,
 +{
 +}
 +
 +// Generic distinction (see #4323)
 +mod issue4323 {
 +    pub struct Foo<A>(A);
 +    pub struct Bar<A, B> {
 +        a: Foo<A>,
 +        b: Foo<B>,
 +    }
 +
 +    impl<A, B> Unpin for Bar<A, B>
 +    where
 +        Foo<A>: Unpin,
 +        Foo<B>: Unpin,
 +    {
 +    }
 +}
 +
 +// Extern macros shouldn't lint (see #4326)
 +extern crate serde;
 +mod issue4326 {
 +    use serde::{Deserialize, Serialize};
 +
 +    trait Foo {}
 +    impl Foo for String {}
 +
 +    #[derive(Debug, Serialize, Deserialize)]
 +    struct Bar<S>
 +    where
 +        S: Foo,
 +    {
 +        foo: S,
 +    }
 +}
 +
 +// Issue #7360
 +struct Foo<T, U>
 +where
 +    T: Clone,
 +    U: Clone,
 +{
 +    t: T,
 +    u: U,
 +}
 +
++// This should not lint
++fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
++
 +fn main() {}
index 148c19c7d0701dc2910de718d175a03122d26e03,0000000000000000000000000000000000000000..abc25e59496bfa13262c3a5b938ce282a7b25edb
mode 100644,000000..100644
--- /dev/null
@@@ -1,23 -1,0 +1,31 @@@
- error: aborting due to 2 previous errors
 +error: this type has already been used as a bound predicate
 +  --> $DIR/type_repetition_in_bounds.rs:8:5
 +   |
 +LL |     T: Clone,
 +   |     ^^^^^^^^
 +   |
 +note: the lint level is defined here
 +  --> $DIR/type_repetition_in_bounds.rs:1:9
 +   |
 +LL | #![deny(clippy::type_repetition_in_bounds)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   = help: consider combining the bounds: `T: Copy + Clone`
 +
 +error: this type has already been used as a bound predicate
 +  --> $DIR/type_repetition_in_bounds.rs:25:5
 +   |
 +LL |     Self: Copy + Default + Ord,
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord`
 +
++error: this type has already been used as a bound predicate
++  --> $DIR/type_repetition_in_bounds.rs:83:43
++   |
++LL | fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
++   |                                           ^^^^^^^^^^
++   |
++   = help: consider combining the bounds: `impl AsRef<str>: AsRef<str> + AsRef<str>`
++
++error: aborting due to 3 previous errors
 +
index afa337c45f41766033abe0d1d85b2694f7fa11f6,0000000000000000000000000000000000000000..7be15b0b2dd3d4669f7202a15d18bd1b9a2d7c20
mode 100644,000000..100644
--- /dev/null
@@@ -1,336 -1,0 +1,337 @@@
 +// aux-build:proc_macro_unsafe.rs
 +
 +#![warn(clippy::undocumented_unsafe_blocks)]
++#![allow(clippy::let_unit_value)]
 +
 +extern crate proc_macro_unsafe;
 +
 +// Valid comments
 +
 +fn nested_local() {
 +    let _ = {
 +        let _ = {
 +            // SAFETY:
 +            let _ = unsafe {};
 +        };
 +    };
 +}
 +
 +fn deep_nest() {
 +    let _ = {
 +        let _ = {
 +            // SAFETY:
 +            let _ = unsafe {};
 +
 +            // Safety:
 +            unsafe {};
 +
 +            let _ = {
 +                let _ = {
 +                    let _ = {
 +                        let _ = {
 +                            let _ = {
 +                                // Safety:
 +                                let _ = unsafe {};
 +
 +                                // SAFETY:
 +                                unsafe {};
 +                            };
 +                        };
 +                    };
 +
 +                    // Safety:
 +                    unsafe {};
 +                };
 +            };
 +        };
 +
 +        // Safety:
 +        unsafe {};
 +    };
 +
 +    // SAFETY:
 +    unsafe {};
 +}
 +
 +fn local_tuple_expression() {
 +    // Safety:
 +    let _ = (42, unsafe {});
 +}
 +
 +fn line_comment() {
 +    // Safety:
 +    unsafe {}
 +}
 +
 +fn line_comment_newlines() {
 +    // SAFETY:
 +
 +    unsafe {}
 +}
 +
 +fn line_comment_empty() {
 +    // Safety:
 +    //
 +    //
 +    //
 +    unsafe {}
 +}
 +
 +fn line_comment_with_extras() {
 +    // This is a description
 +    // Safety:
 +    unsafe {}
 +}
 +
 +fn block_comment() {
 +    /* Safety: */
 +    unsafe {}
 +}
 +
 +fn block_comment_newlines() {
 +    /* SAFETY: */
 +
 +    unsafe {}
 +}
 +
 +fn block_comment_with_extras() {
 +    /* This is a description
 +     * SAFETY:
 +     */
 +    unsafe {}
 +}
 +
 +fn block_comment_terminator_same_line() {
 +    /* This is a description
 +     * Safety: */
 +    unsafe {}
 +}
 +
 +fn buried_safety() {
 +    // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
 +    // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
 +    // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
 +    // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
 +    // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
 +    // laborum. Safety:
 +    // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi
 +    // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio
 +    // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl
 +    // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus.
 +    unsafe {}
 +}
 +
 +fn safety_with_prepended_text() {
 +    // This is a test. safety:
 +    unsafe {}
 +}
 +
 +fn local_line_comment() {
 +    // Safety:
 +    let _ = unsafe {};
 +}
 +
 +fn local_block_comment() {
 +    /* SAFETY: */
 +    let _ = unsafe {};
 +}
 +
 +fn comment_array() {
 +    // Safety:
 +    let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
 +}
 +
 +fn comment_tuple() {
 +    // sAFETY:
 +    let _ = (42, unsafe {}, "test", unsafe {});
 +}
 +
 +fn comment_unary() {
 +    // SAFETY:
 +    let _ = *unsafe { &42 };
 +}
 +
 +#[allow(clippy::match_single_binding)]
 +fn comment_match() {
 +    // SAFETY:
 +    let _ = match unsafe {} {
 +        _ => {},
 +    };
 +}
 +
 +fn comment_addr_of() {
 +    // Safety:
 +    let _ = &unsafe {};
 +}
 +
 +fn comment_repeat() {
 +    // Safety:
 +    let _ = [unsafe {}; 5];
 +}
 +
 +fn comment_macro_call() {
 +    macro_rules! t {
 +        ($b:expr) => {
 +            $b
 +        };
 +    }
 +
 +    t!(
 +        // SAFETY:
 +        unsafe {}
 +    );
 +}
 +
 +fn comment_macro_def() {
 +    macro_rules! t {
 +        () => {
 +            // Safety:
 +            unsafe {}
 +        };
 +    }
 +
 +    t!();
 +}
 +
 +fn non_ascii_comment() {
 +    // ॐ᧻໒ SaFeTy: ௵∰
 +    unsafe {};
 +}
 +
 +fn local_commented_block() {
 +    let _ =
 +        // safety:
 +        unsafe {};
 +}
 +
 +fn local_nest() {
 +    // safety:
 +    let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})];
 +}
 +
 +fn in_fn_call(x: *const u32) {
 +    fn f(x: u32) {}
 +
 +    // Safety: reason
 +    f(unsafe { *x });
 +}
 +
 +fn multi_in_fn_call(x: *const u32) {
 +    fn f(x: u32, y: u32) {}
 +
 +    // Safety: reason
 +    f(unsafe { *x }, unsafe { *x });
 +}
 +
 +fn in_multiline_fn_call(x: *const u32) {
 +    fn f(x: u32, y: u32) {}
 +
 +    f(
 +        // Safety: reason
 +        unsafe { *x },
 +        0,
 +    );
 +}
 +
 +fn in_macro_call(x: *const u32) {
 +    // Safety: reason
 +    println!("{}", unsafe { *x });
 +}
 +
 +fn in_multiline_macro_call(x: *const u32) {
 +    println!(
 +        "{}",
 +        // Safety: reason
 +        unsafe { *x },
 +    );
 +}
 +
 +fn from_proc_macro() {
 +    proc_macro_unsafe::unsafe_block!(token);
 +}
 +
 +// Invalid comments
 +
 +#[rustfmt::skip]
 +fn inline_block_comment() {
 +    /* Safety: */ unsafe {}
 +}
 +
 +fn no_comment() {
 +    unsafe {}
 +}
 +
 +fn no_comment_array() {
 +    let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
 +}
 +
 +fn no_comment_tuple() {
 +    let _ = (42, unsafe {}, "test", unsafe {});
 +}
 +
 +fn no_comment_unary() {
 +    let _ = *unsafe { &42 };
 +}
 +
 +#[allow(clippy::match_single_binding)]
 +fn no_comment_match() {
 +    let _ = match unsafe {} {
 +        _ => {},
 +    };
 +}
 +
 +fn no_comment_addr_of() {
 +    let _ = &unsafe {};
 +}
 +
 +fn no_comment_repeat() {
 +    let _ = [unsafe {}; 5];
 +}
 +
 +fn local_no_comment() {
 +    let _ = unsafe {};
 +}
 +
 +fn no_comment_macro_call() {
 +    macro_rules! t {
 +        ($b:expr) => {
 +            $b
 +        };
 +    }
 +
 +    t!(unsafe {});
 +}
 +
 +fn no_comment_macro_def() {
 +    macro_rules! t {
 +        () => {
 +            unsafe {}
 +        };
 +    }
 +
 +    t!();
 +}
 +
 +fn trailing_comment() {
 +    unsafe {} // SAFETY:
 +}
 +
 +fn internal_comment() {
 +    unsafe {
 +        // SAFETY:
 +    }
 +}
 +
 +fn interference() {
 +    // SAFETY
 +
 +    let _ = 42;
 +
 +    unsafe {};
 +}
 +
 +pub fn print_binary_tree() {
 +    println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
 +}
 +
 +fn main() {}
index 856a07fd31685e3170eba2ff3baf47221494cf01,0000000000000000000000000000000000000000..87d445bd7b86b12e39844c8718076717ff517aaf
mode 100644,000000..100644
--- /dev/null
@@@ -1,151 -1,0 +1,151 @@@
-   --> $DIR/undocumented_unsafe_blocks.rs:256:19
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:260:5
++  --> $DIR/undocumented_unsafe_blocks.rs:257:19
 +   |
 +LL |     /* Safety: */ unsafe {}
 +   |                   ^^^^^^^^^
 +   |
 +   = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:264:14
++  --> $DIR/undocumented_unsafe_blocks.rs:261:5
 +   |
 +LL |     unsafe {}
 +   |     ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:264:29
++  --> $DIR/undocumented_unsafe_blocks.rs:265:14
 +   |
 +LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
 +   |              ^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:264:48
++  --> $DIR/undocumented_unsafe_blocks.rs:265:29
 +   |
 +LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
 +   |                             ^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:268:18
++  --> $DIR/undocumented_unsafe_blocks.rs:265:48
 +   |
 +LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
 +   |                                                ^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:268:37
++  --> $DIR/undocumented_unsafe_blocks.rs:269:18
 +   |
 +LL |     let _ = (42, unsafe {}, "test", unsafe {});
 +   |                  ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:272:14
++  --> $DIR/undocumented_unsafe_blocks.rs:269:37
 +   |
 +LL |     let _ = (42, unsafe {}, "test", unsafe {});
 +   |                                     ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:277:19
++  --> $DIR/undocumented_unsafe_blocks.rs:273:14
 +   |
 +LL |     let _ = *unsafe { &42 };
 +   |              ^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:283:14
++  --> $DIR/undocumented_unsafe_blocks.rs:278:19
 +   |
 +LL |     let _ = match unsafe {} {
 +   |                   ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:287:14
++  --> $DIR/undocumented_unsafe_blocks.rs:284:14
 +   |
 +LL |     let _ = &unsafe {};
 +   |              ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:291:13
++  --> $DIR/undocumented_unsafe_blocks.rs:288:14
 +   |
 +LL |     let _ = [unsafe {}; 5];
 +   |              ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:301:8
++  --> $DIR/undocumented_unsafe_blocks.rs:292:13
 +   |
 +LL |     let _ = unsafe {};
 +   |             ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:307:13
++  --> $DIR/undocumented_unsafe_blocks.rs:302:8
 +   |
 +LL |     t!(unsafe {});
 +   |        ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:315:5
++  --> $DIR/undocumented_unsafe_blocks.rs:308:13
 +   |
 +LL |             unsafe {}
 +   |             ^^^^^^^^^
 +...
 +LL |     t!();
 +   |     ---- in this macro invocation
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +   = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:319:5
++  --> $DIR/undocumented_unsafe_blocks.rs:316:5
 +   |
 +LL |     unsafe {} // SAFETY:
 +   |     ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:329:5
++  --> $DIR/undocumented_unsafe_blocks.rs:320:5
 +   |
 +LL |     unsafe {
 +   |     ^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:333:20
++  --> $DIR/undocumented_unsafe_blocks.rs:330:5
 +   |
 +LL |     unsafe {};
 +   |     ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
++  --> $DIR/undocumented_unsafe_blocks.rs:334:20
 +   |
 +LL |     println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
 +   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: aborting due to 18 previous errors
 +
index 1ed3883c1f06059285c858c40323eec9b069de77,0000000000000000000000000000000000000000..dac5ce272c02665133022468802bbfe679768d0d
mode 100644,000000..100644
--- /dev/null
@@@ -1,25 -1,0 +1,26 @@@
 +#![feature(stmt_expr_attributes)]
++#![allow(clippy::let_unit_value)]
 +
 +use std::mem::{self, MaybeUninit};
 +
 +fn main() {
 +    let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
 +
 +    // edge case: For now we lint on empty arrays
 +    let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() };
 +
 +    // edge case: For now we accept unit tuples
 +    let _: () = unsafe { MaybeUninit::uninit().assume_init() };
 +
 +    // This is OK, because `MaybeUninit` allows uninitialized data.
 +    let _: MaybeUninit<usize> = unsafe { MaybeUninit::uninit().assume_init() };
 +
 +    // This is OK, because all constitutent types are uninit-compatible.
 +    let _: (MaybeUninit<usize>, MaybeUninit<bool>) = unsafe { MaybeUninit::uninit().assume_init() };
 +
 +    // This is OK, because all constitutent types are uninit-compatible.
 +    let _: (MaybeUninit<usize>, [MaybeUninit<bool>; 2]) = unsafe { MaybeUninit::uninit().assume_init() };
 +
 +    // Was a false negative.
 +    let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() };
 +}
index 85b64a8419ab021bd36585ebde6e9431e84301e6,0000000000000000000000000000000000000000..15ef2349489fa711a5ed4a68557a2a8d3b8b3dfd
mode 100644,000000..100644
--- /dev/null
@@@ -1,22 -1,0 +1,22 @@@
-   --> $DIR/uninit.rs:6:29
 +error: this call for this type may be undefined behavior
-   --> $DIR/uninit.rs:9:31
++  --> $DIR/uninit.rs:7:29
 +   |
 +LL |     let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
 +   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `#[deny(clippy::uninit_assumed_init)]` on by default
 +
 +error: this call for this type may be undefined behavior
-   --> $DIR/uninit.rs:24:29
++  --> $DIR/uninit.rs:10:31
 +   |
 +LL |     let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() };
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: this call for this type may be undefined behavior
++  --> $DIR/uninit.rs:25:29
 +   |
 +LL |     let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() };
 +   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: aborting due to 3 previous errors
 +
index 535683729f686a965f7aab4618adae726a9c6aee,0000000000000000000000000000000000000000..38be87bddf19845de6797e70384317bdbfb91162
mode 100644,000000..100644
--- /dev/null
@@@ -1,132 -1,0 +1,133 @@@
-     clippy::self_named_constructors
 +#![warn(clippy::unit_arg)]
 +#![allow(
 +    clippy::no_effect,
 +    unused_must_use,
 +    unused_variables,
 +    clippy::unused_unit,
 +    clippy::unnecessary_wraps,
 +    clippy::or_fun_call,
 +    clippy::needless_question_mark,
++    clippy::self_named_constructors,
++    clippy::let_unit_value
 +)]
 +
 +use std::fmt::Debug;
 +
 +fn foo<T: Debug>(t: T) {
 +    println!("{:?}", t);
 +}
 +
 +fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
 +    println!("{:?}, {:?}, {:?}", t1, t2, t3);
 +}
 +
 +struct Bar;
 +
 +impl Bar {
 +    fn bar<T: Debug>(&self, t: T) {
 +        println!("{:?}", t);
 +    }
 +}
 +
 +fn baz<T: Debug>(t: T) {
 +    foo(t);
 +}
 +
 +trait Tr {
 +    type Args;
 +    fn do_it(args: Self::Args);
 +}
 +
 +struct A;
 +impl Tr for A {
 +    type Args = ();
 +    fn do_it(_: Self::Args) {}
 +}
 +
 +struct B;
 +impl Tr for B {
 +    type Args = <A as Tr>::Args;
 +
 +    fn do_it(args: Self::Args) {
 +        A::do_it(args)
 +    }
 +}
 +
 +fn bad() {
 +    foo({
 +        1;
 +    });
 +    foo(foo(1));
 +    foo({
 +        foo(1);
 +        foo(2);
 +    });
 +    let b = Bar;
 +    b.bar({
 +        1;
 +    });
 +    taking_multiple_units(foo(0), foo(1));
 +    taking_multiple_units(foo(0), {
 +        foo(1);
 +        foo(2);
 +    });
 +    taking_multiple_units(
 +        {
 +            foo(0);
 +            foo(1);
 +        },
 +        {
 +            foo(2);
 +            foo(3);
 +        },
 +    );
 +    // here Some(foo(2)) isn't the top level statement expression, wrap the suggestion in a block
 +    None.or(Some(foo(2)));
 +    // in this case, the suggestion can be inlined, no need for a surrounding block
 +    // foo(()); foo(()) instead of { foo(()); foo(()) }
 +    foo(foo(()));
 +}
 +
 +fn ok() {
 +    foo(());
 +    foo(1);
 +    foo({ 1 });
 +    foo3("a", 3, vec![3]);
 +    let b = Bar;
 +    b.bar({ 1 });
 +    b.bar(());
 +    question_mark();
 +    let named_unit_arg = ();
 +    foo(named_unit_arg);
 +    baz(());
 +    B::do_it(());
 +}
 +
 +fn question_mark() -> Result<(), ()> {
 +    Ok(Ok(())?)?;
 +    Ok(Ok(()))??;
 +    Ok(())
 +}
 +
 +#[allow(dead_code)]
 +mod issue_2945 {
 +    fn unit_fn() -> Result<(), i32> {
 +        Ok(())
 +    }
 +
 +    fn fallible() -> Result<(), i32> {
 +        Ok(unit_fn()?)
 +    }
 +}
 +
 +#[allow(dead_code)]
 +fn returning_expr() -> Option<()> {
 +    Some(foo(1))
 +}
 +
 +fn taking_multiple_units(a: (), b: ()) {}
 +
 +fn main() {
 +    bad();
 +    ok();
 +}
index 5cfb367a7be80795eb5da98fd6b2b3ea4b4614fa,0000000000000000000000000000000000000000..394dee29dc96e5c802826bb52830063e25024ae6
mode 100644,000000..100644
--- /dev/null
@@@ -1,181 -1,0 +1,181 @@@
-   --> $DIR/unit_arg.rs:56:5
 +error: passing a unit value to a function
-   --> $DIR/unit_arg.rs:59:5
++  --> $DIR/unit_arg.rs:57:5
 +   |
 +LL | /     foo({
 +LL | |         1;
 +LL | |     });
 +   | |______^
 +   |
 +   = note: `-D clippy::unit-arg` implied by `-D warnings`
 +help: remove the semicolon from the last statement in the block
 +   |
 +LL |         1
 +   |
 +help: or move the expression in front of the call and replace it with the unit literal `()`
 +   |
 +LL ~     {
 +LL +         1;
 +LL +     };
 +LL ~     foo(());
 +   |
 +
 +error: passing a unit value to a function
-   --> $DIR/unit_arg.rs:60:5
++  --> $DIR/unit_arg.rs:60:5
 +   |
 +LL |     foo(foo(1));
 +   |     ^^^^^^^^^^^
 +   |
 +help: move the expression in front of the call and replace it with the unit literal `()`
 +   |
 +LL ~     foo(1);
 +LL ~     foo(());
 +   |
 +
 +error: passing a unit value to a function
-   --> $DIR/unit_arg.rs:65:5
++  --> $DIR/unit_arg.rs:61:5
 +   |
 +LL | /     foo({
 +LL | |         foo(1);
 +LL | |         foo(2);
 +LL | |     });
 +   | |______^
 +   |
 +help: remove the semicolon from the last statement in the block
 +   |
 +LL |         foo(2)
 +   |
 +help: or move the expression in front of the call and replace it with the unit literal `()`
 +   |
 +LL ~     {
 +LL +         foo(1);
 +LL +         foo(2);
 +LL +     };
 +LL ~     foo(());
 +   |
 +
 +error: passing a unit value to a function
-   --> $DIR/unit_arg.rs:68:5
++  --> $DIR/unit_arg.rs:66:5
 +   |
 +LL | /     b.bar({
 +LL | |         1;
 +LL | |     });
 +   | |______^
 +   |
 +help: remove the semicolon from the last statement in the block
 +   |
 +LL |         1
 +   |
 +help: or move the expression in front of the call and replace it with the unit literal `()`
 +   |
 +LL ~     {
 +LL +         1;
 +LL +     };
 +LL ~     b.bar(());
 +   |
 +
 +error: passing unit values to a function
-   --> $DIR/unit_arg.rs:69:5
++  --> $DIR/unit_arg.rs:69:5
 +   |
 +LL |     taking_multiple_units(foo(0), foo(1));
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: move the expressions in front of the call and replace them with the unit literal `()`
 +   |
 +LL ~     foo(0);
 +LL +     foo(1);
 +LL ~     taking_multiple_units((), ());
 +   |
 +
 +error: passing unit values to a function
-   --> $DIR/unit_arg.rs:73:5
++  --> $DIR/unit_arg.rs:70:5
 +   |
 +LL | /     taking_multiple_units(foo(0), {
 +LL | |         foo(1);
 +LL | |         foo(2);
 +LL | |     });
 +   | |______^
 +   |
 +help: remove the semicolon from the last statement in the block
 +   |
 +LL |         foo(2)
 +   |
 +help: or move the expressions in front of the call and replace them with the unit literal `()`
 +   |
 +LL ~     foo(0);
 +LL +     {
 +LL +         foo(1);
 +LL +         foo(2);
 +LL +     };
 +LL ~     taking_multiple_units((), ());
 +   |
 +
 +error: passing unit values to a function
-   --> $DIR/unit_arg.rs:84:13
++  --> $DIR/unit_arg.rs:74:5
 +   |
 +LL | /     taking_multiple_units(
 +LL | |         {
 +LL | |             foo(0);
 +LL | |             foo(1);
 +...  |
 +LL | |         },
 +LL | |     );
 +   | |_____^
 +   |
 +help: remove the semicolon from the last statement in the block
 +   |
 +LL |             foo(1)
 +   |
 +help: remove the semicolon from the last statement in the block
 +   |
 +LL |             foo(3)
 +   |
 +help: or move the expressions in front of the call and replace them with the unit literal `()`
 +   |
 +LL ~     {
 +LL +         foo(0);
 +LL +         foo(1);
 +LL +     };
 +LL +     {
 +LL +         foo(2);
 + ...
 +
 +error: passing a unit value to a function
-   --> $DIR/unit_arg.rs:87:5
++  --> $DIR/unit_arg.rs:85:13
 +   |
 +LL |     None.or(Some(foo(2)));
 +   |             ^^^^^^^^^^^^
 +   |
 +help: move the expression in front of the call and replace it with the unit literal `()`
 +   |
 +LL ~     None.or({
 +LL +         foo(2);
 +LL +         Some(())
 +LL ~     });
 +   |
 +
 +error: passing a unit value to a function
-   --> $DIR/unit_arg.rs:124:5
++  --> $DIR/unit_arg.rs:88:5
 +   |
 +LL |     foo(foo(()));
 +   |     ^^^^^^^^^^^^
 +   |
 +help: move the expression in front of the call and replace it with the unit literal `()`
 +   |
 +LL ~     foo(());
 +LL ~     foo(());
 +   |
 +
 +error: passing a unit value to a function
++  --> $DIR/unit_arg.rs:125:5
 +   |
 +LL |     Some(foo(1))
 +   |     ^^^^^^^^^^^^
 +   |
 +help: move the expression in front of the call and replace it with the unit literal `()`
 +   |
 +LL ~     foo(1);
 +LL +     Some(())
 +   |
 +
 +error: aborting due to 10 previous errors
 +
index 989916c239bad090ad71a54dcb6109220da07b3f,0000000000000000000000000000000000000000..43eb54eff477b72518c92b93e5b9448830aea6f0
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,28 @@@
 +#![warn(clippy::unit_hash)]
++#![allow(clippy::let_unit_value)]
 +
 +use std::collections::hash_map::DefaultHasher;
 +use std::hash::Hash;
 +
 +enum Foo {
 +    Empty,
 +    WithValue(u8),
 +}
 +
 +fn do_nothing() {}
 +
 +fn main() {
 +    let mut state = DefaultHasher::new();
 +    let my_enum = Foo::Empty;
 +
 +    match my_enum {
 +        Foo::Empty => ().hash(&mut state),
 +        Foo::WithValue(x) => x.hash(&mut state),
 +    }
 +
 +    let res = ();
 +    res.hash(&mut state);
 +
 +    #[allow(clippy::unit_arg)]
 +    do_nothing().hash(&mut state);
 +}
index da276296e0282507b446f629bb5ff051c2f2086f,0000000000000000000000000000000000000000..050fa55a12bea71bab7f4c44f11b81724d1400cf
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,27 @@@
-   --> $DIR/unit_hash.rs:18:23
 +error: this call to `hash` on the unit type will do nothing
-   --> $DIR/unit_hash.rs:23:5
++  --> $DIR/unit_hash.rs:19:23
 +   |
 +LL |         Foo::Empty => ().hash(&mut state),
 +   |                       ^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
 +   |
 +   = note: `-D clippy::unit-hash` implied by `-D warnings`
 +   = note: the implementation of `Hash` for `()` is a no-op
 +
 +error: this call to `hash` on the unit type will do nothing
-   --> $DIR/unit_hash.rs:26:5
++  --> $DIR/unit_hash.rs:24:5
 +   |
 +LL |     res.hash(&mut state);
 +   |     ^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
 +   |
 +   = note: the implementation of `Hash` for `()` is a no-op
 +
 +error: this call to `hash` on the unit type will do nothing
++  --> $DIR/unit_hash.rs:27:5
 +   |
 +LL |     do_nothing().hash(&mut state);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
 +   |
 +   = note: the implementation of `Hash` for `()` is a no-op
 +
 +error: aborting due to 3 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f95f91329a2faeeae22da74496d42568e89512c8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++// 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
++    ref_str_argument("");
++
++    // should not be linted
++    ref_str_argument("");
++
++    // should not be linted
++    ref_string_argument(&String::new());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0cbdc151ed9b1b0b3a2056ed6e4e78665f175ee0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++// 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
++    ref_str_argument(&String::from(""));
++
++    // should not be linted
++    ref_str_argument("");
++
++    // should not be linted
++    ref_string_argument(&String::new());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..46bc4597b335f0bfb7be4ce8053f46b3735e95b4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++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:15:22
++   |
++LL |     ref_str_argument(&String::from(""));
++   |                      ^^^^^^^^^^^^^^^^^ help: try: `""`
++
++error: aborting due to 2 previous errors
++
index 38ba41ac54ecb2ca7814f135f1525b44d66a825a,0000000000000000000000000000000000000000..7455e22d49b21f8df93bea25f1095f5beac55983
mode 100644,000000..100644
--- /dev/null
@@@ -1,262 -1,0 +1,274 @@@
 +// 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);
 +
 +    require_x(&Cow::<X>::Owned(x.clone()));
 +    require_x(x_ref);
 +
 +    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"));
 +}
 +
 +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))
 +    }
 +}
index 15fb7ee83e3d100a50cfa13dfd54cf12afcd7cd0,0000000000000000000000000000000000000000..bbcd00ad2204a9a789cee2e457b6d89357aaa223
mode 100644,000000..100644
--- /dev/null
@@@ -1,262 -1,0 +1,274 @@@
 +// 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());
 +
 +    require_x(&Cow::<X>::Owned(x.clone()).into_owned());
 +    require_x(&x_ref.to_owned());
 +
 +    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());
 +}
 +
 +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()))
 +    }
 +}
index c53ce32be775706f91d4ce47b2e4b5bd4220abd9,0000000000000000000000000000000000000000..f9713559e4f7fce4ac6b3d6096ae1e25ee83ee52
mode 100644,000000..100644
--- /dev/null
@@@ -1,501 -1,0 +1,513 @@@
-   --> $DIR/unnecessary_to_owned.rs:150:64
 +error: redundant clone
-   --> $DIR/unnecessary_to_owned.rs:150:20
++  --> $DIR/unnecessary_to_owned.rs:151:64
 +   |
 +LL |     require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
 +   |                                                                ^^^^^^^^^^^ help: remove this
 +   |
 +   = note: `-D clippy::redundant-clone` implied by `-D warnings`
 +note: this value is dropped without further use
-   --> $DIR/unnecessary_to_owned.rs:151:40
++  --> $DIR/unnecessary_to_owned.rs:151:20
 +   |
 +LL |     require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
 +   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: redundant clone
-   --> $DIR/unnecessary_to_owned.rs:151:21
++  --> $DIR/unnecessary_to_owned.rs:152:40
 +   |
 +LL |     require_os_str(&OsString::from("x").to_os_string());
 +   |                                        ^^^^^^^^^^^^^^^ help: remove this
 +   |
 +note: this value is dropped without further use
-   --> $DIR/unnecessary_to_owned.rs:152:48
++  --> $DIR/unnecessary_to_owned.rs:152:21
 +   |
 +LL |     require_os_str(&OsString::from("x").to_os_string());
 +   |                     ^^^^^^^^^^^^^^^^^^^
 +
 +error: redundant clone
-   --> $DIR/unnecessary_to_owned.rs:152:19
++  --> $DIR/unnecessary_to_owned.rs:153:48
 +   |
 +LL |     require_path(&std::path::PathBuf::from("x").to_path_buf());
 +   |                                                ^^^^^^^^^^^^^^ help: remove this
 +   |
 +note: this value is dropped without further use
-   --> $DIR/unnecessary_to_owned.rs:153:35
++  --> $DIR/unnecessary_to_owned.rs:153:19
 +   |
 +LL |     require_path(&std::path::PathBuf::from("x").to_path_buf());
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: redundant clone
-   --> $DIR/unnecessary_to_owned.rs:153:18
++  --> $DIR/unnecessary_to_owned.rs:154:35
 +   |
 +LL |     require_str(&String::from("x").to_string());
 +   |                                   ^^^^^^^^^^^^ help: remove this
 +   |
 +note: this value is dropped without further use
-   --> $DIR/unnecessary_to_owned.rs:59:36
++  --> $DIR/unnecessary_to_owned.rs:154:18
 +   |
 +LL |     require_str(&String::from("x").to_string());
 +   |                  ^^^^^^^^^^^^^^^^^
 +
 +error: unnecessary use of `into_owned`
-   --> $DIR/unnecessary_to_owned.rs:60:19
++  --> $DIR/unnecessary_to_owned.rs:60:36
 +   |
 +LL |     require_c_str(&Cow::from(c_str).into_owned());
 +   |                                    ^^^^^^^^^^^^^ help: remove this
 +   |
 +   = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:62:20
++  --> $DIR/unnecessary_to_owned.rs:61:19
 +   |
 +LL |     require_c_str(&c_str.to_owned());
 +   |                   ^^^^^^^^^^^^^^^^^ help: use: `c_str`
 +
 +error: unnecessary use of `to_os_string`
-   --> $DIR/unnecessary_to_owned.rs:63:38
++  --> $DIR/unnecessary_to_owned.rs:63:20
 +   |
 +LL |     require_os_str(&os_str.to_os_string());
 +   |                    ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str`
 +
 +error: unnecessary use of `into_owned`
-   --> $DIR/unnecessary_to_owned.rs:64:20
++  --> $DIR/unnecessary_to_owned.rs:64:38
 +   |
 +LL |     require_os_str(&Cow::from(os_str).into_owned());
 +   |                                      ^^^^^^^^^^^^^ help: remove this
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:66:18
++  --> $DIR/unnecessary_to_owned.rs:65:20
 +   |
 +LL |     require_os_str(&os_str.to_owned());
 +   |                    ^^^^^^^^^^^^^^^^^^ help: use: `os_str`
 +
 +error: unnecessary use of `to_path_buf`
-   --> $DIR/unnecessary_to_owned.rs:67:34
++  --> $DIR/unnecessary_to_owned.rs:67:18
 +   |
 +LL |     require_path(&path.to_path_buf());
 +   |                  ^^^^^^^^^^^^^^^^^^^ help: use: `path`
 +
 +error: unnecessary use of `into_owned`
-   --> $DIR/unnecessary_to_owned.rs:68:18
++  --> $DIR/unnecessary_to_owned.rs:68:34
 +   |
 +LL |     require_path(&Cow::from(path).into_owned());
 +   |                                  ^^^^^^^^^^^^^ help: remove this
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:70:17
++  --> $DIR/unnecessary_to_owned.rs:69:18
 +   |
 +LL |     require_path(&path.to_owned());
 +   |                  ^^^^^^^^^^^^^^^^ help: use: `path`
 +
 +error: unnecessary use of `to_string`
-   --> $DIR/unnecessary_to_owned.rs:71:30
++  --> $DIR/unnecessary_to_owned.rs:71:17
 +   |
 +LL |     require_str(&s.to_string());
 +   |                 ^^^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `into_owned`
-   --> $DIR/unnecessary_to_owned.rs:72:17
++  --> $DIR/unnecessary_to_owned.rs:72:30
 +   |
 +LL |     require_str(&Cow::from(s).into_owned());
 +   |                              ^^^^^^^^^^^^^ help: remove this
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:73:17
++  --> $DIR/unnecessary_to_owned.rs:73:17
 +   |
 +LL |     require_str(&s.to_owned());
 +   |                 ^^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_string`
-   --> $DIR/unnecessary_to_owned.rs:75:19
++  --> $DIR/unnecessary_to_owned.rs:74:17
 +   |
 +LL |     require_str(&x_ref.to_string());
 +   |                 ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()`
 +
 +error: unnecessary use of `to_vec`
-   --> $DIR/unnecessary_to_owned.rs:76:36
++  --> $DIR/unnecessary_to_owned.rs:76:19
 +   |
 +LL |     require_slice(&slice.to_vec());
 +   |                   ^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `into_owned`
-   --> $DIR/unnecessary_to_owned.rs:77:19
++  --> $DIR/unnecessary_to_owned.rs:77:36
 +   |
 +LL |     require_slice(&Cow::from(slice).into_owned());
 +   |                                    ^^^^^^^^^^^^^ help: remove this
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:78:19
++  --> $DIR/unnecessary_to_owned.rs:78:19
 +   |
 +LL |     require_slice(&array.to_owned());
 +   |                   ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:79:19
++  --> $DIR/unnecessary_to_owned.rs:79:19
 +   |
 +LL |     require_slice(&array_ref.to_owned());
 +   |                   ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:80:19
++  --> $DIR/unnecessary_to_owned.rs:80:19
 +   |
 +LL |     require_slice(&slice.to_owned());
 +   |                   ^^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:82:42
++  --> $DIR/unnecessary_to_owned.rs:81:19
 +   |
 +LL |     require_slice(&x_ref.to_owned());
 +   |                   ^^^^^^^^^^^^^^^^^ help: use: `x_ref`
 +
 +error: unnecessary use of `into_owned`
-   --> $DIR/unnecessary_to_owned.rs:83:15
++  --> $DIR/unnecessary_to_owned.rs:83:42
 +   |
 +LL |     require_x(&Cow::<X>::Owned(x.clone()).into_owned());
 +   |                                          ^^^^^^^^^^^^^ help: remove this
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:85:25
++  --> $DIR/unnecessary_to_owned.rs:84:15
 +   |
 +LL |     require_x(&x_ref.to_owned());
 +   |               ^^^^^^^^^^^^^^^^^ help: use: `x_ref`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:86:26
++  --> $DIR/unnecessary_to_owned.rs:86:25
 +   |
 +LL |     require_deref_c_str(c_str.to_owned());
 +   |                         ^^^^^^^^^^^^^^^^ help: use: `c_str`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:87:24
++  --> $DIR/unnecessary_to_owned.rs:87:26
 +   |
 +LL |     require_deref_os_str(os_str.to_owned());
 +   |                          ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:88:23
++  --> $DIR/unnecessary_to_owned.rs:88:24
 +   |
 +LL |     require_deref_path(path.to_owned());
 +   |                        ^^^^^^^^^^^^^^^ help: use: `path`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:89:25
++  --> $DIR/unnecessary_to_owned.rs:89:23
 +   |
 +LL |     require_deref_str(s.to_owned());
 +   |                       ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:91:30
++  --> $DIR/unnecessary_to_owned.rs:90:25
 +   |
 +LL |     require_deref_slice(slice.to_owned());
 +   |                         ^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:92:31
++  --> $DIR/unnecessary_to_owned.rs:92:30
 +   |
 +LL |     require_impl_deref_c_str(c_str.to_owned());
 +   |                              ^^^^^^^^^^^^^^^^ help: use: `c_str`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:93:29
++  --> $DIR/unnecessary_to_owned.rs:93:31
 +   |
 +LL |     require_impl_deref_os_str(os_str.to_owned());
 +   |                               ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:94:28
++  --> $DIR/unnecessary_to_owned.rs:94:29
 +   |
 +LL |     require_impl_deref_path(path.to_owned());
 +   |                             ^^^^^^^^^^^^^^^ help: use: `path`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:95:30
++  --> $DIR/unnecessary_to_owned.rs:95:28
 +   |
 +LL |     require_impl_deref_str(s.to_owned());
 +   |                            ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:97:29
++  --> $DIR/unnecessary_to_owned.rs:96:30
 +   |
 +LL |     require_impl_deref_slice(slice.to_owned());
 +   |                              ^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:97:43
++  --> $DIR/unnecessary_to_owned.rs:98:29
 +   |
 +LL |     require_deref_str_slice(s.to_owned(), slice.to_owned());
 +   |                             ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:98:29
++  --> $DIR/unnecessary_to_owned.rs:98:43
 +   |
 +LL |     require_deref_str_slice(s.to_owned(), slice.to_owned());
 +   |                                           ^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:98:47
++  --> $DIR/unnecessary_to_owned.rs:99:29
 +   |
 +LL |     require_deref_slice_str(slice.to_owned(), s.to_owned());
 +   |                             ^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:100:26
++  --> $DIR/unnecessary_to_owned.rs:99:47
 +   |
 +LL |     require_deref_slice_str(slice.to_owned(), s.to_owned());
 +   |                                               ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:101:27
++  --> $DIR/unnecessary_to_owned.rs:101:26
 +   |
 +LL |     require_as_ref_c_str(c_str.to_owned());
 +   |                          ^^^^^^^^^^^^^^^^ help: use: `c_str`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:102:25
++  --> $DIR/unnecessary_to_owned.rs:102:27
 +   |
 +LL |     require_as_ref_os_str(os_str.to_owned());
 +   |                           ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:103:24
++  --> $DIR/unnecessary_to_owned.rs:103:25
 +   |
 +LL |     require_as_ref_path(path.to_owned());
 +   |                         ^^^^^^^^^^^^^^^ help: use: `path`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:104:24
++  --> $DIR/unnecessary_to_owned.rs:104:24
 +   |
 +LL |     require_as_ref_str(s.to_owned());
 +   |                        ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:105:26
++  --> $DIR/unnecessary_to_owned.rs:105:24
 +   |
 +LL |     require_as_ref_str(x.to_owned());
 +   |                        ^^^^^^^^^^^^ help: use: `&x`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:106:26
++  --> $DIR/unnecessary_to_owned.rs:106:26
 +   |
 +LL |     require_as_ref_slice(array.to_owned());
 +   |                          ^^^^^^^^^^^^^^^^ help: use: `array`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:107:26
++  --> $DIR/unnecessary_to_owned.rs:107:26
 +   |
 +LL |     require_as_ref_slice(array_ref.to_owned());
 +   |                          ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:109:31
++  --> $DIR/unnecessary_to_owned.rs:108:26
 +   |
 +LL |     require_as_ref_slice(slice.to_owned());
 +   |                          ^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:110:32
++  --> $DIR/unnecessary_to_owned.rs:110:31
 +   |
 +LL |     require_impl_as_ref_c_str(c_str.to_owned());
 +   |                               ^^^^^^^^^^^^^^^^ help: use: `c_str`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:111:30
++  --> $DIR/unnecessary_to_owned.rs:111:32
 +   |
 +LL |     require_impl_as_ref_os_str(os_str.to_owned());
 +   |                                ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:112:29
++  --> $DIR/unnecessary_to_owned.rs:112:30
 +   |
 +LL |     require_impl_as_ref_path(path.to_owned());
 +   |                              ^^^^^^^^^^^^^^^ help: use: `path`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:113:29
++  --> $DIR/unnecessary_to_owned.rs:113:29
 +   |
 +LL |     require_impl_as_ref_str(s.to_owned());
 +   |                             ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:114:31
++  --> $DIR/unnecessary_to_owned.rs:114:29
 +   |
 +LL |     require_impl_as_ref_str(x.to_owned());
 +   |                             ^^^^^^^^^^^^ help: use: `&x`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:115:31
++  --> $DIR/unnecessary_to_owned.rs:115:31
 +   |
 +LL |     require_impl_as_ref_slice(array.to_owned());
 +   |                               ^^^^^^^^^^^^^^^^ help: use: `array`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:116:31
++  --> $DIR/unnecessary_to_owned.rs:116:31
 +   |
 +LL |     require_impl_as_ref_slice(array_ref.to_owned());
 +   |                               ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:118:30
++  --> $DIR/unnecessary_to_owned.rs:117:31
 +   |
 +LL |     require_impl_as_ref_slice(slice.to_owned());
 +   |                               ^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:118:44
++  --> $DIR/unnecessary_to_owned.rs:119:30
 +   |
 +LL |     require_as_ref_str_slice(s.to_owned(), array.to_owned());
 +   |                              ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:119:30
++  --> $DIR/unnecessary_to_owned.rs:119:44
 +   |
 +LL |     require_as_ref_str_slice(s.to_owned(), array.to_owned());
 +   |                                            ^^^^^^^^^^^^^^^^ help: use: `array`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:119:44
++  --> $DIR/unnecessary_to_owned.rs:120:30
 +   |
 +LL |     require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
 +   |                              ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:120:30
++  --> $DIR/unnecessary_to_owned.rs:120:44
 +   |
 +LL |     require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
 +   |                                            ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:120:44
++  --> $DIR/unnecessary_to_owned.rs:121:30
 +   |
 +LL |     require_as_ref_str_slice(s.to_owned(), slice.to_owned());
 +   |                              ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:121:30
++  --> $DIR/unnecessary_to_owned.rs:121:44
 +   |
 +LL |     require_as_ref_str_slice(s.to_owned(), slice.to_owned());
 +   |                                            ^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:121:48
++  --> $DIR/unnecessary_to_owned.rs:122:30
 +   |
 +LL |     require_as_ref_slice_str(array.to_owned(), s.to_owned());
 +   |                              ^^^^^^^^^^^^^^^^ help: use: `array`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:122:30
++  --> $DIR/unnecessary_to_owned.rs:122:48
 +   |
 +LL |     require_as_ref_slice_str(array.to_owned(), s.to_owned());
 +   |                                                ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:122:52
++  --> $DIR/unnecessary_to_owned.rs:123:30
 +   |
 +LL |     require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
 +   |                              ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:123:30
++  --> $DIR/unnecessary_to_owned.rs:123:52
 +   |
 +LL |     require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
 +   |                                                    ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:123:48
++  --> $DIR/unnecessary_to_owned.rs:124:30
 +   |
 +LL |     require_as_ref_slice_str(slice.to_owned(), s.to_owned());
 +   |                              ^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:125:20
++  --> $DIR/unnecessary_to_owned.rs:124:48
 +   |
 +LL |     require_as_ref_slice_str(slice.to_owned(), s.to_owned());
 +   |                                                ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_string`
-   --> $DIR/unnecessary_to_owned.rs:127:13
++  --> $DIR/unnecessary_to_owned.rs:126:20
 +   |
 +LL |     let _ = x.join(&x_ref.to_string());
 +   |                    ^^^^^^^^^^^^^^^^^^ help: use: `x_ref`
 +
 +error: unnecessary use of `to_vec`
-   --> $DIR/unnecessary_to_owned.rs:128:13
++  --> $DIR/unnecessary_to_owned.rs:128:13
 +   |
 +LL |     let _ = slice.to_vec().into_iter();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:129:13
++  --> $DIR/unnecessary_to_owned.rs:129:13
 +   |
 +LL |     let _ = slice.to_owned().into_iter();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 +
 +error: unnecessary use of `to_vec`
-   --> $DIR/unnecessary_to_owned.rs:130:13
++  --> $DIR/unnecessary_to_owned.rs:130:13
 +   |
 +LL |     let _ = [std::path::PathBuf::new()][..].to_vec().into_iter();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:132:13
++  --> $DIR/unnecessary_to_owned.rs:131:13
 +   |
 +LL |     let _ = [std::path::PathBuf::new()][..].to_owned().into_iter();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 +
 +error: unnecessary use of `to_vec`
-   --> $DIR/unnecessary_to_owned.rs:133:13
++  --> $DIR/unnecessary_to_owned.rs:133:13
 +   |
 +LL |     let _ = IntoIterator::into_iter(slice.to_vec());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:134:13
++  --> $DIR/unnecessary_to_owned.rs:134:13
 +   |
 +LL |     let _ = IntoIterator::into_iter(slice.to_owned());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 +
 +error: unnecessary use of `to_vec`
-   --> $DIR/unnecessary_to_owned.rs:135:13
++  --> $DIR/unnecessary_to_owned.rs:135:13
 +   |
 +LL |     let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:196:14
++  --> $DIR/unnecessary_to_owned.rs:136:13
 +   |
 +LL |     let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 +
 +error: unnecessary use of `to_vec`
-   --> $DIR/unnecessary_to_owned.rs:260:24
++  --> $DIR/unnecessary_to_owned.rs:197:14
 +   |
 +LL |     for t in file_types.to_vec() {
 +   |              ^^^^^^^^^^^^^^^^^^^
 +   |
 +help: use
 +   |
 +LL |     for t in file_types {
 +   |              ~~~~~~~~~~
 +help: remove this `&`
 +   |
 +LL -         let path = match get_file_path(&t) {
 +LL +         let path = match get_file_path(t) {
 +   | 
 +
++error: unnecessary use of `to_vec`
++  --> $DIR/unnecessary_to_owned.rs:220:14
++   |
++LL |     let _ = &["x"][..].to_vec().into_iter();
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()`
++
++error: unnecessary use of `to_vec`
++  --> $DIR/unnecessary_to_owned.rs:225:14
++   |
++LL |     let _ = &["x"][..].to_vec().into_iter();
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()`
++
 +error: unnecessary use of `to_string`
- error: aborting due to 77 previous errors
++  --> $DIR/unnecessary_to_owned.rs:272:24
 +   |
 +LL |         Box::new(build(y.to_string()))
 +   |                        ^^^^^^^^^^^^^ help: use: `y`
 +
++error: aborting due to 79 previous errors
 +
index 46463a29e9b20deec853a9a7fab3293cad5017d5,0000000000000000000000000000000000000000..c223b5bc711b2a83956542e652457132f8136fd6
mode 100644,000000..100644
--- /dev/null
@@@ -1,32 -1,0 +1,35 @@@
-     const C0: &u8 = &1;
-     if let &(0 | 2) | C0 = &0 {}
 +// run-rustfix
 +
 +#![feature(box_patterns)]
 +#![warn(clippy::unnested_or_patterns)]
 +#![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)]
 +#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
 +
 +fn main() {
++    // Should be ignored by this lint, as nesting requires more characters.
++    if let &0 | &2 = &0 {}
++
 +    if let box (0 | 2) = Box::new(0) {}
 +    if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
++    const C0: Option<u8> = Some(1);
++    if let Some(1 | 2) | C0 = None {}
 +    if let &mut (0 | 2) = &mut 0 {}
 +    if let x @ (0 | 2) = 0 {}
 +    if let (0, 1 | 2 | 3) = (0, 0) {}
 +    if let (1 | 2 | 3, 0) = (0, 0) {}
 +    if let (x, ..) | (x, 1 | 2) = (0, 1) {}
 +    if let [0 | 1] = [0] {}
 +    if let [x, 0 | 1] = [0, 1] {}
 +    if let [x, 0 | 1 | 2] = [0, 1] {}
 +    if let [x, ..] | [x, 1 | 2] = [0, 1] {}
 +    struct TS(u8, u8);
 +    if let TS(0 | 1, x) = TS(0, 0) {}
 +    if let TS(1 | 2 | 3, 0) = TS(0, 0) {}
 +    if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {}
 +    struct S {
 +        x: u8,
 +        y: u8,
 +    }
 +    if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {}
 +    if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
 +}
index 8ce0738bfc27b3c31cfe88e858544cf8d3bddc71,0000000000000000000000000000000000000000..04cd11036e4e00e954a27faf2c35cfb650db530e
mode 100644,000000..100644
--- /dev/null
@@@ -1,32 -1,0 +1,35 @@@
-     const C0: &u8 = &1;
-     if let &0 | C0 | &2 = &0 {}
 +// run-rustfix
 +
 +#![feature(box_patterns)]
 +#![warn(clippy::unnested_or_patterns)]
 +#![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)]
 +#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
 +
 +fn main() {
++    // Should be ignored by this lint, as nesting requires more characters.
++    if let &0 | &2 = &0 {}
++
 +    if let box 0 | box 2 = Box::new(0) {}
 +    if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {}
++    const C0: Option<u8> = Some(1);
++    if let Some(1) | C0 | Some(2) = None {}
 +    if let &mut 0 | &mut 2 = &mut 0 {}
 +    if let x @ 0 | x @ 2 = 0 {}
 +    if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
 +    if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {}
 +    if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {}
 +    if let [0] | [1] = [0] {}
 +    if let [x, 0] | [x, 1] = [0, 1] {}
 +    if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {}
 +    if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {}
 +    struct TS(u8, u8);
 +    if let TS(0, x) | TS(1, x) = TS(0, 0) {}
 +    if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {}
 +    if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {}
 +    struct S {
 +        x: u8,
 +        y: u8,
 +    }
 +    if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
 +    if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
 +}
index de424c3fdb8f2c628da68ce4eab5d39ab5523002,0000000000000000000000000000000000000000..453c66cbba8fc2838311408c637b65e8b20c9e59
mode 100644,000000..100644
--- /dev/null
@@@ -1,179 -1,0 +1,179 @@@
-   --> $DIR/unnested_or_patterns.rs:9:12
 +error: unnested or-patterns
-   --> $DIR/unnested_or_patterns.rs:10:12
++  --> $DIR/unnested_or_patterns.rs:12:12
 +   |
 +LL |     if let box 0 | box 2 = Box::new(0) {}
 +   |            ^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::unnested-or-patterns` implied by `-D warnings`
 +help: nest the patterns
 +   |
 +LL |     if let box (0 | 2) = Box::new(0) {}
 +   |            ~~~~~~~~~~~
 +
 +error: unnested or-patterns
-   --> $DIR/unnested_or_patterns.rs:12:12
++  --> $DIR/unnested_or_patterns.rs:13:12
 +   |
 +LL |     if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
 +   |            ~~~~~~~~~~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
- LL |     if let &0 | C0 | &2 = &0 {}
-    |            ^^^^^^^^^^^^
++  --> $DIR/unnested_or_patterns.rs:15:12
 +   |
- LL |     if let &(0 | 2) | C0 = &0 {}
-    |            ~~~~~~~~~~~~~
++LL |     if let Some(1) | C0 | Some(2) = None {}
++   |            ^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
-   --> $DIR/unnested_or_patterns.rs:13:12
++LL |     if let Some(1 | 2) | C0 = None {}
++   |            ~~~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
-   --> $DIR/unnested_or_patterns.rs:14:12
++  --> $DIR/unnested_or_patterns.rs:16:12
 +   |
 +LL |     if let &mut 0 | &mut 2 = &mut 0 {}
 +   |            ^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let &mut (0 | 2) = &mut 0 {}
 +   |            ~~~~~~~~~~~~
 +
 +error: unnested or-patterns
-   --> $DIR/unnested_or_patterns.rs:15:12
++  --> $DIR/unnested_or_patterns.rs:17:12
 +   |
 +LL |     if let x @ 0 | x @ 2 = 0 {}
 +   |            ^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let x @ (0 | 2) = 0 {}
 +   |            ~~~~~~~~~~~
 +
 +error: unnested or-patterns
-   --> $DIR/unnested_or_patterns.rs:16:12
++  --> $DIR/unnested_or_patterns.rs:18:12
 +   |
 +LL |     if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let (0, 1 | 2 | 3) = (0, 0) {}
 +   |            ~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
-   --> $DIR/unnested_or_patterns.rs:17:12
++  --> $DIR/unnested_or_patterns.rs:19:12
 +   |
 +LL |     if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let (1 | 2 | 3, 0) = (0, 0) {}
 +   |            ~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
-   --> $DIR/unnested_or_patterns.rs:18:12
++  --> $DIR/unnested_or_patterns.rs:20:12
 +   |
 +LL |     if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let (x, ..) | (x, 1 | 2) = (0, 1) {}
 +   |            ~~~~~~~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
-   --> $DIR/unnested_or_patterns.rs:19:12
++  --> $DIR/unnested_or_patterns.rs:21:12
 +   |
 +LL |     if let [0] | [1] = [0] {}
 +   |            ^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let [0 | 1] = [0] {}
 +   |            ~~~~~~~
 +
 +error: unnested or-patterns
-   --> $DIR/unnested_or_patterns.rs:20:12
++  --> $DIR/unnested_or_patterns.rs:22:12
 +   |
 +LL |     if let [x, 0] | [x, 1] = [0, 1] {}
 +   |            ^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let [x, 0 | 1] = [0, 1] {}
 +   |            ~~~~~~~~~~
 +
 +error: unnested or-patterns
-   --> $DIR/unnested_or_patterns.rs:21:12
++  --> $DIR/unnested_or_patterns.rs:23:12
 +   |
 +LL |     if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let [x, 0 | 1 | 2] = [0, 1] {}
 +   |            ~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
-   --> $DIR/unnested_or_patterns.rs:23:12
++  --> $DIR/unnested_or_patterns.rs:24:12
 +   |
 +LL |     if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let [x, ..] | [x, 1 | 2] = [0, 1] {}
 +   |            ~~~~~~~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
-   --> $DIR/unnested_or_patterns.rs:24:12
++  --> $DIR/unnested_or_patterns.rs:26:12
 +   |
 +LL |     if let TS(0, x) | TS(1, x) = TS(0, 0) {}
 +   |            ^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let TS(0 | 1, x) = TS(0, 0) {}
 +   |            ~~~~~~~~~~~~
 +
 +error: unnested or-patterns
-   --> $DIR/unnested_or_patterns.rs:25:12
++  --> $DIR/unnested_or_patterns.rs:27:12
 +   |
 +LL |     if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let TS(1 | 2 | 3, 0) = TS(0, 0) {}
 +   |            ~~~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
-   --> $DIR/unnested_or_patterns.rs:30:12
++  --> $DIR/unnested_or_patterns.rs:28:12
 +   |
 +LL |     if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {}
 +   |            ~~~~~~~~~~~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
++  --> $DIR/unnested_or_patterns.rs:33:12
 +   |
 +LL |     if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {}
 +   |            ~~~~~~~~~~~~~~~~~
 +
 +error: aborting due to 16 previous errors
 +
index ce58a80347b558ef7a46b48b2883def9f0455f71,0000000000000000000000000000000000000000..c23231a99e9f049015d44694fbd8d58ee2b8c1c3
mode 100644,000000..100644
--- /dev/null
@@@ -1,69 -1,0 +1,75 @@@
 +// run-rustfix
 +// aux-build:proc_macro_derive.rs
 +
 +#![warn(clippy::useless_attribute)]
 +#![warn(unreachable_pub)]
 +#![feature(rustc_private)]
 +
 +#![allow(dead_code)]
 +#![cfg_attr(feature = "cargo-clippy", allow(dead_code))]
 +#[rustfmt::skip]
 +#[allow(unused_imports)]
 +#[allow(unused_extern_crates)]
 +#[macro_use]
 +extern crate rustc_middle;
 +
 +#[macro_use]
 +extern crate proc_macro_derive;
 +
 +// don't lint on unused_import for `use` items
 +#[allow(unused_imports)]
 +use std::collections;
 +
 +// don't lint on unused for `use` items
 +#[allow(unused)]
 +use std::option;
 +
 +// don't lint on deprecated for `use` items
 +mod foo {
 +    #[deprecated]
 +    pub struct Bar;
 +}
 +#[allow(deprecated)]
 +pub use foo::Bar;
 +
 +// This should not trigger the lint. There's lint level definitions inside the external derive
 +// that would trigger the useless_attribute lint.
 +#[derive(DeriveSomething)]
 +struct Baz;
 +
 +// don't lint on unreachable_pub for `use` items
 +mod a {
 +    mod b {
 +        #[allow(dead_code)]
 +        #[allow(unreachable_pub)]
 +        pub struct C;
 +    }
 +
 +    #[allow(unreachable_pub)]
 +    pub use self::b::C;
 +}
 +
 +// don't lint on clippy::wildcard_imports for `use` items
 +#[allow(clippy::wildcard_imports)]
 +pub use std::io::prelude::*;
 +
 +// don't lint on clippy::enum_glob_use for `use` items
 +#[allow(clippy::enum_glob_use)]
 +pub use std::cmp::Ordering::*;
 +
++// don't lint on clippy::redundant_pub_crate
++mod c {
++    #[allow(clippy::redundant_pub_crate)]
++    pub(crate) struct S;
++}
++
 +fn test_indented_attr() {
 +    #![allow(clippy::almost_swapped)]
 +    use std::collections::HashSet;
 +
 +    let _ = HashSet::<u32>::default();
 +}
 +
 +fn main() {
 +    test_indented_attr();
 +}
index c82bb9ba07fd731efb14a91a954fb3a132cc6423,0000000000000000000000000000000000000000..7a7b198ea6078e701ce33c9faf5269f72820f5c4
mode 100644,000000..100644
--- /dev/null
@@@ -1,69 -1,0 +1,75 @@@
 +// run-rustfix
 +// aux-build:proc_macro_derive.rs
 +
 +#![warn(clippy::useless_attribute)]
 +#![warn(unreachable_pub)]
 +#![feature(rustc_private)]
 +
 +#[allow(dead_code)]
 +#[cfg_attr(feature = "cargo-clippy", allow(dead_code))]
 +#[rustfmt::skip]
 +#[allow(unused_imports)]
 +#[allow(unused_extern_crates)]
 +#[macro_use]
 +extern crate rustc_middle;
 +
 +#[macro_use]
 +extern crate proc_macro_derive;
 +
 +// don't lint on unused_import for `use` items
 +#[allow(unused_imports)]
 +use std::collections;
 +
 +// don't lint on unused for `use` items
 +#[allow(unused)]
 +use std::option;
 +
 +// don't lint on deprecated for `use` items
 +mod foo {
 +    #[deprecated]
 +    pub struct Bar;
 +}
 +#[allow(deprecated)]
 +pub use foo::Bar;
 +
 +// This should not trigger the lint. There's lint level definitions inside the external derive
 +// that would trigger the useless_attribute lint.
 +#[derive(DeriveSomething)]
 +struct Baz;
 +
 +// don't lint on unreachable_pub for `use` items
 +mod a {
 +    mod b {
 +        #[allow(dead_code)]
 +        #[allow(unreachable_pub)]
 +        pub struct C;
 +    }
 +
 +    #[allow(unreachable_pub)]
 +    pub use self::b::C;
 +}
 +
 +// don't lint on clippy::wildcard_imports for `use` items
 +#[allow(clippy::wildcard_imports)]
 +pub use std::io::prelude::*;
 +
 +// don't lint on clippy::enum_glob_use for `use` items
 +#[allow(clippy::enum_glob_use)]
 +pub use std::cmp::Ordering::*;
 +
++// don't lint on clippy::redundant_pub_crate
++mod c {
++    #[allow(clippy::redundant_pub_crate)]
++    pub(crate) struct S;
++}
++
 +fn test_indented_attr() {
 +    #[allow(clippy::almost_swapped)]
 +    use std::collections::HashSet;
 +
 +    let _ = HashSet::<u32>::default();
 +}
 +
 +fn main() {
 +    test_indented_attr();
 +}
index d0194e4bbbe5b0a3133c130a8ccd30a80402fd59,0000000000000000000000000000000000000000..255d2876355316ed987b39a86231806c7f122d31
mode 100644,000000..100644
--- /dev/null
@@@ -1,22 -1,0 +1,22 @@@
-   --> $DIR/useless_attribute.rs:61:5
 +error: useless lint attribute
 +  --> $DIR/useless_attribute.rs:8:1
 +   |
 +LL | #[allow(dead_code)]
 +   | ^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(dead_code)]`
 +   |
 +   = note: `-D clippy::useless-attribute` implied by `-D warnings`
 +
 +error: useless lint attribute
 +  --> $DIR/useless_attribute.rs:9:1
 +   |
 +LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))]
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)`
 +
 +error: useless lint attribute
++  --> $DIR/useless_attribute.rs:67:5
 +   |
 +LL |     #[allow(clippy::almost_swapped)]
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]`
 +
 +error: aborting due to 3 previous errors
 +
index 8402c33a4cd5fb695d37116caf003763dcf186bd,0000000000000000000000000000000000000000..b6f47ae906b73709b6096e5ffeeb4caa36dedab3
mode 100644,000000..100644
--- /dev/null
@@@ -1,246 -1,0 +1,245 @@@
- #![allow(unused)]
- #![allow(clippy::unnecessary_wraps)]
 +// edition:2015
 +// run-rustfix
 +// aux-build:wildcard_imports_helper.rs
 +
 +// the 2015 edition here is needed because edition 2018 changed the module system
 +// (see https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html) which means the lint
 +// no longer detects some of the cases starting with Rust 2018.
 +// FIXME: We should likely add another edition 2021 test case for this lint
 +
 +#![warn(clippy::wildcard_imports)]
++#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)]
 +#![warn(unused_imports)]
 +
 +extern crate wildcard_imports_helper;
 +
 +use crate::fn_mod::foo;
 +use crate::mod_mod::inner_mod;
 +use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod};
 +#[macro_use]
 +use crate::struct_mod::{A, inner_struct_mod};
 +
 +#[allow(unused_imports)]
 +use wildcard_imports_helper::inner::inner_for_self_import;
 +use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar;
 +use wildcard_imports_helper::{ExternA, extern_foo};
 +
 +use std::io::prelude::*;
 +use wildcard_imports_helper::prelude::v1::*;
 +
 +struct ReadFoo;
 +
 +impl Read for ReadFoo {
 +    fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
 +        Ok(0)
 +    }
 +}
 +
 +mod fn_mod {
 +    pub fn foo() {}
 +}
 +
 +mod mod_mod {
 +    pub mod inner_mod {
 +        pub fn foo() {}
 +    }
 +}
 +
 +mod multi_fn_mod {
 +    pub fn multi_foo() {}
 +    pub fn multi_bar() {}
 +    pub fn multi_baz() {}
 +    pub mod multi_inner_mod {
 +        pub fn foo() {}
 +    }
 +}
 +
 +mod struct_mod {
 +    pub struct A;
 +    pub struct B;
 +    pub mod inner_struct_mod {
 +        pub struct C;
 +    }
 +
 +    #[macro_export]
 +    macro_rules! double_struct_import_test {
 +        () => {
 +            let _ = A;
 +        };
 +    }
 +}
 +
 +fn main() {
 +    foo();
 +    multi_foo();
 +    multi_bar();
 +    multi_inner_mod::foo();
 +    inner_mod::foo();
 +    extern_foo();
 +    inner_extern_bar();
 +
 +    let _ = A;
 +    let _ = inner_struct_mod::C;
 +    let _ = ExternA;
 +    let _ = PreludeModAnywhere;
 +
 +    double_struct_import_test!();
 +    double_struct_import_test!();
 +}
 +
 +mod in_fn_test {
 +    pub use self::inner_exported::*;
 +    #[allow(unused_imports)]
 +    pub(crate) use self::inner_exported2::*;
 +
 +    fn test_intern() {
 +        use crate::fn_mod::foo;
 +
 +        foo();
 +    }
 +
 +    fn test_extern() {
 +        use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo};
 +        use wildcard_imports_helper::{ExternA, extern_foo};
 +
 +        inner_for_self_import::inner_extern_foo();
 +        inner_extern_foo();
 +
 +        extern_foo();
 +
 +        let _ = ExternA;
 +    }
 +
 +    fn test_inner_nested() {
 +        use self::{inner::inner_foo, inner2::inner_bar};
 +
 +        inner_foo();
 +        inner_bar();
 +    }
 +
 +    fn test_extern_reexported() {
 +        use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported};
 +
 +        extern_exported();
 +        let _ = ExternExportedStruct;
 +        let _ = ExternExportedEnum::A;
 +    }
 +
 +    mod inner_exported {
 +        pub fn exported() {}
 +        pub struct ExportedStruct;
 +        pub enum ExportedEnum {
 +            A,
 +        }
 +    }
 +
 +    mod inner_exported2 {
 +        pub(crate) fn exported2() {}
 +    }
 +
 +    mod inner {
 +        pub fn inner_foo() {}
 +    }
 +
 +    mod inner2 {
 +        pub fn inner_bar() {}
 +    }
 +}
 +
 +fn test_reexported() {
 +    use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported};
 +
 +    exported();
 +    let _ = ExportedStruct;
 +    let _ = ExportedEnum::A;
 +}
 +
 +#[rustfmt::skip]
 +fn test_weird_formatting() {
 +    use crate:: in_fn_test::exported;
 +    use crate:: fn_mod::foo;
 +
 +    exported();
 +    foo();
 +}
 +
 +mod super_imports {
 +    fn foofoo() {}
 +
 +    mod should_be_replaced {
 +        use super::foofoo;
 +
 +        fn with_super() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass {
 +        use super::*;
 +
 +        fn with_super() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass_inside_function {
 +        fn with_super_inside_function() {
 +            use super::*;
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass_further_inside {
 +        fn insidefoo() {}
 +        mod inner {
 +            use super::*;
 +            fn with_super() {
 +                let _ = insidefoo();
 +            }
 +        }
 +    }
 +
 +    mod should_be_replaced_futher_inside {
 +        fn insidefoo() {}
 +        mod inner {
 +            use super::insidefoo;
 +            fn with_super() {
 +                let _ = insidefoo();
 +            }
 +        }
 +    }
 +
 +    mod use_explicit_should_be_replaced {
 +        use super_imports::foofoo;
 +
 +        fn with_explicit() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod use_double_super_should_be_replaced {
 +        mod inner {
 +            use super::super::foofoo;
 +
 +            fn with_double_super() {
 +                let _ = foofoo();
 +            }
 +        }
 +    }
 +
 +    mod use_super_explicit_should_be_replaced {
 +        use super::super::super_imports::foofoo;
 +
 +        fn with_super_explicit() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod attestation_should_be_replaced {
 +        use super::foofoo;
 +
 +        fn with_explicit() {
 +            let _ = foofoo();
 +        }
 +    }
 +}
index faaeaade9b02bff03b30793a73638ba5a3a560eb,0000000000000000000000000000000000000000..eb404b7a3de5f661cbb2af6b428ca042830ac985
mode 100644,000000..100644
--- /dev/null
@@@ -1,247 -1,0 +1,246 @@@
- #![allow(unused)]
- #![allow(clippy::unnecessary_wraps)]
 +// edition:2015
 +// run-rustfix
 +// aux-build:wildcard_imports_helper.rs
 +
 +// the 2015 edition here is needed because edition 2018 changed the module system
 +// (see https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html) which means the lint
 +// no longer detects some of the cases starting with Rust 2018.
 +// FIXME: We should likely add another edition 2021 test case for this lint
 +
 +#![warn(clippy::wildcard_imports)]
++#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)]
 +#![warn(unused_imports)]
 +
 +extern crate wildcard_imports_helper;
 +
 +use crate::fn_mod::*;
 +use crate::mod_mod::*;
 +use crate::multi_fn_mod::*;
 +#[macro_use]
 +use crate::struct_mod::*;
 +
 +#[allow(unused_imports)]
 +use wildcard_imports_helper::inner::inner_for_self_import;
 +use wildcard_imports_helper::inner::inner_for_self_import::*;
 +use wildcard_imports_helper::*;
 +
 +use std::io::prelude::*;
 +use wildcard_imports_helper::prelude::v1::*;
 +
 +struct ReadFoo;
 +
 +impl Read for ReadFoo {
 +    fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
 +        Ok(0)
 +    }
 +}
 +
 +mod fn_mod {
 +    pub fn foo() {}
 +}
 +
 +mod mod_mod {
 +    pub mod inner_mod {
 +        pub fn foo() {}
 +    }
 +}
 +
 +mod multi_fn_mod {
 +    pub fn multi_foo() {}
 +    pub fn multi_bar() {}
 +    pub fn multi_baz() {}
 +    pub mod multi_inner_mod {
 +        pub fn foo() {}
 +    }
 +}
 +
 +mod struct_mod {
 +    pub struct A;
 +    pub struct B;
 +    pub mod inner_struct_mod {
 +        pub struct C;
 +    }
 +
 +    #[macro_export]
 +    macro_rules! double_struct_import_test {
 +        () => {
 +            let _ = A;
 +        };
 +    }
 +}
 +
 +fn main() {
 +    foo();
 +    multi_foo();
 +    multi_bar();
 +    multi_inner_mod::foo();
 +    inner_mod::foo();
 +    extern_foo();
 +    inner_extern_bar();
 +
 +    let _ = A;
 +    let _ = inner_struct_mod::C;
 +    let _ = ExternA;
 +    let _ = PreludeModAnywhere;
 +
 +    double_struct_import_test!();
 +    double_struct_import_test!();
 +}
 +
 +mod in_fn_test {
 +    pub use self::inner_exported::*;
 +    #[allow(unused_imports)]
 +    pub(crate) use self::inner_exported2::*;
 +
 +    fn test_intern() {
 +        use crate::fn_mod::*;
 +
 +        foo();
 +    }
 +
 +    fn test_extern() {
 +        use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
 +        use wildcard_imports_helper::*;
 +
 +        inner_for_self_import::inner_extern_foo();
 +        inner_extern_foo();
 +
 +        extern_foo();
 +
 +        let _ = ExternA;
 +    }
 +
 +    fn test_inner_nested() {
 +        use self::{inner::*, inner2::*};
 +
 +        inner_foo();
 +        inner_bar();
 +    }
 +
 +    fn test_extern_reexported() {
 +        use wildcard_imports_helper::*;
 +
 +        extern_exported();
 +        let _ = ExternExportedStruct;
 +        let _ = ExternExportedEnum::A;
 +    }
 +
 +    mod inner_exported {
 +        pub fn exported() {}
 +        pub struct ExportedStruct;
 +        pub enum ExportedEnum {
 +            A,
 +        }
 +    }
 +
 +    mod inner_exported2 {
 +        pub(crate) fn exported2() {}
 +    }
 +
 +    mod inner {
 +        pub fn inner_foo() {}
 +    }
 +
 +    mod inner2 {
 +        pub fn inner_bar() {}
 +    }
 +}
 +
 +fn test_reexported() {
 +    use crate::in_fn_test::*;
 +
 +    exported();
 +    let _ = ExportedStruct;
 +    let _ = ExportedEnum::A;
 +}
 +
 +#[rustfmt::skip]
 +fn test_weird_formatting() {
 +    use crate:: in_fn_test::  * ;
 +    use crate:: fn_mod::
 +        *;
 +
 +    exported();
 +    foo();
 +}
 +
 +mod super_imports {
 +    fn foofoo() {}
 +
 +    mod should_be_replaced {
 +        use super::*;
 +
 +        fn with_super() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass {
 +        use super::*;
 +
 +        fn with_super() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass_inside_function {
 +        fn with_super_inside_function() {
 +            use super::*;
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass_further_inside {
 +        fn insidefoo() {}
 +        mod inner {
 +            use super::*;
 +            fn with_super() {
 +                let _ = insidefoo();
 +            }
 +        }
 +    }
 +
 +    mod should_be_replaced_futher_inside {
 +        fn insidefoo() {}
 +        mod inner {
 +            use super::*;
 +            fn with_super() {
 +                let _ = insidefoo();
 +            }
 +        }
 +    }
 +
 +    mod use_explicit_should_be_replaced {
 +        use super_imports::*;
 +
 +        fn with_explicit() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod use_double_super_should_be_replaced {
 +        mod inner {
 +            use super::super::*;
 +
 +            fn with_double_super() {
 +                let _ = foofoo();
 +            }
 +        }
 +    }
 +
 +    mod use_super_explicit_should_be_replaced {
 +        use super::super::super_imports::*;
 +
 +        fn with_super_explicit() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod attestation_should_be_replaced {
 +        use super::*;
 +
 +        fn with_explicit() {
 +            let _ = foofoo();
 +        }
 +    }
 +}
index 7534a65ec9bd56c0878846b81663d05ef269cb92,0000000000000000000000000000000000000000..626c1754fc82c8720bbc3e1ac84c0f932dbce630
mode 100644,000000..100644
--- /dev/null
@@@ -1,132 -1,0 +1,132 @@@
-   --> $DIR/wildcard_imports.rs:17:5
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:18:5
++  --> $DIR/wildcard_imports.rs:16:5
 +   |
 +LL | use crate::fn_mod::*;
 +   |     ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
 +   |
 +   = note: `-D clippy::wildcard-imports` implied by `-D warnings`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:19:5
++  --> $DIR/wildcard_imports.rs:17:5
 +   |
 +LL | use crate::mod_mod::*;
 +   |     ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:21:5
++  --> $DIR/wildcard_imports.rs:18:5
 +   |
 +LL | use crate::multi_fn_mod::*;
 +   |     ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:25:5
++  --> $DIR/wildcard_imports.rs:20:5
 +   |
 +LL | use crate::struct_mod::*;
 +   |     ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:26:5
++  --> $DIR/wildcard_imports.rs:24:5
 +   |
 +LL | use wildcard_imports_helper::inner::inner_for_self_import::*;
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:97:13
++  --> $DIR/wildcard_imports.rs:25:5
 +   |
 +LL | use wildcard_imports_helper::*;
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:103:75
++  --> $DIR/wildcard_imports.rs:96:13
 +   |
 +LL |         use crate::fn_mod::*;
 +   |             ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:104:13
++  --> $DIR/wildcard_imports.rs:102:75
 +   |
 +LL |         use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
 +   |                                                                           ^ help: try: `inner_extern_foo`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:115:20
++  --> $DIR/wildcard_imports.rs:103:13
 +   |
 +LL |         use wildcard_imports_helper::*;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:115:30
++  --> $DIR/wildcard_imports.rs:114:20
 +   |
 +LL |         use self::{inner::*, inner2::*};
 +   |                    ^^^^^^^^ help: try: `inner::inner_foo`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:122:13
++  --> $DIR/wildcard_imports.rs:114:30
 +   |
 +LL |         use self::{inner::*, inner2::*};
 +   |                              ^^^^^^^^^ help: try: `inner2::inner_bar`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:151:9
++  --> $DIR/wildcard_imports.rs:121:13
 +   |
 +LL |         use wildcard_imports_helper::*;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:160:9
++  --> $DIR/wildcard_imports.rs:150:9
 +   |
 +LL |     use crate::in_fn_test::*;
 +   |         ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:161:9
++  --> $DIR/wildcard_imports.rs:159:9
 +   |
 +LL |     use crate:: in_fn_test::  * ;
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:172:13
++  --> $DIR/wildcard_imports.rs:160:9
 +   |
 +LL |       use crate:: fn_mod::
 +   |  _________^
 +LL | |         *;
 +   | |_________^ help: try: `crate:: fn_mod::foo`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:207:17
++  --> $DIR/wildcard_imports.rs:171:13
 +   |
 +LL |         use super::*;
 +   |             ^^^^^^^^ help: try: `super::foofoo`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:215:13
++  --> $DIR/wildcard_imports.rs:206:17
 +   |
 +LL |             use super::*;
 +   |                 ^^^^^^^^ help: try: `super::insidefoo`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:224:17
++  --> $DIR/wildcard_imports.rs:214:13
 +   |
 +LL |         use super_imports::*;
 +   |             ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:233:13
++  --> $DIR/wildcard_imports.rs:223:17
 +   |
 +LL |             use super::super::*;
 +   |                 ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
 +
 +error: usage of wildcard import
-   --> $DIR/wildcard_imports.rs:241:13
++  --> $DIR/wildcard_imports.rs:232:13
 +   |
 +LL |         use super::super::super_imports::*;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
 +
 +error: usage of wildcard import
++  --> $DIR/wildcard_imports.rs:240:13
 +   |
 +LL |         use super::*;
 +   |             ^^^^^^^^ help: try: `super::foofoo`
 +
 +error: aborting due to 21 previous errors
 +
index f8fee4b3ab2d8fb420c66ada277a88a046a4bc2d,0000000000000000000000000000000000000000..e3cc90ee222ada2f2e2ca21742f13a03b10ba50b
mode 100644,000000..100644
--- /dev/null
@@@ -1,211 -1,0 +1,206 @@@
-         // Should lint: is_ methods should only take &self, or no self at all.
-         fn is_still_buggy(&mut self) -> bool {
-             false
-         }
 +#![warn(clippy::wrong_self_convention)]
 +#![allow(dead_code)]
 +
 +fn main() {}
 +
 +#[derive(Clone, Copy)]
 +struct Foo;
 +
 +impl Foo {
 +    fn as_i32(self) {}
 +    fn as_u32(&self) {}
 +    fn into_i32(self) {}
 +    fn is_i32(self) {}
 +    fn is_u32(&self) {}
 +    fn to_i32(self) {}
 +    fn from_i32(self) {}
 +
 +    pub fn as_i64(self) {}
 +    pub fn into_i64(self) {}
 +    pub fn is_i64(self) {}
 +    pub fn to_i64(self) {}
 +    pub fn from_i64(self) {}
 +    // check whether the lint can be allowed at the function level
 +    #[allow(clippy::wrong_self_convention)]
 +    pub fn from_cake(self) {}
 +
 +    fn as_x<F: AsRef<Self>>(_: F) {}
 +    fn as_y<F: AsRef<Foo>>(_: F) {}
 +}
 +
 +struct Bar;
 +
 +impl Bar {
 +    fn as_i32(self) {}
 +    fn as_u32(&self) {}
 +    fn into_i32(&self) {}
 +    fn into_u32(self) {}
 +    fn is_i32(self) {}
 +    fn is_u32(&self) {}
 +    fn to_i32(self) {}
 +    fn to_u32(&self) {}
 +    fn from_i32(self) {}
 +
 +    pub fn as_i64(self) {}
 +    pub fn into_i64(&self) {}
 +    pub fn is_i64(self) {}
 +    pub fn to_i64(self) {}
 +    pub fn from_i64(self) {}
 +
 +    // test for false positives
 +    fn as_(self) {}
 +    fn into_(&self) {}
 +    fn is_(self) {}
 +    fn to_(self) {}
 +    fn from_(self) {}
 +    fn to_mut(&mut self) {}
 +}
 +
 +// Allow Box<Self>, Rc<Self>, Arc<Self> for methods that take conventionally take Self by value
 +#[allow(clippy::boxed_local)]
 +mod issue4293 {
 +    use std::rc::Rc;
 +    use std::sync::Arc;
 +
 +    struct T;
 +
 +    impl T {
 +        fn into_s1(self: Box<Self>) {}
 +        fn into_s2(self: Rc<Self>) {}
 +        fn into_s3(self: Arc<Self>) {}
 +
 +        fn into_t1(self: Box<T>) {}
 +        fn into_t2(self: Rc<T>) {}
 +        fn into_t3(self: Arc<T>) {}
 +    }
 +}
 +
 +// False positive for async (see #4037)
 +mod issue4037 {
 +    pub struct Foo;
 +    pub struct Bar;
 +
 +    impl Foo {
 +        pub async fn into_bar(self) -> Bar {
 +            Bar
 +        }
 +    }
 +}
 +
 +// Lint also in trait definition (see #6307)
 +mod issue6307 {
 +    trait T: Sized {
 +        fn as_i32(self) {}
 +        fn as_u32(&self) {}
 +        fn into_i32(self) {}
 +        fn into_i32_ref(&self) {}
 +        fn into_u32(self) {}
 +        fn is_i32(self) {}
 +        fn is_u32(&self) {}
 +        fn to_i32(self) {}
 +        fn to_u32(&self) {}
 +        fn from_i32(self) {}
 +        // check whether the lint can be allowed at the function level
 +        #[allow(clippy::wrong_self_convention)]
 +        fn from_cake(self) {}
 +
 +        // test for false positives
 +        fn as_(self) {}
 +        fn into_(&self) {}
 +        fn is_(self) {}
 +        fn to_(self) {}
 +        fn from_(self) {}
 +        fn to_mut(&mut self) {}
 +    }
 +
 +    trait U {
 +        fn as_i32(self);
 +        fn as_u32(&self);
 +        fn into_i32(self);
 +        fn into_i32_ref(&self);
 +        fn into_u32(self);
 +        fn is_i32(self);
 +        fn is_u32(&self);
 +        fn to_i32(self);
 +        fn to_u32(&self);
 +        fn from_i32(self);
 +        // check whether the lint can be allowed at the function level
 +        #[allow(clippy::wrong_self_convention)]
 +        fn from_cake(self);
 +
 +        // test for false positives
 +        fn as_(self);
 +        fn into_(&self);
 +        fn is_(self);
 +        fn to_(self);
 +        fn from_(self);
 +        fn to_mut(&mut self);
 +    }
 +
 +    trait C: Copy {
 +        fn as_i32(self);
 +        fn as_u32(&self);
 +        fn into_i32(self);
 +        fn into_i32_ref(&self);
 +        fn into_u32(self);
 +        fn is_i32(self);
 +        fn is_u32(&self);
 +        fn to_i32(self);
 +        fn to_u32(&self);
 +        fn from_i32(self);
 +        // check whether the lint can be allowed at the function level
 +        #[allow(clippy::wrong_self_convention)]
 +        fn from_cake(self);
 +
 +        // test for false positives
 +        fn as_(self);
 +        fn into_(&self);
 +        fn is_(self);
 +        fn to_(self);
 +        fn from_(self);
 +        fn to_mut(&mut self);
 +    }
 +}
 +
 +mod issue6727 {
 +    #[derive(Clone, Copy)]
 +    struct FooCopy;
 +
 +    impl FooCopy {
 +        fn to_u64(self) -> u64 {
 +            1
 +        }
 +        // trigger lint
 +        fn to_u64_v2(&self) -> u64 {
 +            1
 +        }
 +    }
 +
 +    struct FooNoCopy;
 +
 +    impl FooNoCopy {
 +        // trigger lint
 +        fn to_u64(self) -> u64 {
 +            2
 +        }
 +        fn to_u64_v2(&self) -> u64 {
 +            2
 +        }
 +    }
 +}
 +
 +pub mod issue8142 {
 +    struct S;
 +
 +    impl S {
 +        // Should not lint: "no self at all" is allowed.
 +        fn is_forty_two(x: u32) -> bool {
 +            x == 42
 +        }
 +
 +        // Should not lint: &self is allowed.
 +        fn is_test_code(&self) -> bool {
 +            true
 +        }
 +    }
 +}
index 5493a99572e068746d2900921ca208595e99f9ad,0000000000000000000000000000000000000000..2e7ee51d7e11a7e07593df9a926e2b7912a2f78c
mode 100644,000000..100644
--- /dev/null
@@@ -1,203 -1,0 +1,195 @@@
- error: methods called `is_*` usually take `self` by reference or no `self`
 +error: methods called `from_*` usually take no `self`
 +  --> $DIR/wrong_self_convention.rs:16:17
 +   |
 +LL |     fn from_i32(self) {}
 +   |                 ^^^^
 +   |
 +   = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods called `from_*` usually take no `self`
 +  --> $DIR/wrong_self_convention.rs:22:21
 +   |
 +LL |     pub fn from_i64(self) {}
 +   |                     ^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
 +  --> $DIR/wrong_self_convention.rs:34:15
 +   |
 +LL |     fn as_i32(self) {}
 +   |               ^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods called `into_*` usually take `self` by value
 +  --> $DIR/wrong_self_convention.rs:36:17
 +   |
 +LL |     fn into_i32(&self) {}
 +   |                 ^^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
- error: methods called `is_*` usually take `self` by reference or no `self`
++error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self`
 +  --> $DIR/wrong_self_convention.rs:38:15
 +   |
 +LL |     fn is_i32(self) {}
 +   |               ^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
 +  --> $DIR/wrong_self_convention.rs:40:15
 +   |
 +LL |     fn to_i32(self) {}
 +   |               ^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods called `from_*` usually take no `self`
 +  --> $DIR/wrong_self_convention.rs:42:17
 +   |
 +LL |     fn from_i32(self) {}
 +   |                 ^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
 +  --> $DIR/wrong_self_convention.rs:44:19
 +   |
 +LL |     pub fn as_i64(self) {}
 +   |                   ^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods called `into_*` usually take `self` by value
 +  --> $DIR/wrong_self_convention.rs:45:21
 +   |
 +LL |     pub fn into_i64(&self) {}
 +   |                     ^^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
- error: methods called `is_*` usually take `self` by reference or no `self`
++error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self`
 +  --> $DIR/wrong_self_convention.rs:46:19
 +   |
 +LL |     pub fn is_i64(self) {}
 +   |                   ^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
 +  --> $DIR/wrong_self_convention.rs:47:19
 +   |
 +LL |     pub fn to_i64(self) {}
 +   |                   ^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods called `from_*` usually take no `self`
 +  --> $DIR/wrong_self_convention.rs:48:21
 +   |
 +LL |     pub fn from_i64(self) {}
 +   |                     ^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
 +  --> $DIR/wrong_self_convention.rs:93:19
 +   |
 +LL |         fn as_i32(self) {}
 +   |                   ^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods called `into_*` usually take `self` by value
 +  --> $DIR/wrong_self_convention.rs:96:25
 +   |
 +LL |         fn into_i32_ref(&self) {}
 +   |                         ^^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
- error: methods called `is_*` usually take `self` by reference or no `self`
++error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self`
 +  --> $DIR/wrong_self_convention.rs:98:19
 +   |
 +LL |         fn is_i32(self) {}
 +   |                   ^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods called `from_*` usually take no `self`
 +  --> $DIR/wrong_self_convention.rs:102:21
 +   |
 +LL |         fn from_i32(self) {}
 +   |                     ^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
 +  --> $DIR/wrong_self_convention.rs:117:19
 +   |
 +LL |         fn as_i32(self);
 +   |                   ^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods called `into_*` usually take `self` by value
 +  --> $DIR/wrong_self_convention.rs:120:25
 +   |
 +LL |         fn into_i32_ref(&self);
 +   |                         ^^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
- error: methods called `is_*` usually take `self` by reference or no `self`
-   --> $DIR/wrong_self_convention.rs:197:27
-    |
- LL |         fn is_still_buggy(&mut self) -> bool {
-    |                           ^^^^^^^^^
-    |
-    = help: consider choosing a less ambiguous name
- error: aborting due to 25 previous errors
++error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self`
 +  --> $DIR/wrong_self_convention.rs:122:19
 +   |
 +LL |         fn is_i32(self);
 +   |                   ^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods called `from_*` usually take no `self`
 +  --> $DIR/wrong_self_convention.rs:126:21
 +   |
 +LL |         fn from_i32(self);
 +   |                     ^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods called `into_*` usually take `self` by value
 +  --> $DIR/wrong_self_convention.rs:144:25
 +   |
 +LL |         fn into_i32_ref(&self);
 +   |                         ^^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods called `from_*` usually take no `self`
 +  --> $DIR/wrong_self_convention.rs:150:21
 +   |
 +LL |         fn from_i32(self);
 +   |                     ^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value
 +  --> $DIR/wrong_self_convention.rs:174:22
 +   |
 +LL |         fn to_u64_v2(&self) -> u64 {
 +   |                      ^^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
 +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
 +  --> $DIR/wrong_self_convention.rs:183:19
 +   |
 +LL |         fn to_u64(self) -> u64 {
 +   |                   ^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +
++error: aborting due to 24 previous errors
 +
index a8fe8331133778ebcf934b4b234c07c430f484c9,0000000000000000000000000000000000000000..0dcf4743e8b8dbcd1186b794a9f2846a763a2da2
mode 100644,000000..100644
--- /dev/null
@@@ -1,106 -1,0 +1,116 @@@
 +#![warn(clippy::wrong_self_convention)]
 +#![allow(dead_code)]
 +
 +fn main() {}
 +
 +mod issue6983 {
 +    pub struct Thing;
 +    pub trait Trait {
 +        fn to_thing(&self) -> Thing;
 +    }
 +
 +    impl Trait for u8 {
 +        // don't trigger, e.g. `ToString` from `std` requires `&self`
 +        fn to_thing(&self) -> Thing {
 +            Thing
 +        }
 +    }
 +
 +    trait ToU64 {
 +        fn to_u64(self) -> u64;
 +    }
 +
 +    struct FooNoCopy;
 +    // don't trigger
 +    impl ToU64 for FooNoCopy {
 +        fn to_u64(self) -> u64 {
 +            2
 +        }
 +    }
 +}
 +
 +mod issue7032 {
 +    trait Foo {
 +        fn from_usize(x: usize) -> Self;
 +    }
 +    // don't trigger
 +    impl Foo for usize {
 +        fn from_usize(x: usize) -> Self {
 +            x
 +        }
 +    }
 +}
 +
 +mod issue7179 {
 +    pub struct S(i32);
 +
 +    impl S {
 +        // don't trigger (`s` is not `self`)
 +        pub fn from_be(s: Self) -> Self {
 +            S(i32::from_be(s.0))
 +        }
 +
 +        // lint
 +        pub fn from_be_self(self) -> Self {
 +            S(i32::from_be(self.0))
 +        }
 +    }
 +
 +    trait T {
 +        // don't trigger (`s` is not `self`)
 +        fn from_be(s: Self) -> Self;
 +        // lint
 +        fn from_be_self(self) -> Self;
 +    }
 +
 +    trait Foo: Sized {
 +        fn as_byte_slice(slice: &[Self]) -> &[u8];
 +    }
 +}
 +
 +mod issue3414 {
 +    struct CellLikeThing<T>(T);
 +
 +    impl<T> CellLikeThing<T> {
 +        // don't trigger
 +        fn into_inner(this: Self) -> T {
 +            this.0
 +        }
 +    }
 +
 +    impl<T> std::ops::Deref for CellLikeThing<T> {
 +        type Target = T;
 +
 +        fn deref(&self) -> &T {
 +            &self.0
 +        }
 +    }
 +}
 +
 +// don't trigger
 +mod issue4546 {
 +    use std::pin::Pin;
 +
 +    struct S;
 +    impl S {
 +        pub fn as_mut(self: Pin<&mut Self>) {}
 +
 +        pub fn as_other_thingy(self: Pin<&Self>) {}
 +
 +        pub fn is_other_thingy(self: Pin<&Self>) {}
 +
 +        pub fn to_mut(self: Pin<&mut Self>) {}
 +
 +        pub fn to_other_thingy(self: Pin<&Self>) {}
 +    }
 +}
++
++mod issue_8480_8513 {
++    struct Cat(String);
++
++    impl Cat {
++        fn is_animal(&mut self) -> bool {
++            todo!();
++        }
++    }
++}