]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit '4f142aa1058f14f153f8bfd2d82f04ddb9982388' into clippyup
authorflip1995 <hello@philkrones.com>
Sun, 23 Oct 2022 13:18:45 +0000 (15:18 +0200)
committerflip1995 <hello@philkrones.com>
Sun, 23 Oct 2022 13:18:45 +0000 (15:18 +0200)
278 files changed:
1  2 
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/CHANGELOG.md
src/tools/clippy/CONTRIBUTING.md
src/tools/clippy/book/src/development/adding_lints.md
src/tools/clippy/book/src/development/basics.md
src/tools/clippy/clippy_dev/src/serve.rs
src/tools/clippy/clippy_dev/src/setup/intellij.rs
src/tools/clippy/clippy_dev/src/update_lints.rs
src/tools/clippy/clippy_lints/src/booleans.rs
src/tools/clippy/clippy_lints/src/box_default.rs
src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs
src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs
src/tools/clippy/clippy_lints/src/casts/mod.rs
src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
src/tools/clippy/clippy_lints/src/comparison_chain.rs
src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
src/tools/clippy/clippy_lints/src/dereference.rs
src/tools/clippy/clippy_lints/src/derive.rs
src/tools/clippy/clippy_lints/src/disallowed_methods.rs
src/tools/clippy/clippy_lints/src/entry.rs
src/tools/clippy/clippy_lints/src/eta_reduction.rs
src/tools/clippy/clippy_lints/src/format_args.rs
src/tools/clippy/clippy_lints/src/from_over_into.rs
src/tools/clippy/clippy_lints/src/functions/must_use.rs
src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs
src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs
src/tools/clippy/clippy_lints/src/large_enum_variant.rs
src/tools/clippy/clippy_lints/src/let_underscore.rs
src/tools/clippy/clippy_lints/src/lib.register_all.rs
src/tools/clippy/clippy_lints/src/lib.register_complexity.rs
src/tools/clippy/clippy_lints/src/lib.register_internal.rs
src/tools/clippy/clippy_lints/src/lib.register_lints.rs
src/tools/clippy/clippy_lints/src/lib.register_nursery.rs
src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs
src/tools/clippy/clippy_lints/src/lib.register_restriction.rs
src/tools/clippy/clippy_lints/src/lib.register_style.rs
src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
src/tools/clippy/clippy_lints/src/manual_async_fn.rs
src/tools/clippy/clippy_lints/src/manual_clamp.rs
src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
src/tools/clippy/clippy_lints/src/matches/manual_filter.rs
src/tools/clippy/clippy_lints/src/matches/manual_map.rs
src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs
src/tools/clippy/clippy_lints/src/matches/mod.rs
src/tools/clippy/clippy_lints/src/matches/single_match.rs
src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs
src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
src/tools/clippy/clippy_lints/src/methods/or_fun_call.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/misc_early/literal_suffix.rs
src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs
src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs
src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs
src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
src/tools/clippy/clippy_lints/src/needless_for_each.rs
src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs
src/tools/clippy/clippy_lints/src/non_copy_const.rs
src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs
src/tools/clippy/clippy_lints/src/partial_pub_fields.rs
src/tools/clippy/clippy_lints/src/ptr.rs
src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
src/tools/clippy/clippy_lints/src/redundant_clone.rs
src/tools/clippy/clippy_lints/src/ref_option_ref.rs
src/tools/clippy/clippy_lints/src/shadow.rs
src/tools/clippy/clippy_lints/src/transmute/utils.rs
src/tools/clippy/clippy_lints/src/types/rc_buffer.rs
src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
src/tools/clippy/clippy_lints/src/unused_io_amount.rs
src/tools/clippy/clippy_lints/src/useless_conversion.rs
src/tools/clippy/clippy_lints/src/utils/author.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/produce_ice.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
src/tools/clippy/clippy_utils/src/attrs.rs
src/tools/clippy/clippy_utils/src/consts.rs
src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
src/tools/clippy/clippy_utils/src/lib.rs
src/tools/clippy/clippy_utils/src/macros.rs
src/tools/clippy/clippy_utils/src/mir/maybe_storage_live.rs
src/tools/clippy/clippy_utils/src/mir/mod.rs
src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs
src/tools/clippy/clippy_utils/src/mir/possible_origin.rs
src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs
src/tools/clippy/clippy_utils/src/numeric_literal.rs
src/tools/clippy/clippy_utils/src/paths.rs
src/tools/clippy/clippy_utils/src/sugg.rs
src/tools/clippy/lintcheck/src/config.rs
src/tools/clippy/lintcheck/src/driver.rs
src/tools/clippy/lintcheck/src/main.rs
src/tools/clippy/lintcheck/src/recursive.rs
src/tools/clippy/rust-toolchain
src/tools/clippy/src/docs.rs
src/tools/clippy/src/docs/as_ptr_cast_mut.txt
src/tools/clippy/src/docs/box_default.txt
src/tools/clippy/src/docs/cast_nan_to_int.txt
src/tools/clippy/src/docs/manual_filter.txt
src/tools/clippy/src/docs/missing_trait_methods.txt
src/tools/clippy/src/docs/partial_pub_fields.txt
src/tools/clippy/src/docs/unused_format_specs.txt
src/tools/clippy/tests/compile-test.rs
src/tools/clippy/tests/dogfood.rs
src/tools/clippy/tests/ui-internal/custom_ice_message.stderr
src/tools/clippy/tests/ui-internal/invalid_paths.rs
src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed
src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs
src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs
src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
src/tools/clippy/tests/ui/as_ptr_cast_mut.rs
src/tools/clippy/tests/ui/as_ptr_cast_mut.stderr
src/tools/clippy/tests/ui/author.stdout
src/tools/clippy/tests/ui/author/blocks.stdout
src/tools/clippy/tests/ui/author/call.stdout
src/tools/clippy/tests/ui/author/if.stdout
src/tools/clippy/tests/ui/author/issue_3849.stdout
src/tools/clippy/tests/ui/author/loop.stdout
src/tools/clippy/tests/ui/author/matches.stdout
src/tools/clippy/tests/ui/author/repeat.stdout
src/tools/clippy/tests/ui/author/struct.stdout
src/tools/clippy/tests/ui/box_default.fixed
src/tools/clippy/tests/ui/box_default.rs
src/tools/clippy/tests/ui/box_default.stderr
src/tools/clippy/tests/ui/box_default_no_std.rs
src/tools/clippy/tests/ui/cast_abs_to_unsigned.fixed
src/tools/clippy/tests/ui/cast_abs_to_unsigned.rs
src/tools/clippy/tests/ui/cast_abs_to_unsigned.stderr
src/tools/clippy/tests/ui/cast_lossless_bool.fixed
src/tools/clippy/tests/ui/cast_lossless_bool.rs
src/tools/clippy/tests/ui/cast_lossless_bool.stderr
src/tools/clippy/tests/ui/cast_nan_to_int.rs
src/tools/clippy/tests/ui/cast_nan_to_int.stderr
src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed
src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs
src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr
src/tools/clippy/tests/ui/checked_conversions.fixed
src/tools/clippy/tests/ui/checked_conversions.rs
src/tools/clippy/tests/ui/checked_conversions.stderr
src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed
src/tools/clippy/tests/ui/cloned_instead_of_copied.rs
src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr
src/tools/clippy/tests/ui/collapsible_match.rs
src/tools/clippy/tests/ui/collapsible_match.stderr
src/tools/clippy/tests/ui/crashes/ice-9625.rs
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/err_expect.fixed
src/tools/clippy/tests/ui/err_expect.rs
src/tools/clippy/tests/ui/err_expect.stderr
src/tools/clippy/tests/ui/filter_map_next_fixable.fixed
src/tools/clippy/tests/ui/filter_map_next_fixable.rs
src/tools/clippy/tests/ui/filter_map_next_fixable.stderr
src/tools/clippy/tests/ui/format_args.fixed
src/tools/clippy/tests/ui/format_args.rs
src/tools/clippy/tests/ui/format_args.stderr
src/tools/clippy/tests/ui/from_over_into.fixed
src/tools/clippy/tests/ui/from_over_into.rs
src/tools/clippy/tests/ui/from_over_into.stderr
src/tools/clippy/tests/ui/from_over_into_unfixable.rs
src/tools/clippy/tests/ui/from_over_into_unfixable.stderr
src/tools/clippy/tests/ui/implicit_saturating_sub.fixed
src/tools/clippy/tests/ui/implicit_saturating_sub.rs
src/tools/clippy/tests/ui/implicit_saturating_sub.stderr
src/tools/clippy/tests/ui/literals.rs
src/tools/clippy/tests/ui/literals.stderr
src/tools/clippy/tests/ui/manual_assert.edition2018.fixed
src/tools/clippy/tests/ui/manual_assert.edition2018.stderr
src/tools/clippy/tests/ui/manual_assert.edition2021.fixed
src/tools/clippy/tests/ui/manual_assert.rs
src/tools/clippy/tests/ui/manual_clamp.rs
src/tools/clippy/tests/ui/manual_clamp.stderr
src/tools/clippy/tests/ui/manual_filter.fixed
src/tools/clippy/tests/ui/manual_filter.rs
src/tools/clippy/tests/ui/manual_filter.stderr
src/tools/clippy/tests/ui/manual_rem_euclid.fixed
src/tools/clippy/tests/ui/manual_rem_euclid.rs
src/tools/clippy/tests/ui/manual_rem_euclid.stderr
src/tools/clippy/tests/ui/manual_strip.rs
src/tools/clippy/tests/ui/manual_strip.stderr
src/tools/clippy/tests/ui/map_unwrap_or.rs
src/tools/clippy/tests/ui/map_unwrap_or.stderr
src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed
src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs
src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr
src/tools/clippy/tests/ui/match_overlapping_arm.rs
src/tools/clippy/tests/ui/match_overlapping_arm.stderr
src/tools/clippy/tests/ui/match_single_binding.fixed
src/tools/clippy/tests/ui/match_single_binding.rs
src/tools/clippy/tests/ui/match_single_binding.stderr
src/tools/clippy/tests/ui/match_wild_err_arm.rs
src/tools/clippy/tests/ui/match_wild_err_arm.stderr
src/tools/clippy/tests/ui/mem_replace.fixed
src/tools/clippy/tests/ui/mem_replace.rs
src/tools/clippy/tests/ui/mem_replace.stderr
src/tools/clippy/tests/ui/min_rust_version_attr.rs
src/tools/clippy/tests/ui/min_rust_version_attr.stderr
src/tools/clippy/tests/ui/min_rust_version_invalid_attr.rs
src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr
src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
src/tools/clippy/tests/ui/missing_trait_methods.rs
src/tools/clippy/tests/ui/missing_trait_methods.stderr
src/tools/clippy/tests/ui/needless_borrow.fixed
src/tools/clippy/tests/ui/needless_borrow.rs
src/tools/clippy/tests/ui/needless_borrow.stderr
src/tools/clippy/tests/ui/option_as_ref_deref.fixed
src/tools/clippy/tests/ui/option_as_ref_deref.rs
src/tools/clippy/tests/ui/option_as_ref_deref.stderr
src/tools/clippy/tests/ui/or_fun_call.fixed
src/tools/clippy/tests/ui/or_fun_call.rs
src/tools/clippy/tests/ui/partial_pub_fields.rs
src/tools/clippy/tests/ui/partial_pub_fields.stderr
src/tools/clippy/tests/ui/ptr_arg.rs
src/tools/clippy/tests/ui/ptr_arg.stderr
src/tools/clippy/tests/ui/range_contains.fixed
src/tools/clippy/tests/ui/range_contains.rs
src/tools/clippy/tests/ui/range_contains.stderr
src/tools/clippy/tests/ui/redundant_field_names.fixed
src/tools/clippy/tests/ui/redundant_field_names.rs
src/tools/clippy/tests/ui/redundant_field_names.stderr
src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed
src/tools/clippy/tests/ui/redundant_static_lifetimes.rs
src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr
src/tools/clippy/tests/ui/ref_option_ref.rs
src/tools/clippy/tests/ui/uninlined_format_args.fixed
src/tools/clippy/tests/ui/uninlined_format_args.rs
src/tools/clippy/tests/ui/uninlined_format_args.stderr
src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2018.fixed
src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2018.stderr
src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2021.fixed
src/tools/clippy/tests/ui/uninlined_format_args_panic.edition2021.stderr
src/tools/clippy/tests/ui/uninlined_format_args_panic.rs
src/tools/clippy/tests/ui/unnecessary_cast.fixed
src/tools/clippy/tests/ui/unnecessary_cast.rs
src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
src/tools/clippy/tests/ui/unnecessary_to_owned.rs
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/unused_format_specs.fixed
src/tools/clippy/tests/ui/unused_format_specs.rs
src/tools/clippy/tests/ui/unused_format_specs.stderr
src/tools/clippy/tests/ui/unused_format_specs_unfixable.rs
src/tools/clippy/tests/ui/unused_format_specs_unfixable.stderr
src/tools/clippy/tests/ui/use_self.fixed
src/tools/clippy/tests/ui/use_self.rs
src/tools/clippy/tests/ui/use_self.stderr
src/tools/clippy/tests/versioncheck.rs
src/tools/clippy/util/gh-pages/index.html
src/tools/clippy/util/gh-pages/script.js

index fac2c99714d9bf808d6a13f6bae1b6a82af59356,0000000000000000000000000000000000000000..b992130119713b0408b76341873995a28a5c5e38
mode 100644,000000..100644
--- /dev/null
@@@ -1,77 -1,0 +1,78 @@@
 +name: Clippy Test
 +
 +on:
 +  push:
 +    # Ignore bors branches, since they are covered by `clippy_bors.yml`
 +    branches-ignore:
 +      - auto
 +      - try
 +    # Don't run Clippy tests, when only text files were modified
 +    paths-ignore:
 +    - 'COPYRIGHT'
 +    - 'LICENSE-*'
 +    - '**.md'
 +    - '**.txt'
 +  pull_request:
 +    # Don't run Clippy tests, when only text files were modified
 +    paths-ignore:
 +    - 'COPYRIGHT'
 +    - 'LICENSE-*'
 +    - '**.md'
 +    - '**.txt'
 +
 +env:
 +  RUST_BACKTRACE: 1
 +  CARGO_TARGET_DIR: '${{ github.workspace }}/target'
 +  NO_FMT_TEST: 1
 +  CARGO_INCREMENTAL: 0
++  CARGO_UNSTABLE_SPARSE_REGISTRY: true
 +
 +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 30607af490124589f44ce0cf831ca57ab8edad98,0000000000000000000000000000000000000000..6448b2d4068deb480aa9484fb082288e7c8b81ca
mode 100644,000000..100644
--- /dev/null
@@@ -1,282 -1,0 +1,283 @@@
 +name: Clippy Test (bors)
 +
 +on:
 +  push:
 +    branches:
 +      - auto
 +      - try
 +
 +env:
 +  RUST_BACKTRACE: 1
 +  CARGO_TARGET_DIR: '${{ github.workspace }}/target'
 +  NO_FMT_TEST: 1
 +  CARGO_INCREMENTAL: 0
++  CARGO_UNSTABLE_SPARSE_REGISTRY: true
 +
 +defaults:
 +  run:
 +    shell: bash
 +
 +jobs:
 +  changelog:
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
 +      with:
 +        github_token: "${{ secrets.github_token }}"
 +
 +    - name: Checkout
 +      uses: actions/checkout@v3.0.2
 +      with:
 +        ref: ${{ github.ref }}
 +
 +    # Run
 +    - name: Check Changelog
 +      run: |
 +        MESSAGE=$(git log --format=%B -n 1)
 +        PR=$(echo "$MESSAGE" | grep -o "#[0-9]*" | head -1 | sed -e 's/^#//')
 +        body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \
 +          python -c "import sys, json; print(json.load(sys.stdin)['body'])")
 +        output=$(grep "^changelog:\s*\S" <<< "$body" | sed "s/changelog:\s*//g") || {
 +          echo "ERROR: PR body must contain 'changelog: ...'"
 +          exit 1
 +        }
 +        if [[ "$output" = "none" ]]; then
 +          echo "WARNING: changelog is 'none'"
 +        else
 +          echo "changelog: $output"
 +        fi
 +      env:
 +        PYTHONIOENCODING: 'utf-8'
 +  base:
 +    needs: changelog
 +    strategy:
 +      matrix:
 +        os: [ubuntu-latest, windows-latest, macos-latest]
 +        host: [x86_64-unknown-linux-gnu, i686-unknown-linux-gnu, x86_64-apple-darwin, x86_64-pc-windows-msvc]
 +        exclude:
 +        - os: ubuntu-latest
 +          host: x86_64-apple-darwin
 +        - os: ubuntu-latest
 +          host: x86_64-pc-windows-msvc
 +        - os: macos-latest
 +          host: x86_64-unknown-linux-gnu
 +        - os: macos-latest
 +          host: i686-unknown-linux-gnu
 +        - os: macos-latest
 +          host: x86_64-pc-windows-msvc
 +        - os: windows-latest
 +          host: x86_64-unknown-linux-gnu
 +        - os: windows-latest
 +          host: i686-unknown-linux-gnu
 +        - os: windows-latest
 +          host: x86_64-apple-darwin
 +
 +    runs-on: ${{ matrix.os }}
 +
 +    # NOTE: If you modify this job, make sure you copy the changes to clippy.yml
 +    steps:
 +    # Setup
 +    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
 +      with:
 +        github_token: "${{ secrets.github_token }}"
 +
 +    - name: Install dependencies (Linux-i686)
 +      run: |
 +        sudo dpkg --add-architecture i386
 +        sudo apt-get update
 +        sudo apt-get install gcc-multilib libssl-dev:i386 libgit2-dev:i386
 +      if: matrix.host == 'i686-unknown-linux-gnu'
 +
 +    - name: Checkout
 +      uses: actions/checkout@v3.0.2
 +
 +    - name: Install toolchain
 +      run: rustup show active-toolchain
 +
 +    # Run
 +    - name: Set LD_LIBRARY_PATH (Linux)
 +      if: runner.os == 'Linux'
 +      run: |
 +        SYSROOT=$(rustc --print sysroot)
 +        echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV
 +    - name: Link rustc dylib (MacOS)
 +      if: runner.os == 'macOS'
 +      run: |
 +        SYSROOT=$(rustc --print sysroot)
 +        sudo mkdir -p /usr/local/lib
 +        sudo find "${SYSROOT}/lib" -maxdepth 1 -name '*dylib' -exec ln -s {} /usr/local/lib \;
 +    - name: Set PATH (Windows)
 +      if: runner.os == 'Windows'
 +      run: |
 +        SYSROOT=$(rustc --print sysroot)
 +        echo "$SYSROOT/bin" >> $GITHUB_PATH
 +
 +    - name: Build
 +      run: cargo build --features deny-warnings,internal
 +
 +    - name: Test
 +      if: runner.os == 'Linux'
 +      run: cargo test --features deny-warnings,internal
 +
 +    - name: Test
 +      if: runner.os != 'Linux'
 +      run: cargo test --features deny-warnings,internal -- --skip dogfood
 +
 +    - name: Test clippy_lints
 +      run: cargo test --features deny-warnings,internal
 +      working-directory: clippy_lints
 +
 +    - name: Test clippy_utils
 +      run: cargo test --features deny-warnings,internal
 +      working-directory: clippy_utils
 +
 +    - name: Test rustc_tools_util
 +      run: cargo test --features deny-warnings
 +      working-directory: rustc_tools_util
 +
 +    - name: Test clippy_dev
 +      run: cargo test --features deny-warnings
 +      working-directory: clippy_dev
 +
 +    - name: Test clippy-driver
 +      run: bash .github/driver.sh
 +      env:
 +        OS: ${{ runner.os }}
 +
 +  metadata_collection:
 +    needs: changelog
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +     # Setup
 +    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
 +      with:
 +        github_token: "${{ secrets.github_token }}"
 +
 +    - name: Checkout
 +      uses: actions/checkout@v3.0.2
 +
 +    - name: Install toolchain
 +      run: rustup show active-toolchain
 +
 +    - name: Test metadata collection
 +      run: cargo collect-metadata
 +
 +  integration_build:
 +    needs: changelog
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +    # Setup
 +    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
 +      with:
 +        github_token: "${{ secrets.github_token }}"
 +
 +    - name: Checkout
 +      uses: actions/checkout@v3.0.2
 +
 +    - name: Install toolchain
 +      run: rustup show active-toolchain
 +
 +    # Run
 +    - name: Build Integration Test
 +      run: cargo test --test integration --features integration --no-run
 +
 +    # Upload
 +    - name: Extract Binaries
 +      run: |
 +        DIR=$CARGO_TARGET_DIR/debug
 +        rm $DIR/deps/integration-*.d
 +        mv $DIR/deps/integration-* $DIR/integration
 +        find $DIR ! -executable -o -type d ! -path $DIR | xargs rm -rf
 +        rm -rf $CARGO_TARGET_DIR/release
 +
 +    - name: Upload Binaries
 +      uses: actions/upload-artifact@v1
 +      with:
 +        name: target
 +        path: target
 +
 +  integration:
 +    needs: integration_build
 +    strategy:
 +      fail-fast: false
 +      max-parallel: 6
 +      matrix:
 +        integration:
 +        - 'rust-lang/cargo'
 +        # FIXME: re-enable once fmt_macros is renamed in RLS
 +        # - 'rust-lang/rls'
 +        - 'rust-lang/chalk'
 +        - 'rust-lang/rustfmt'
 +        - 'Marwes/combine'
 +        - 'Geal/nom'
 +        - 'rust-lang/stdarch'
 +        - 'serde-rs/serde'
 +        # FIXME: chrono currently cannot be compiled with `--all-targets`
 +        # - 'chronotope/chrono'
 +        - 'hyperium/hyper'
 +        - 'rust-random/rand'
 +        - 'rust-lang/futures-rs'
 +        - 'rust-itertools/itertools'
 +        - 'rust-lang-nursery/failure'
 +        - 'rust-lang/log'
 +
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +    # Setup
 +    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
 +      with:
 +        github_token: "${{ secrets.github_token }}"
 +
 +    - name: Checkout
 +      uses: actions/checkout@v3.0.2
 +
 +    - name: Install toolchain
 +      run: rustup show active-toolchain
 +
 +    # Download
 +    - name: Download target dir
 +      uses: actions/download-artifact@v1
 +      with:
 +        name: target
 +        path: target
 +
 +    - name: Make Binaries Executable
 +      run: chmod +x $CARGO_TARGET_DIR/debug/*
 +
 +    # Run
 +    - name: Test ${{ matrix.integration }}
 +      run: |
 +        RUSTUP_TOOLCHAIN="$(rustup show active-toolchain | grep -o -E "nightly-[0-9]{4}-[0-9]{2}-[0-9]{2}")" \
 +          $CARGO_TARGET_DIR/debug/integration
 +      env:
 +        INTEGRATION: ${{ matrix.integration }}
 +
 +  # These jobs doesn't actually test anything, but they're only used to tell
 +  # bors the build completed, as there is no practical way to detect when a
 +  # workflow is successful listening to webhooks only.
 +  #
 +  # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
 +
 +  end-success:
 +    name: bors test finished
 +    if: github.event.pusher.name == 'bors' && success()
 +    runs-on: ubuntu-latest
 +    needs: [changelog, base, metadata_collection, integration_build, integration]
 +
 +    steps:
 +      - name: Mark the job as successful
 +        run: exit 0
 +
 +  end-failure:
 +    name: bors test finished
 +    if: github.event.pusher.name == 'bors' && (failure() || cancelled())
 +    runs-on: ubuntu-latest
 +    needs: [changelog, base, metadata_collection, integration_build, integration]
 +
 +    steps:
 +      - name: Mark the job as a failure
 +        run: exit 1
index 22051093c9cf960992c3a80923e71f72ef4414f1,0000000000000000000000000000000000000000..14f20212adda5a9dbbd3728d343b9f127d2b44ad
mode 100644,000000..100644
--- /dev/null
@@@ -1,70 -1,0 +1,72 @@@
 +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
++  CARGO_INCREMENTAL: 0
++  CARGO_UNSTABLE_SPARSE_REGISTRY: true
 +
 +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 42615179f7053073fce07a19319a24216b159865,0000000000000000000000000000000000000000..2d7bda27e4fcccf4773d21d4e016bb728f30044d
mode 100644,000000..100644
--- /dev/null
@@@ -1,4361 -1,0 +1,4367 @@@
 +# Changelog
 +
 +All notable changes to this project will be documented in this file.
 +See [Changelog Update](book/src/development/infrastructure/changelog_update.md) if you want to update this
 +document.
 +
 +## Unreleased / In Rust Nightly
 +
 +[3c7e7dbc...master](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...master)
 +
 +## Rust 1.64
 +
 +Current stable, released 2022-09-22
 +
 +[d7b5cbf0...3c7e7dbc](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...3c7e7dbc)
 +
 +### New Lints
 +
 +* [`arithmetic_side_effects`]
 +  [#9130](https://github.com/rust-lang/rust-clippy/pull/9130)
 +* [`invalid_utf8_in_unchecked`]
 +  [#9105](https://github.com/rust-lang/rust-clippy/pull/9105)
 +* [`assertions_on_result_states`]
 +  [#9225](https://github.com/rust-lang/rust-clippy/pull/9225)
 +* [`manual_find`]
 +  [#8649](https://github.com/rust-lang/rust-clippy/pull/8649)
 +* [`manual_retain`]
 +  [#8972](https://github.com/rust-lang/rust-clippy/pull/8972)
 +* [`default_instead_of_iter_empty`]
 +  [#8989](https://github.com/rust-lang/rust-clippy/pull/8989)
 +* [`manual_rem_euclid`]
 +  [#9031](https://github.com/rust-lang/rust-clippy/pull/9031)
 +* [`obfuscated_if_else`]
 +  [#9148](https://github.com/rust-lang/rust-clippy/pull/9148)
 +* [`std_instead_of_core`]
 +  [#9103](https://github.com/rust-lang/rust-clippy/pull/9103)
 +* [`std_instead_of_alloc`]
 +  [#9103](https://github.com/rust-lang/rust-clippy/pull/9103)
 +* [`alloc_instead_of_core`]
 +  [#9103](https://github.com/rust-lang/rust-clippy/pull/9103)
 +* [`explicit_auto_deref`]
 +  [#8355](https://github.com/rust-lang/rust-clippy/pull/8355)
 +
 +
 +### Moves and Deprecations
 +
 +* Moved [`format_push_string`] to `restriction` (now allow-by-default)
 +  [#9161](https://github.com/rust-lang/rust-clippy/pull/9161)
 +
 +### Enhancements
 +
 +* [`significant_drop_in_scrutinee`]: Now gives more context in the lint message
 +  [#8981](https://github.com/rust-lang/rust-clippy/pull/8981)
 +* [`single_match`], [`single_match_else`]: Now catches more `Option` cases
 +  [#8985](https://github.com/rust-lang/rust-clippy/pull/8985)
 +* [`unused_async`]: Now works for async methods
 +  [#9025](https://github.com/rust-lang/rust-clippy/pull/9025)
 +* [`manual_filter_map`], [`manual_find_map`]: Now lint more expressions
 +  [#8958](https://github.com/rust-lang/rust-clippy/pull/8958)
 +* [`question_mark`]: Now works for simple `if let` expressions
 +  [#8356](https://github.com/rust-lang/rust-clippy/pull/8356)
 +* [`undocumented_unsafe_blocks`]: Now finds comments before the start of closures
 +  [#9117](https://github.com/rust-lang/rust-clippy/pull/9117)
 +* [`trait_duplication_in_bounds`]: Now catches duplicate bounds in where clauses
 +  [#8703](https://github.com/rust-lang/rust-clippy/pull/8703)
 +* [`shadow_reuse`], [`shadow_same`], [`shadow_unrelated`]: Now lint in const blocks
 +  [#9124](https://github.com/rust-lang/rust-clippy/pull/9124)
 +* [`slow_vector_initialization`]: Now detects cases with `vec.capacity()`
 +  [#8953](https://github.com/rust-lang/rust-clippy/pull/8953)
 +* [`unused_self`]: Now respects the `avoid-breaking-exported-api` config option
 +  [#9199](https://github.com/rust-lang/rust-clippy/pull/9199)
 +* [`box_collection`]: Now supports all std collections
 +  [#9170](https://github.com/rust-lang/rust-clippy/pull/9170)
 +
 +### False Positive Fixes
 +
 +* [`significant_drop_in_scrutinee`]: Now ignores calls to `IntoIterator::into_iter`
 +  [#9140](https://github.com/rust-lang/rust-clippy/pull/9140)
 +* [`while_let_loop`]: Now ignores cases when the significant drop order would change
 +  [#8981](https://github.com/rust-lang/rust-clippy/pull/8981)
 +* [`branches_sharing_code`]: Now ignores cases where moved variables have a significant
 +  drop or variable modifications can affect the conditions
 +  [#9138](https://github.com/rust-lang/rust-clippy/pull/9138)
 +* [`let_underscore_lock`]: Now ignores bindings that aren't locked
 +  [#8990](https://github.com/rust-lang/rust-clippy/pull/8990)
 +* [`trivially_copy_pass_by_ref`]: Now tracks lifetimes and ignores cases where unsafe
 +  pointers are used
 +  [#8639](https://github.com/rust-lang/rust-clippy/pull/8639)
 +* [`let_unit_value`]: No longer ignores `#[allow]` attributes on the value
 +  [#9082](https://github.com/rust-lang/rust-clippy/pull/9082)
 +* [`declare_interior_mutable_const`]: Now ignores the `thread_local!` macro
 +  [#9015](https://github.com/rust-lang/rust-clippy/pull/9015)
 +* [`if_same_then_else`]: Now ignores branches with `todo!` and `unimplemented!`
 +  [#9006](https://github.com/rust-lang/rust-clippy/pull/9006)
 +* [`enum_variant_names`]: Now ignores names with `_` prefixes
 +  [#9032](https://github.com/rust-lang/rust-clippy/pull/9032)
 +* [`let_unit_value`]: Now ignores cases, where the unit type is manually specified
 +  [#9056](https://github.com/rust-lang/rust-clippy/pull/9056)
 +* [`match_same_arms`]: Now ignores branches with `todo!`
 +  [#9207](https://github.com/rust-lang/rust-clippy/pull/9207)
 +* [`assign_op_pattern`]: Ignores cases that break borrowing rules
 +  [#9214](https://github.com/rust-lang/rust-clippy/pull/9214)
 +* [`extra_unused_lifetimes`]: No longer triggers in derive macros
 +  [#9037](https://github.com/rust-lang/rust-clippy/pull/9037)
 +* [`mismatching_type_param_order`]: Now ignores complicated generic parameters
 +  [#9146](https://github.com/rust-lang/rust-clippy/pull/9146)
 +* [`equatable_if_let`]: No longer lints in macros
 +  [#9074](https://github.com/rust-lang/rust-clippy/pull/9074)
 +* [`new_without_default`]: Now ignores generics and lifetime parameters on `fn new`
 +  [#9115](https://github.com/rust-lang/rust-clippy/pull/9115)
 +* [`needless_borrow`]: Now ignores cases that result in the execution of different traits
 +  [#9096](https://github.com/rust-lang/rust-clippy/pull/9096)
 +* [`declare_interior_mutable_const`]: No longer triggers in thread-local initializers
 +  [#9246](https://github.com/rust-lang/rust-clippy/pull/9246)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`type_repetition_in_bounds`]: The suggestion now works with maybe bounds
 +  [#9132](https://github.com/rust-lang/rust-clippy/pull/9132)
 +* [`transmute_ptr_to_ref`]: Now suggests `pointer::cast` when possible
 +  [#8939](https://github.com/rust-lang/rust-clippy/pull/8939)
 +* [`useless_format`]: Now suggests the correct variable name
 +  [#9237](https://github.com/rust-lang/rust-clippy/pull/9237)
 +* [`or_fun_call`]: The lint emission will now only span over the `unwrap_or` call
 +  [#9144](https://github.com/rust-lang/rust-clippy/pull/9144)
 +* [`neg_multiply`]: Now suggests adding parentheses around suggestion if needed
 +  [#9026](https://github.com/rust-lang/rust-clippy/pull/9026)
 +* [`unnecessary_lazy_evaluations`]: Now suggest for `bool::then_some` for lazy evaluation
 +  [#9099](https://github.com/rust-lang/rust-clippy/pull/9099)
 +* [`manual_flatten`]: Improved message for long code snippets
 +  [#9156](https://github.com/rust-lang/rust-clippy/pull/9156)
 +* [`explicit_counter_loop`]: The suggestion is now machine applicable
 +  [#9149](https://github.com/rust-lang/rust-clippy/pull/9149)
 +* [`needless_borrow`]: Now keeps parentheses around fields, when needed
 +  [#9210](https://github.com/rust-lang/rust-clippy/pull/9210)
 +* [`while_let_on_iterator`]: The suggestion now works in `FnOnce` closures
 +  [#9134](https://github.com/rust-lang/rust-clippy/pull/9134)
 +
 +### ICE Fixes
 +
 +* Fix ICEs related to `#![feature(generic_const_exprs)]` usage
 +  [#9241](https://github.com/rust-lang/rust-clippy/pull/9241)
 +* Fix ICEs related to reference lints
 +  [#9093](https://github.com/rust-lang/rust-clippy/pull/9093)
 +* [`question_mark`]: Fix ICE on zero field tuple structs
 +  [#9244](https://github.com/rust-lang/rust-clippy/pull/9244)
 +
 +### Documentation Improvements
 +
 +* [`needless_option_take`]: Now includes a "What it does" and "Why is this bad?" section.
 +  [#9022](https://github.com/rust-lang/rust-clippy/pull/9022)
 +
 +### Others
 +
 +* Using `--cap-lints=allow` and only `--force-warn`ing some will now work with Clippy's driver
 +  [#9036](https://github.com/rust-lang/rust-clippy/pull/9036)
 +* Clippy now tries to read the `rust-version` from `Cargo.toml` to identify the
 +  minimum supported rust version
 +  [#8774](https://github.com/rust-lang/rust-clippy/pull/8774)
 +
 +## Rust 1.63
 +
 +Released 2022-08-11
 +
 +[7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0)
 +
 +### New Lints
 +
 +* [`borrow_deref_ref`]
 +  [#7930](https://github.com/rust-lang/rust-clippy/pull/7930)
 +* [`doc_link_with_quotes`]
 +  [#8385](https://github.com/rust-lang/rust-clippy/pull/8385)
 +* [`no_effect_replace`]
 +  [#8754](https://github.com/rust-lang/rust-clippy/pull/8754)
 +* [`rc_clone_in_vec_init`]
 +  [#8769](https://github.com/rust-lang/rust-clippy/pull/8769)
 +* [`derive_partial_eq_without_eq`]
 +  [#8796](https://github.com/rust-lang/rust-clippy/pull/8796)
 +* [`mismatching_type_param_order`]
 +  [#8831](https://github.com/rust-lang/rust-clippy/pull/8831)
 +* [`duplicate_mod`] [#8832](https://github.com/rust-lang/rust-clippy/pull/8832)
 +* [`unused_rounding`]
 +  [#8866](https://github.com/rust-lang/rust-clippy/pull/8866)
 +* [`get_first`] [#8882](https://github.com/rust-lang/rust-clippy/pull/8882)
 +* [`swap_ptr_to_ref`]
 +  [#8916](https://github.com/rust-lang/rust-clippy/pull/8916)
 +* [`almost_complete_letter_range`]
 +  [#8918](https://github.com/rust-lang/rust-clippy/pull/8918)
 +* [`needless_parens_on_range_literals`]
 +  [#8933](https://github.com/rust-lang/rust-clippy/pull/8933)
 +* [`as_underscore`] [#8934](https://github.com/rust-lang/rust-clippy/pull/8934)
 +
 +### Moves and Deprecations
 +
 +* Rename `eval_order_dependence` to [`mixed_read_write_in_expression`], move to
 +  `nursery` [#8621](https://github.com/rust-lang/rust-clippy/pull/8621)
 +
 +### Enhancements
 +
 +* [`undocumented_unsafe_blocks`]: Now also lints on unsafe trait implementations
 +  [#8761](https://github.com/rust-lang/rust-clippy/pull/8761)
 +* [`empty_line_after_outer_attr`]: Now also lints on argumentless macros
 +  [#8790](https://github.com/rust-lang/rust-clippy/pull/8790)
 +* [`expect_used`]: Now can be disabled in tests with the `allow-expect-in-tests`
 +  option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
 +* [`unwrap_used`]: Now can be disabled in tests with the `allow-unwrap-in-tests`
 +  option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
 +* [`disallowed_methods`]: Now also lints indirect usages
 +  [#8852](https://github.com/rust-lang/rust-clippy/pull/8852)
 +* [`get_last_with_len`]: Now also lints `VecDeque` and any deref to slice
 +  [#8862](https://github.com/rust-lang/rust-clippy/pull/8862)
 +* [`manual_range_contains`]: Now also lints on chains of `&&` and `||`
 +  [#8884](https://github.com/rust-lang/rust-clippy/pull/8884)
 +* [`rc_clone_in_vec_init`]: Now also lints on `Weak`
 +  [#8885](https://github.com/rust-lang/rust-clippy/pull/8885)
 +* [`dbg_macro`]: Introduce `allow-dbg-in-tests` config option
 +  [#8897](https://github.com/rust-lang/rust-clippy/pull/8897)
 +* [`use_self`]: Now also lints on `TupleStruct` and `Struct` patterns
 +  [#8899](https://github.com/rust-lang/rust-clippy/pull/8899)
 +* [`manual_find_map`] and [`manual_filter_map`]: Now also lints on more complex
 +  method chains inside `map`
 +  [#8930](https://github.com/rust-lang/rust-clippy/pull/8930)
 +* [`needless_return`]: Now also lints on macro expressions in return statements
 +  [#8932](https://github.com/rust-lang/rust-clippy/pull/8932)
 +* [`doc_markdown`]: Users can now indicate, that the `doc-valid-idents` config
 +  should extend the default and not replace it
 +  [#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
 +* [`disallowed_names`]: Users can now indicate, that the `disallowed-names`
 +  config should extend the default and not replace it
 +  [#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
 +* [`never_loop`]: Now checks for `continue` in struct expression
 +  [#9002](https://github.com/rust-lang/rust-clippy/pull/9002)
 +
 +### False Positive Fixes
 +
 +* [`useless_transmute`]: No longer lints on types with erased regions
 +  [#8564](https://github.com/rust-lang/rust-clippy/pull/8564)
 +* [`vec_init_then_push`]: No longer lints when further extended
 +  [#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
 +* [`cmp_owned`]: No longer lints on `From::from` for `Copy` types
 +  [#8807](https://github.com/rust-lang/rust-clippy/pull/8807)
 +* [`redundant_allocation`]: No longer lints on fat pointers that would become
 +  thin pointers [#8813](https://github.com/rust-lang/rust-clippy/pull/8813)
 +* [`derive_partial_eq_without_eq`]:
 +    * Handle differing predicates applied by `#[derive(PartialEq)]` and
 +      `#[derive(Eq)]`
 +      [#8869](https://github.com/rust-lang/rust-clippy/pull/8869)
 +    * No longer lints on non-public types and better handles generics
 +      [#8950](https://github.com/rust-lang/rust-clippy/pull/8950)
 +* [`empty_line_after_outer_attr`]: No longer lints empty lines in inner
 +  string values [#8892](https://github.com/rust-lang/rust-clippy/pull/8892)
 +* [`branches_sharing_code`]: No longer lints when using different binding names
 +  [#8901](https://github.com/rust-lang/rust-clippy/pull/8901)
 +* [`significant_drop_in_scrutinee`]: No longer lints on Try `?` and `await`
 +  desugared expressions [#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
 +* [`checked_conversions`]: No longer lints in `const` contexts
 +  [#8907](https://github.com/rust-lang/rust-clippy/pull/8907)
 +* [`iter_overeager_cloned`]: No longer lints on `.cloned().flatten()` when
 +  `T::Item` doesn't implement `IntoIterator`
 +  [#8960](https://github.com/rust-lang/rust-clippy/pull/8960)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`vec_init_then_push`]: Suggest to remove `mut` binding when possible
 +  [#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
 +* [`manual_range_contains`]: Fix suggestion for integers with different signs
 +  [#8763](https://github.com/rust-lang/rust-clippy/pull/8763)
 +* [`identity_op`]: Add parenthesis to suggestions where required
 +  [#8786](https://github.com/rust-lang/rust-clippy/pull/8786)
 +* [`cast_lossless`]: No longer gives wrong suggestion on `usize`/`isize`->`f64`
 +  [#8778](https://github.com/rust-lang/rust-clippy/pull/8778)
 +* [`rc_clone_in_vec_init`]: Add suggestion
 +  [#8814](https://github.com/rust-lang/rust-clippy/pull/8814)
 +* The "unknown field" error messages for config files now wraps the field names
 +  [#8823](https://github.com/rust-lang/rust-clippy/pull/8823)
 +* [`cast_abs_to_unsigned`]: Do not remove cast if it's required
 +  [#8876](https://github.com/rust-lang/rust-clippy/pull/8876)
 +* [`significant_drop_in_scrutinee`]: Improve lint message for types that are not
 +  references and not trivially clone-able
 +  [#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
 +* [`for_loops_over_fallibles`]: Now suggests the correct variant of `iter()`,
 +  `iter_mut()` or `into_iter()`
 +  [#8941](https://github.com/rust-lang/rust-clippy/pull/8941)
 +
 +### ICE Fixes
 +
 +* Fix ICE in [`let_unit_value`] when calling a `static`/`const` callable type
 +  [#8835](https://github.com/rust-lang/rust-clippy/pull/8835)
 +* Fix ICEs on callable `static`/`const`s
 +  [#8896](https://github.com/rust-lang/rust-clippy/pull/8896)
 +* [`needless_late_init`]
 +  [#8912](https://github.com/rust-lang/rust-clippy/pull/8912)
 +* Fix ICE in shadow lints
 +  [#8913](https://github.com/rust-lang/rust-clippy/pull/8913)
 +
 +### Documentation Improvements
 +
 +* Clippy has a [Book](https://doc.rust-lang.org/nightly/clippy/) now!
 +  [#7359](https://github.com/rust-lang/rust-clippy/pull/7359)
 +* Add a *copy lint name*-button to Clippy's lint list
 +  [#8839](https://github.com/rust-lang/rust-clippy/pull/8839)
 +* Display past names of renamed lints on Clippy's lint list
 +  [#8843](https://github.com/rust-lang/rust-clippy/pull/8843)
 +* Add the ability to show the lint output in the lint list
 +  [#8947](https://github.com/rust-lang/rust-clippy/pull/8947)
 +
 +## Rust 1.62
 +
 +Released 2022-06-30
 +
 +[d0cf3481...7c21f91b](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...7c21f91b)
 +
 +### New Lints
 +
 +* [`large_include_file`]
 +  [#8727](https://github.com/rust-lang/rust-clippy/pull/8727)
 +* [`cast_abs_to_unsigned`]
 +  [#8635](https://github.com/rust-lang/rust-clippy/pull/8635)
 +* [`err_expect`]
 +  [#8606](https://github.com/rust-lang/rust-clippy/pull/8606)
 +* [`unnecessary_owned_empty_strings`]
 +  [#8660](https://github.com/rust-lang/rust-clippy/pull/8660)
 +* [`empty_structs_with_brackets`]
 +  [#8594](https://github.com/rust-lang/rust-clippy/pull/8594)
 +* [`crate_in_macro_def`]
 +  [#8576](https://github.com/rust-lang/rust-clippy/pull/8576)
 +* [`needless_option_take`]
 +  [#8665](https://github.com/rust-lang/rust-clippy/pull/8665)
 +* [`bytes_count_to_len`]
 +  [#8711](https://github.com/rust-lang/rust-clippy/pull/8711)
 +* [`is_digit_ascii_radix`]
 +  [#8624](https://github.com/rust-lang/rust-clippy/pull/8624)
 +* [`await_holding_invalid_type`]
 +  [#8707](https://github.com/rust-lang/rust-clippy/pull/8707)
 +* [`trim_split_whitespace`]
 +  [#8575](https://github.com/rust-lang/rust-clippy/pull/8575)
 +* [`pub_use`]
 +  [#8670](https://github.com/rust-lang/rust-clippy/pull/8670)
 +* [`format_push_string`]
 +  [#8626](https://github.com/rust-lang/rust-clippy/pull/8626)
 +* [`empty_drop`]
 +  [#8571](https://github.com/rust-lang/rust-clippy/pull/8571)
 +* [`drop_non_drop`]
 +  [#8630](https://github.com/rust-lang/rust-clippy/pull/8630)
 +* [`forget_non_drop`]
 +  [#8630](https://github.com/rust-lang/rust-clippy/pull/8630)
 +
 +### Moves and Deprecations
 +
 +* Move [`only_used_in_recursion`] to `nursery` (now allow-by-default)
 +  [#8783](https://github.com/rust-lang/rust-clippy/pull/8783)
 +* Move [`stable_sort_primitive`] to `pedantic` (now allow-by-default)
 +  [#8716](https://github.com/rust-lang/rust-clippy/pull/8716)
 +
 +### Enhancements
 +
 +* Remove overlap between [`manual_split_once`] and [`needless_splitn`]
 +  [#8631](https://github.com/rust-lang/rust-clippy/pull/8631)
 +* [`map_identity`]: Now checks for needless `map_err`
 +  [#8487](https://github.com/rust-lang/rust-clippy/pull/8487)
 +* [`extra_unused_lifetimes`]: Now checks for impl lifetimes
 +  [#8737](https://github.com/rust-lang/rust-clippy/pull/8737)
 +* [`cast_possible_truncation`]: Now catches more cases with larger shift or divide operations
 +  [#8687](https://github.com/rust-lang/rust-clippy/pull/8687)
 +* [`identity_op`]: Now checks for modulo expressions
 +  [#8519](https://github.com/rust-lang/rust-clippy/pull/8519)
 +* [`panic`]: No longer lint in constant context
 +  [#8592](https://github.com/rust-lang/rust-clippy/pull/8592)
 +* [`manual_split_once`]: Now lints manual iteration of `splitn`
 +  [#8717](https://github.com/rust-lang/rust-clippy/pull/8717)
 +* [`self_named_module_files`], [`mod_module_files`]: Now handle relative module paths
 +  [#8611](https://github.com/rust-lang/rust-clippy/pull/8611)
 +* [`unsound_collection_transmute`]: Now has better size and alignment checks
 +  [#8648](https://github.com/rust-lang/rust-clippy/pull/8648)
 +* [`unnested_or_patterns`]: Ignore cases, where the suggestion would be longer
 +  [#8619](https://github.com/rust-lang/rust-clippy/pull/8619)
 +
 +### False Positive Fixes
 +
 +* [`rest_pat_in_fully_bound_structs`]: Now ignores structs marked with `#[non_exhaustive]`
 +  [#8690](https://github.com/rust-lang/rust-clippy/pull/8690)
 +* [`needless_late_init`]: No longer lints `if let` statements, `let mut` bindings or instances that
 +  changes the drop order significantly
 +  [#8617](https://github.com/rust-lang/rust-clippy/pull/8617)
 +* [`unnecessary_cast`]: No longer lints to casts to aliased or non-primitive types
 +  [#8596](https://github.com/rust-lang/rust-clippy/pull/8596)
 +* [`init_numbered_fields`]: No longer lints type aliases
 +  [#8780](https://github.com/rust-lang/rust-clippy/pull/8780)
 +* [`needless_option_as_deref`]: No longer lints for `as_deref_mut` on `Option` values that can't be moved
 +  [#8646](https://github.com/rust-lang/rust-clippy/pull/8646)
 +* [`mistyped_literal_suffixes`]: Now ignores float literals without an exponent
 +  [#8742](https://github.com/rust-lang/rust-clippy/pull/8742)
 +* [`undocumented_unsafe_blocks`]: Now ignores unsafe blocks from proc-macros and works better for sub-expressions
 +  [#8450](https://github.com/rust-lang/rust-clippy/pull/8450)
 +* [`same_functions_in_if_condition`]: Now allows different constants, even if they have the same value
 +  [#8673](https://github.com/rust-lang/rust-clippy/pull/8673)
 +* [`needless_match`]: Now checks for more complex types and ignores type coercion
 +  [#8549](https://github.com/rust-lang/rust-clippy/pull/8549)
 +* [`assertions_on_constants`]: Now ignores constants from `cfg!` macros
 +  [#8614](https://github.com/rust-lang/rust-clippy/pull/8614)
 +* [`indexing_slicing`]: Fix false positives with constant indices in
 +  [#8588](https://github.com/rust-lang/rust-clippy/pull/8588)
 +* [`iter_with_drain`]: Now ignores iterator references
 +  [#8668](https://github.com/rust-lang/rust-clippy/pull/8668)
 +* [`useless_attribute`]: Now allows [`redundant_pub_crate`] on `use` items
 +  [#8743](https://github.com/rust-lang/rust-clippy/pull/8743)
 +* [`cast_ptr_alignment`]: Now ignores expressions, when used for unaligned reads and writes
 +  [#8632](https://github.com/rust-lang/rust-clippy/pull/8632)
 +* [`wrong_self_convention`]: Now allows `&mut self` and no self as arguments for `is_*` methods
 +  [#8738](https://github.com/rust-lang/rust-clippy/pull/8738)
 +* [`mut_from_ref`]: Only lint in unsafe code
 +  [#8647](https://github.com/rust-lang/rust-clippy/pull/8647)
 +* [`redundant_pub_crate`]: Now allows macro exports
 +  [#8736](https://github.com/rust-lang/rust-clippy/pull/8736)
 +* [`needless_match`]: Ignores cases where the else block expression is different
 +  [#8700](https://github.com/rust-lang/rust-clippy/pull/8700)
 +* [`transmute_int_to_char`]: Now allows transmutations in `const` code
 +  [#8610](https://github.com/rust-lang/rust-clippy/pull/8610)
 +* [`manual_non_exhaustive`]: Ignores cases, where the enum value is used
 +  [#8645](https://github.com/rust-lang/rust-clippy/pull/8645)
 +* [`redundant_closure`]: Now ignores coerced closure
 +  [#8431](https://github.com/rust-lang/rust-clippy/pull/8431)
 +* [`identity_op`]: Is now ignored in cases where extra brackets would be needed
 +  [#8730](https://github.com/rust-lang/rust-clippy/pull/8730)
 +* [`let_unit_value`]: Now ignores cases which are used for type inference
 +  [#8563](https://github.com/rust-lang/rust-clippy/pull/8563)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`manual_split_once`]: Fixed incorrect suggestions for single result accesses
 +  [#8631](https://github.com/rust-lang/rust-clippy/pull/8631)
 +* [`bytes_nth`]: Fix typos in the diagnostic message
 +  [#8403](https://github.com/rust-lang/rust-clippy/pull/8403)
 +* [`mistyped_literal_suffixes`]: Now suggests the correct integer types
 +  [#8742](https://github.com/rust-lang/rust-clippy/pull/8742)
 +* [`unnecessary_to_owned`]: Fixed suggestion based on the configured msrv
 +  [#8692](https://github.com/rust-lang/rust-clippy/pull/8692)
 +* [`single_element_loop`]: Improve lint for Edition 2021 arrays
 +  [#8616](https://github.com/rust-lang/rust-clippy/pull/8616)
 +* [`manual_bits`]: Now includes a cast for proper type conversion, when needed
 +  [#8677](https://github.com/rust-lang/rust-clippy/pull/8677)
 +* [`option_map_unit_fn`], [`result_map_unit_fn`]: Fix some incorrect suggestions
 +  [#8584](https://github.com/rust-lang/rust-clippy/pull/8584)
 +* [`collapsible_else_if`]: Add whitespace in suggestion
 +  [#8729](https://github.com/rust-lang/rust-clippy/pull/8729)
 +* [`transmute_bytes_to_str`]: Now suggest `from_utf8_unchecked` in `const` context
 +  [#8612](https://github.com/rust-lang/rust-clippy/pull/8612)
 +* [`map_clone`]: Improve message and suggestion based on the msrv
 +  [#8688](https://github.com/rust-lang/rust-clippy/pull/8688)
 +* [`needless_late_init`]: Now shows the `let` statement where it was first initialized
 +  [#8779](https://github.com/rust-lang/rust-clippy/pull/8779)
 +
 +### ICE Fixes
 +
 +* [`only_used_in_recursion`]
 +  [#8691](https://github.com/rust-lang/rust-clippy/pull/8691)
 +* [`cast_slice_different_sizes`]
 +  [#8720](https://github.com/rust-lang/rust-clippy/pull/8720)
 +* [`iter_overeager_cloned`]
 +  [#8602](https://github.com/rust-lang/rust-clippy/pull/8602)
 +* [`undocumented_unsafe_blocks`]
 +  [#8686](https://github.com/rust-lang/rust-clippy/pull/8686)
 +
 +## Rust 1.61
 +
 +Released 2022-05-19
 +
 +[57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481)
 +
 +### New Lints
 +
 +* [`only_used_in_recursion`]
 +  [#8422](https://github.com/rust-lang/rust-clippy/pull/8422)
 +* [`cast_enum_truncation`]
 +  [#8381](https://github.com/rust-lang/rust-clippy/pull/8381)
 +* [`missing_spin_loop`]
 +  [#8174](https://github.com/rust-lang/rust-clippy/pull/8174)
 +* [`deref_by_slicing`]
 +  [#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
 +* [`needless_match`]
 +  [#8471](https://github.com/rust-lang/rust-clippy/pull/8471)
 +* [`allow_attributes_without_reason`] (Requires `#![feature(lint_reasons)]`)
 +  [#8504](https://github.com/rust-lang/rust-clippy/pull/8504)
 +* [`print_in_format_impl`]
 +  [#8253](https://github.com/rust-lang/rust-clippy/pull/8253)
 +* [`unnecessary_find_map`]
 +  [#8489](https://github.com/rust-lang/rust-clippy/pull/8489)
 +* [`or_then_unwrap`]
 +  [#8561](https://github.com/rust-lang/rust-clippy/pull/8561)
 +* [`unnecessary_join`]
 +  [#8579](https://github.com/rust-lang/rust-clippy/pull/8579)
 +* [`iter_with_drain`]
 +  [#8483](https://github.com/rust-lang/rust-clippy/pull/8483)
 +* [`cast_enum_constructor`]
 +  [#8562](https://github.com/rust-lang/rust-clippy/pull/8562)
 +* [`cast_slice_different_sizes`]
 +  [#8445](https://github.com/rust-lang/rust-clippy/pull/8445)
 +
 +### Moves and Deprecations
 +
 +* Moved [`transmute_undefined_repr`] to `nursery` (now allow-by-default)
 +  [#8432](https://github.com/rust-lang/rust-clippy/pull/8432)
 +* Moved [`try_err`] to `restriction`
 +  [#8544](https://github.com/rust-lang/rust-clippy/pull/8544)
 +* Move [`iter_with_drain`] to `nursery`
 +  [#8541](https://github.com/rust-lang/rust-clippy/pull/8541)
 +* Renamed `to_string_in_display` to [`recursive_format_impl`]
 +  [#8188](https://github.com/rust-lang/rust-clippy/pull/8188)
 +
 +### Enhancements
 +
 +* [`dbg_macro`]: The lint level can now be set with crate attributes and works inside macros
 +  [#8411](https://github.com/rust-lang/rust-clippy/pull/8411)
 +* [`ptr_as_ptr`]: Now works inside macros
 +  [#8442](https://github.com/rust-lang/rust-clippy/pull/8442)
 +* [`use_self`]: Now works for variants in match expressions
 +  [#8456](https://github.com/rust-lang/rust-clippy/pull/8456)
 +* [`await_holding_lock`]: Now lints for `parking_lot::{Mutex, RwLock}`
 +  [#8419](https://github.com/rust-lang/rust-clippy/pull/8419)
 +* [`recursive_format_impl`]: Now checks for format calls on `self`
 +  [#8188](https://github.com/rust-lang/rust-clippy/pull/8188)
 +
 +### False Positive Fixes
 +
 +* [`new_without_default`]: No longer lints for `new()` methods with `#[doc(hidden)]`
 +  [#8472](https://github.com/rust-lang/rust-clippy/pull/8472)
 +* [`transmute_undefined_repr`]: No longer lints for single field structs with `#[repr(C)]`,
 +  generic parameters, wide pointers, unions, tuples and allow several forms of type erasure
 +  [#8425](https://github.com/rust-lang/rust-clippy/pull/8425)
 +  [#8553](https://github.com/rust-lang/rust-clippy/pull/8553)
 +  [#8440](https://github.com/rust-lang/rust-clippy/pull/8440)
 +  [#8547](https://github.com/rust-lang/rust-clippy/pull/8547)
 +* [`match_single_binding`], [`match_same_arms`], [`match_as_ref`], [`match_bool`]: No longer
 +  lint `match` expressions with `cfg`ed arms
 +  [#8443](https://github.com/rust-lang/rust-clippy/pull/8443)
 +* [`single_component_path_imports`]: No longer lint on macros
 +  [#8537](https://github.com/rust-lang/rust-clippy/pull/8537)
 +* [`ptr_arg`]: Allow `&mut` arguments for `Cow<_>`
 +  [#8552](https://github.com/rust-lang/rust-clippy/pull/8552)
 +* [`needless_borrow`]: No longer lints for method calls
 +  [#8441](https://github.com/rust-lang/rust-clippy/pull/8441)
 +* [`match_same_arms`]: Now ensures that interposing arm patterns don't overlap
 +  [#8232](https://github.com/rust-lang/rust-clippy/pull/8232)
 +* [`default_trait_access`]: Now allows `Default::default` in update expressions
 +  [#8433](https://github.com/rust-lang/rust-clippy/pull/8433)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`redundant_slicing`]: Fixed suggestion for a method calls
 +  [#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
 +* [`map_flatten`]: Long suggestions will now be split up into two help messages
 +  [#8520](https://github.com/rust-lang/rust-clippy/pull/8520)
 +* [`unnecessary_lazy_evaluations`]: Now shows suggestions for longer code snippets
 +  [#8543](https://github.com/rust-lang/rust-clippy/pull/8543)
 +* [`unnecessary_sort_by`]: Now suggests `Reverse` including the path
 +  [#8462](https://github.com/rust-lang/rust-clippy/pull/8462)
 +* [`search_is_some`]: More suggestions are now `MachineApplicable`
 +  [#8536](https://github.com/rust-lang/rust-clippy/pull/8536)
 +
 +### Documentation Improvements
 +
 +* [`new_without_default`]: Document `pub` requirement for the struct and fields
 +  [#8429](https://github.com/rust-lang/rust-clippy/pull/8429)
 +
 +## Rust 1.60
 +
 +Released 2022-04-07
 +
 +[0eff589...57b3c4b](https://github.com/rust-lang/rust-clippy/compare/0eff589...57b3c4b)
 +
 +### New Lints
 +
 +* [`single_char_lifetime_names`]
 +  [#8236](https://github.com/rust-lang/rust-clippy/pull/8236)
 +* [`iter_overeager_cloned`]
 +  [#8203](https://github.com/rust-lang/rust-clippy/pull/8203)
 +* [`transmute_undefined_repr`]
 +  [#8398](https://github.com/rust-lang/rust-clippy/pull/8398)
 +* [`default_union_representation`]
 +  [#8289](https://github.com/rust-lang/rust-clippy/pull/8289)
 +* [`manual_bits`]
 +  [#8213](https://github.com/rust-lang/rust-clippy/pull/8213)
 +* [`borrow_as_ptr`]
 +  [#8210](https://github.com/rust-lang/rust-clippy/pull/8210)
 +
 +### Moves and Deprecations
 +
 +* Moved [`disallowed_methods`] and [`disallowed_types`] to `style` (now warn-by-default)
 +  [#8261](https://github.com/rust-lang/rust-clippy/pull/8261)
 +* Rename `ref_in_deref` to [`needless_borrow`]
 +  [#8217](https://github.com/rust-lang/rust-clippy/pull/8217)
 +* Moved [`mutex_atomic`] to `nursery` (now allow-by-default)
 +  [#8260](https://github.com/rust-lang/rust-clippy/pull/8260)
 +
 +### Enhancements
 +
 +* [`ptr_arg`]: Now takes the argument usage into account and lints for mutable references
 +  [#8271](https://github.com/rust-lang/rust-clippy/pull/8271)
 +* [`unused_io_amount`]: Now supports async read and write traits
 +  [#8179](https://github.com/rust-lang/rust-clippy/pull/8179)
 +* [`while_let_on_iterator`]: Improved detection to catch more cases
 +  [#8221](https://github.com/rust-lang/rust-clippy/pull/8221)
 +* [`trait_duplication_in_bounds`]: Now covers trait functions with `Self` bounds
 +  [#8252](https://github.com/rust-lang/rust-clippy/pull/8252)
 +* [`unwrap_used`]: Now works for `.get(i).unwrap()` and `.get_mut(i).unwrap()`
 +  [#8372](https://github.com/rust-lang/rust-clippy/pull/8372)
 +* [`map_clone`]: The suggestion takes `msrv` into account
 +  [#8280](https://github.com/rust-lang/rust-clippy/pull/8280)
 +* [`manual_bits`] and [`borrow_as_ptr`]: Now track the `clippy::msrv` attribute
 +  [#8280](https://github.com/rust-lang/rust-clippy/pull/8280)
 +* [`disallowed_methods`]: Now works for methods on primitive types
 +  [#8112](https://github.com/rust-lang/rust-clippy/pull/8112)
 +* [`not_unsafe_ptr_arg_deref`]: Now works for type aliases
 +  [#8273](https://github.com/rust-lang/rust-clippy/pull/8273)
 +* [`needless_question_mark`]: Now works for async functions
 +  [#8311](https://github.com/rust-lang/rust-clippy/pull/8311)
 +* [`iter_not_returning_iterator`]: Now handles type projections
 +  [#8228](https://github.com/rust-lang/rust-clippy/pull/8228)
 +* [`wrong_self_convention`]: Now detects wrong `self` references in more cases
 +  [#8208](https://github.com/rust-lang/rust-clippy/pull/8208)
 +* [`single_match`]: Now works for `match` statements with tuples
 +  [#8322](https://github.com/rust-lang/rust-clippy/pull/8322)
 +
 +### False Positive Fixes
 +
 +* [`erasing_op`]: No longer triggers if the output type changes
 +  [#8204](https://github.com/rust-lang/rust-clippy/pull/8204)
 +* [`if_same_then_else`]: No longer triggers for `if let` statements
 +  [#8297](https://github.com/rust-lang/rust-clippy/pull/8297)
 +* [`manual_memcpy`]: No longer lints on `VecDeque`
 +  [#8226](https://github.com/rust-lang/rust-clippy/pull/8226)
 +* [`trait_duplication_in_bounds`]: Now takes path segments into account
 +  [#8315](https://github.com/rust-lang/rust-clippy/pull/8315)
 +* [`deref_addrof`]: No longer lints when the dereference or borrow occurs in different a context
 +  [#8268](https://github.com/rust-lang/rust-clippy/pull/8268)
 +* [`type_repetition_in_bounds`]: Now checks for full equality to prevent false positives
 +  [#8224](https://github.com/rust-lang/rust-clippy/pull/8224)
 +* [`ptr_arg`]: No longer lint for mutable references in traits
 +  [#8369](https://github.com/rust-lang/rust-clippy/pull/8369)
 +* [`implicit_clone`]: No longer lints for double references
 +  [#8231](https://github.com/rust-lang/rust-clippy/pull/8231)
 +* [`needless_lifetimes`]: No longer lints lifetimes for explicit `self` types
 +  [#8278](https://github.com/rust-lang/rust-clippy/pull/8278)
 +* [`op_ref`]: No longer lints in `BinOp` impl if that can cause recursion
 +  [#8298](https://github.com/rust-lang/rust-clippy/pull/8298)
 +* [`enum_variant_names`]: No longer triggers for empty variant names
 +  [#8329](https://github.com/rust-lang/rust-clippy/pull/8329)
 +* [`redundant_closure`]: No longer lints for `Arc<T>` or `Rc<T>`
 +  [#8193](https://github.com/rust-lang/rust-clippy/pull/8193)
 +* [`iter_not_returning_iterator`]: No longer lints on trait implementations but therefore on trait definitions
 +  [#8228](https://github.com/rust-lang/rust-clippy/pull/8228)
 +* [`single_match`]: No longer lints on exhaustive enum patterns without a wildcard
 +  [#8322](https://github.com/rust-lang/rust-clippy/pull/8322)
 +* [`manual_swap`]: No longer lints on cases that involve automatic dereferences
 +  [#8220](https://github.com/rust-lang/rust-clippy/pull/8220)
 +* [`useless_format`]: Now works for implicit named arguments
 +  [#8295](https://github.com/rust-lang/rust-clippy/pull/8295)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`needless_borrow`]: Prevent mutable borrows being moved and suggest removing the borrow on method calls
 +  [#8217](https://github.com/rust-lang/rust-clippy/pull/8217)
 +* [`chars_next_cmp`]: Correctly escapes the suggestion
 +  [#8376](https://github.com/rust-lang/rust-clippy/pull/8376)
 +* [`explicit_write`]: Add suggestions for `write!`s with format arguments
 +  [#8365](https://github.com/rust-lang/rust-clippy/pull/8365)
 +* [`manual_memcpy`]: Suggests `copy_from_slice` when applicable
 +  [#8226](https://github.com/rust-lang/rust-clippy/pull/8226)
 +* [`or_fun_call`]: Improved suggestion display for long arguments
 +  [#8292](https://github.com/rust-lang/rust-clippy/pull/8292)
 +* [`unnecessary_cast`]: Now correctly includes the sign
 +  [#8350](https://github.com/rust-lang/rust-clippy/pull/8350)
 +* [`cmp_owned`]: No longer flips the comparison order
 +  [#8299](https://github.com/rust-lang/rust-clippy/pull/8299)
 +* [`explicit_counter_loop`]: Now correctly suggests `iter()` on references
 +  [#8382](https://github.com/rust-lang/rust-clippy/pull/8382)
 +
 +### ICE Fixes
 +
 +* [`manual_split_once`]
 +  [#8250](https://github.com/rust-lang/rust-clippy/pull/8250)
 +
 +### Documentation Improvements
 +
 +* [`map_flatten`]: Add documentation for the `Option` type
 +  [#8354](https://github.com/rust-lang/rust-clippy/pull/8354)
 +* Document that Clippy's driver might use a different code generation than rustc
 +  [#8037](https://github.com/rust-lang/rust-clippy/pull/8037)
 +* Clippy's lint list will now automatically focus the search box
 +  [#8343](https://github.com/rust-lang/rust-clippy/pull/8343)
 +
 +### Others
 +
 +* Clippy now warns if we find multiple Clippy config files exist
 +  [#8326](https://github.com/rust-lang/rust-clippy/pull/8326)
 +
 +## Rust 1.59
 +
 +Released 2022-02-24
 +
 +[e181011...0eff589](https://github.com/rust-lang/rust-clippy/compare/e181011...0eff589)
 +
 +### New Lints
 +
 +* [`index_refutable_slice`]
 +  [#7643](https://github.com/rust-lang/rust-clippy/pull/7643)
 +* [`needless_splitn`]
 +  [#7896](https://github.com/rust-lang/rust-clippy/pull/7896)
 +* [`unnecessary_to_owned`]
 +  [#7978](https://github.com/rust-lang/rust-clippy/pull/7978)
 +* [`needless_late_init`]
 +  [#7995](https://github.com/rust-lang/rust-clippy/pull/7995)
 +* [`octal_escapes`] [#8007](https://github.com/rust-lang/rust-clippy/pull/8007)
 +* [`return_self_not_must_use`]
 +  [#8071](https://github.com/rust-lang/rust-clippy/pull/8071)
 +* [`init_numbered_fields`]
 +  [#8170](https://github.com/rust-lang/rust-clippy/pull/8170)
 +
 +### Moves and Deprecations
 +
 +* Move `if_then_panic` to `pedantic` and rename to [`manual_assert`] (now
 +  allow-by-default) [#7810](https://github.com/rust-lang/rust-clippy/pull/7810)
 +* Rename `disallow_type` to [`disallowed_types`] and `disallowed_method` to
 +  [`disallowed_methods`]
 +  [#7984](https://github.com/rust-lang/rust-clippy/pull/7984)
 +* Move [`map_flatten`] to `complexity` (now warn-by-default)
 +  [#8054](https://github.com/rust-lang/rust-clippy/pull/8054)
 +
 +### Enhancements
 +
 +* [`match_overlapping_arm`]: Fix false negative where after included ranges,
 +  overlapping ranges weren't linted anymore
 +  [#7909](https://github.com/rust-lang/rust-clippy/pull/7909)
 +* [`deprecated_cfg_attr`]: Now takes the specified MSRV into account
 +  [#7944](https://github.com/rust-lang/rust-clippy/pull/7944)
 +* [`cast_lossless`]: Now also lints for `bool` to integer casts
 +  [#7948](https://github.com/rust-lang/rust-clippy/pull/7948)
 +* [`let_underscore_lock`]: Also emit lints for the `parking_lot` crate
 +  [#7957](https://github.com/rust-lang/rust-clippy/pull/7957)
 +* [`needless_borrow`]
 +  [#7977](https://github.com/rust-lang/rust-clippy/pull/7977)
 +    * Lint when a borrow is auto-dereffed more than once
 +    * Lint in the trailing expression of a block for a match arm
 +* [`strlen_on_c_strings`]
 +  [8001](https://github.com/rust-lang/rust-clippy/pull/8001)
 +    * Lint when used without a fully-qualified path
 +    * Suggest removing the surrounding unsafe block when possible
 +* [`non_ascii_literal`]: Now also lints on `char`s, not just `string`s
 +  [#8034](https://github.com/rust-lang/rust-clippy/pull/8034)
 +* [`single_char_pattern`]: Now also lints on `split_inclusive`, `split_once`,
 +  `rsplit_once`, `replace`, and `replacen`
 +  [#8077](https://github.com/rust-lang/rust-clippy/pull/8077)
 +* [`unwrap_or_else_default`]: Now also lints on `std` constructors like
 +  `Vec::new`, `HashSet::new`, and `HashMap::new`
 +  [#8163](https://github.com/rust-lang/rust-clippy/pull/8163)
 +* [`shadow_reuse`]: Now also lints on shadowed `if let` bindings, instead of
 +  [`shadow_unrelated`]
 +  [#8165](https://github.com/rust-lang/rust-clippy/pull/8165)
 +
 +### False Positive Fixes
 +
 +* [`or_fun_call`], [`unnecessary_lazy_evaluations`]: Improve heuristics, so that
 +  cheap functions (e.g. calling `.len()` on a `Vec`) won't get linted anymore
 +  [#7639](https://github.com/rust-lang/rust-clippy/pull/7639)
 +* [`manual_split_once`]: No longer suggests code changing the original behavior
 +  [#7896](https://github.com/rust-lang/rust-clippy/pull/7896)
 +* Don't show [`no_effect`] or [`unnecessary_operation`] warning for unit struct
 +  implementing `FnOnce`
 +  [#7898](https://github.com/rust-lang/rust-clippy/pull/7898)
 +* [`semicolon_if_nothing_returned`]: Fixed a bug, where the lint wrongly
 +  triggered on `let-else` statements
 +  [#7955](https://github.com/rust-lang/rust-clippy/pull/7955)
 +* [`if_then_some_else_none`]: No longer lints if there is an early return
 +  [#7980](https://github.com/rust-lang/rust-clippy/pull/7980)
 +* [`needless_collect`]: No longer suggests removal of `collect` when removal
 +  would create code requiring mutably borrowing a value multiple times
 +  [#7982](https://github.com/rust-lang/rust-clippy/pull/7982)
 +* [`shadow_same`]: Fix false positive for `async` function's params
 +  [#7997](https://github.com/rust-lang/rust-clippy/pull/7997)
 +* [`suboptimal_flops`]: No longer triggers in constant functions
 +  [#8009](https://github.com/rust-lang/rust-clippy/pull/8009)
 +* [`type_complexity`]: No longer lints on associated types in traits
 +  [#8030](https://github.com/rust-lang/rust-clippy/pull/8030)
 +* [`question_mark`]: No longer lints if returned object is not local
 +  [#8080](https://github.com/rust-lang/rust-clippy/pull/8080)
 +* [`option_if_let_else`]: No longer lint on complex sub-patterns
 +  [#8086](https://github.com/rust-lang/rust-clippy/pull/8086)
 +* [`blocks_in_if_conditions`]: No longer lints on empty closures
 +  [#8100](https://github.com/rust-lang/rust-clippy/pull/8100)
 +* [`enum_variant_names`]: No longer lint when first prefix is only a substring
 +  of a camel-case word
 +  [#8127](https://github.com/rust-lang/rust-clippy/pull/8127)
 +* [`identity_op`]: Only lint on integral operands
 +  [#8183](https://github.com/rust-lang/rust-clippy/pull/8183)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`search_is_some`]: Fix suggestion for `any()` not taking item by reference
 +  [#7463](https://github.com/rust-lang/rust-clippy/pull/7463)
 +* [`almost_swapped`]: Now detects if there is a `no_std` or `no_core` attribute
 +  and adapts the suggestion accordingly
 +  [#7877](https://github.com/rust-lang/rust-clippy/pull/7877)
 +* [`redundant_pattern_matching`]: Fix suggestion for deref expressions
 +  [#7949](https://github.com/rust-lang/rust-clippy/pull/7949)
 +* [`explicit_counter_loop`]: Now also produces a suggestion for non-`usize`
 +  types [#7950](https://github.com/rust-lang/rust-clippy/pull/7950)
 +* [`manual_map`]: Fix suggestion when used with unsafe functions and blocks
 +  [#7968](https://github.com/rust-lang/rust-clippy/pull/7968)
 +* [`option_map_or_none`]: Suggest `map` over `and_then` when possible
 +  [#7971](https://github.com/rust-lang/rust-clippy/pull/7971)
 +* [`option_if_let_else`]: No longer expands macros in the suggestion
 +  [#7974](https://github.com/rust-lang/rust-clippy/pull/7974)
 +* [`iter_cloned_collect`]: Suggest `copied` over `cloned` when possible
 +  [#8006](https://github.com/rust-lang/rust-clippy/pull/8006)
 +* [`doc_markdown`]: No longer uses inline hints to improve readability of
 +  suggestion [#8011](https://github.com/rust-lang/rust-clippy/pull/8011)
 +* [`needless_question_mark`]: Now better explains the suggestion
 +  [#8028](https://github.com/rust-lang/rust-clippy/pull/8028)
 +* [`single_char_pattern`]: Escape backslash `\` in suggestion
 +  [#8067](https://github.com/rust-lang/rust-clippy/pull/8067)
 +* [`needless_bool`]: Suggest `a != b` over `!(a == b)`
 +  [#8117](https://github.com/rust-lang/rust-clippy/pull/8117)
 +* [`iter_skip_next`]: Suggest to add a `mut` if it is necessary in order to
 +  apply this lints suggestion
 +  [#8133](https://github.com/rust-lang/rust-clippy/pull/8133)
 +* [`neg_multiply`]: Now produces a suggestion
 +  [#8144](https://github.com/rust-lang/rust-clippy/pull/8144)
 +* [`needless_return`]: Now suggests the unit type `()` over an empty block `{}`
 +  in match arms [#8185](https://github.com/rust-lang/rust-clippy/pull/8185)
 +* [`suboptimal_flops`]: Now gives a syntactically correct suggestion for
 +  `to_radians` and `to_degrees`
 +  [#8187](https://github.com/rust-lang/rust-clippy/pull/8187)
 +
 +### ICE Fixes
 +
 +* [`undocumented_unsafe_blocks`]
 +  [#7945](https://github.com/rust-lang/rust-clippy/pull/7945)
 +  [#7988](https://github.com/rust-lang/rust-clippy/pull/7988)
 +* [`unnecessary_cast`]
 +  [#8167](https://github.com/rust-lang/rust-clippy/pull/8167)
 +
 +### Documentation Improvements
 +
 +* [`print_stdout`], [`print_stderr`], [`dbg_macro`]: Document how the lint level
 +  can be changed crate-wide
 +  [#8040](https://github.com/rust-lang/rust-clippy/pull/8040)
 +* Added a note to the `README` that config changes don't apply to already
 +  compiled code [#8175](https://github.com/rust-lang/rust-clippy/pull/8175)
 +
 +### Others
 +
 +* [Clippy's lint
 +  list](https://rust-lang.github.io/rust-clippy/master/index.html) now displays
 +  the version a lint was added. :tada:
 +  [#7813](https://github.com/rust-lang/rust-clippy/pull/7813)
 +* New and improved issue templates
 +  [#8032](https://github.com/rust-lang/rust-clippy/pull/8032)
 +* _Dev:_ Add `cargo dev lint` command, to run your modified Clippy version on a
 +  file [#7917](https://github.com/rust-lang/rust-clippy/pull/7917)
 +
 +## Rust 1.58
 +
 +Released 2022-01-13
 +
 +[00e31fa...e181011](https://github.com/rust-lang/rust-clippy/compare/00e31fa...e181011)
 +
 +### Rust 1.58.1
 +
 +* Move [`non_send_fields_in_send_ty`] to `nursery` (now allow-by-default)
 +  [#8075](https://github.com/rust-lang/rust-clippy/pull/8075)
 +* [`useless_format`]: Handle implicit named arguments
 +  [#8295](https://github.com/rust-lang/rust-clippy/pull/8295)
 +
 +### New lints
 +
 +* [`transmute_num_to_bytes`]
 +  [#7805](https://github.com/rust-lang/rust-clippy/pull/7805)
 +* [`match_str_case_mismatch`]
 +  [#7806](https://github.com/rust-lang/rust-clippy/pull/7806)
 +* [`format_in_format_args`], [`to_string_in_format_args`]
 +  [#7743](https://github.com/rust-lang/rust-clippy/pull/7743)
 +* [`uninit_vec`]
 +  [#7682](https://github.com/rust-lang/rust-clippy/pull/7682)
 +* [`fn_to_numeric_cast_any`]
 +  [#7705](https://github.com/rust-lang/rust-clippy/pull/7705)
 +* [`undocumented_unsafe_blocks`]
 +  [#7748](https://github.com/rust-lang/rust-clippy/pull/7748)
 +* [`trailing_empty_array`]
 +  [#7838](https://github.com/rust-lang/rust-clippy/pull/7838)
 +* [`string_slice`]
 +  [#7878](https://github.com/rust-lang/rust-clippy/pull/7878)
 +
 +### Moves or deprecations of lints
 +
 +* Move [`non_send_fields_in_send_ty`] to `suspicious`
 +  [#7874](https://github.com/rust-lang/rust-clippy/pull/7874)
 +* Move [`non_ascii_literal`] to `restriction`
 +  [#7907](https://github.com/rust-lang/rust-clippy/pull/7907)
 +
 +### Changes that expand what code existing lints cover
 +
 +* [`question_mark`] now covers `Result`
 +  [#7840](https://github.com/rust-lang/rust-clippy/pull/7840)
 +* Make [`useless_format`] recognize bare `format!("")`
 +  [#7801](https://github.com/rust-lang/rust-clippy/pull/7801)
 +* Lint on underscored variables with no side effects in [`no_effect`]
 +  [#7775](https://github.com/rust-lang/rust-clippy/pull/7775)
 +* Expand [`match_ref_pats`] to check for multiple reference patterns
 +  [#7800](https://github.com/rust-lang/rust-clippy/pull/7800)
 +
 +### False positive fixes
 +
 +* Fix false positive of [`implicit_saturating_sub`] with `else` clause
 +  [#7832](https://github.com/rust-lang/rust-clippy/pull/7832)
 +* Fix [`question_mark`] when there is call in conditional predicate
 +  [#7860](https://github.com/rust-lang/rust-clippy/pull/7860)
 +* [`mut_mut`] no longer lints when type is defined in external macros
 +  [#7795](https://github.com/rust-lang/rust-clippy/pull/7795)
 +* Avoid [`eq_op`] in test functions
 +  [#7811](https://github.com/rust-lang/rust-clippy/pull/7811)
 +* [`cast_possible_truncation`] no longer lints when cast is coming from `signum`
 +  method call [#7850](https://github.com/rust-lang/rust-clippy/pull/7850)
 +* [`match_str_case_mismatch`] no longer lints on uncased characters
 +  [#7865](https://github.com/rust-lang/rust-clippy/pull/7865)
 +* [`ptr_arg`] no longer lints references to type aliases
 +  [#7890](https://github.com/rust-lang/rust-clippy/pull/7890)
 +* [`missing_safety_doc`] now also accepts "implementation safety" headers
 +  [#7856](https://github.com/rust-lang/rust-clippy/pull/7856)
 +* [`missing_safety_doc`] no longer lints if any parent has `#[doc(hidden)]`
 +  attribute [#7849](https://github.com/rust-lang/rust-clippy/pull/7849)
 +* [`if_not_else`] now ignores else-if statements
 +  [#7895](https://github.com/rust-lang/rust-clippy/pull/7895)
 +* Avoid linting [`cast_possible_truncation`] on bit-reducing operations
 +  [#7819](https://github.com/rust-lang/rust-clippy/pull/7819)
 +* Avoid linting [`field_reassign_with_default`] when `Drop` and `Copy` are
 +  involved [#7794](https://github.com/rust-lang/rust-clippy/pull/7794)
 +* [`unnecessary_sort_by`] now checks if argument implements `Ord` trait
 +  [#7824](https://github.com/rust-lang/rust-clippy/pull/7824)
 +* Fix false positive in [`match_overlapping_arm`]
 +  [#7847](https://github.com/rust-lang/rust-clippy/pull/7847)
 +* Prevent [`needless_lifetimes`] false positive in `async` function definition
 +  [#7901](https://github.com/rust-lang/rust-clippy/pull/7901)
 +
 +### Suggestion fixes/improvements
 +
 +* Keep an initial `::` when [`doc_markdown`] suggests to use ticks
 +  [#7916](https://github.com/rust-lang/rust-clippy/pull/7916)
 +* Add a machine applicable suggestion for the [`doc_markdown`] missing backticks
 +  lint [#7904](https://github.com/rust-lang/rust-clippy/pull/7904)
 +* [`equatable_if_let`] no longer expands macros in the suggestion
 +  [#7788](https://github.com/rust-lang/rust-clippy/pull/7788)
 +* Make [`shadow_reuse`] suggestion less verbose
 +  [#7782](https://github.com/rust-lang/rust-clippy/pull/7782)
 +
 +### ICE fixes
 +
 +* Fix ICE in [`enum_variant_names`]
 +  [#7873](https://github.com/rust-lang/rust-clippy/pull/7873)
 +* Fix ICE in [`undocumented_unsafe_blocks`]
 +  [#7891](https://github.com/rust-lang/rust-clippy/pull/7891)
 +
 +### Documentation improvements
 +
 +* Fixed naive doc formatting for `#[must_use]` lints ([`must_use_unit`],
 +  [`double_must_use`], [`must_use_candidate`], [`let_underscore_must_use`])
 +  [#7827](https://github.com/rust-lang/rust-clippy/pull/7827)
 +* Fix typo in example for [`match_result_ok`]
 +  [#7815](https://github.com/rust-lang/rust-clippy/pull/7815)
 +
 +### Others
 +
 +* Allow giving reasons for [`disallowed_types`]
 +  [#7791](https://github.com/rust-lang/rust-clippy/pull/7791)
 +* Fix [`manual_assert`] and [`match_wild_err_arm`] for `#![no_std]` and Rust
 +  2021. [#7851](https://github.com/rust-lang/rust-clippy/pull/7851)
 +* Fix regression in [`semicolon_if_nothing_returned`] on macros containing while
 +  loops [#7789](https://github.com/rust-lang/rust-clippy/pull/7789)
 +* Added a new configuration `literal-suffix-style` to enforce a certain style
 +  writing [`unseparated_literal_suffix`]
 +  [#7726](https://github.com/rust-lang/rust-clippy/pull/7726)
 +
 +## Rust 1.57
 +
 +Released 2021-12-02
 +
 +[7bfc26e...00e31fa](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...00e31fa)
 +
 +### New Lints
 +
 +* [`negative_feature_names`]
 +  [#7539](https://github.com/rust-lang/rust-clippy/pull/7539)
 +* [`redundant_feature_names`]
 +  [#7539](https://github.com/rust-lang/rust-clippy/pull/7539)
 +* [`mod_module_files`]
 +  [#7543](https://github.com/rust-lang/rust-clippy/pull/7543)
 +* [`self_named_module_files`]
 +  [#7543](https://github.com/rust-lang/rust-clippy/pull/7543)
 +* [`manual_split_once`]
 +  [#7565](https://github.com/rust-lang/rust-clippy/pull/7565)
 +* [`derivable_impls`]
 +  [#7570](https://github.com/rust-lang/rust-clippy/pull/7570)
 +* [`needless_option_as_deref`]
 +  [#7596](https://github.com/rust-lang/rust-clippy/pull/7596)
 +* [`iter_not_returning_iterator`]
 +  [#7610](https://github.com/rust-lang/rust-clippy/pull/7610)
 +* [`same_name_method`]
 +  [#7653](https://github.com/rust-lang/rust-clippy/pull/7653)
 +* [`manual_assert`] [#7669](https://github.com/rust-lang/rust-clippy/pull/7669)
 +* [`non_send_fields_in_send_ty`]
 +  [#7709](https://github.com/rust-lang/rust-clippy/pull/7709)
 +* [`equatable_if_let`]
 +  [#7762](https://github.com/rust-lang/rust-clippy/pull/7762)
 +
 +### Moves and Deprecations
 +
 +* Move [`shadow_unrelated`] to `restriction`
 +  [#7338](https://github.com/rust-lang/rust-clippy/pull/7338)
 +* Move [`option_if_let_else`] to `nursery`
 +  [#7568](https://github.com/rust-lang/rust-clippy/pull/7568)
 +* Move [`branches_sharing_code`] to `nursery`
 +  [#7595](https://github.com/rust-lang/rust-clippy/pull/7595)
 +* Rename `if_let_some_result` to [`match_result_ok`] which now also handles
 +  `while let` cases [#7608](https://github.com/rust-lang/rust-clippy/pull/7608)
 +* Move [`many_single_char_names`] to `pedantic`
 +  [#7671](https://github.com/rust-lang/rust-clippy/pull/7671)
 +* Move [`float_cmp`] to `pedantic`
 +  [#7692](https://github.com/rust-lang/rust-clippy/pull/7692)
 +* Rename `box_vec` to [`box_collection`] and lint on more general cases
 +  [#7693](https://github.com/rust-lang/rust-clippy/pull/7693)
 +* Uplift `invalid_atomic_ordering` to rustc
 +  [rust-lang/rust#84039](https://github.com/rust-lang/rust/pull/84039)
 +
 +### Enhancements
 +
 +* Rewrite the `shadow*` lints, so that they find a lot more shadows and are not
 +  limited to certain patterns
 +  [#7338](https://github.com/rust-lang/rust-clippy/pull/7338)
 +* The `avoid-breaking-exported-api` configuration now also works for
 +  [`box_collection`], [`redundant_allocation`], [`rc_buffer`], [`vec_box`],
 +  [`option_option`], [`linkedlist`], [`rc_mutex`]
 +  [#7560](https://github.com/rust-lang/rust-clippy/pull/7560)
 +* [`unnecessary_unwrap`]: Now also checks for `expect`s
 +  [#7584](https://github.com/rust-lang/rust-clippy/pull/7584)
 +* [`disallowed_methods`]: Allow adding a reason that will be displayed with the
 +  lint message
 +  [#7621](https://github.com/rust-lang/rust-clippy/pull/7621)
 +* [`approx_constant`]: Now checks the MSRV for `LOG10_2` and `LOG2_10`
 +  [#7629](https://github.com/rust-lang/rust-clippy/pull/7629)
 +* [`approx_constant`]: Add `TAU`
 +  [#7642](https://github.com/rust-lang/rust-clippy/pull/7642)
 +* [`needless_borrow`]: Now also lints on needless mutable borrows
 +  [#7657](https://github.com/rust-lang/rust-clippy/pull/7657)
 +* [`missing_safety_doc`]: Now also lints on unsafe traits
 +  [#7734](https://github.com/rust-lang/rust-clippy/pull/7734)
 +
 +### False Positive Fixes
 +
 +* [`manual_map`]: No longer lints when the option is borrowed in the match and
 +  also consumed in the arm
 +  [#7531](https://github.com/rust-lang/rust-clippy/pull/7531)
 +* [`filter_next`]: No longer lints if `filter` method is not the
 +  `Iterator::filter` method
 +  [#7562](https://github.com/rust-lang/rust-clippy/pull/7562)
 +* [`manual_flatten`]: No longer lints if expression is used after `if let`
 +  [#7566](https://github.com/rust-lang/rust-clippy/pull/7566)
 +* [`option_if_let_else`]: Multiple fixes
 +  [#7573](https://github.com/rust-lang/rust-clippy/pull/7573)
 +    * `break` and `continue` statements local to the would-be closure are
 +      allowed
 +    * Don't lint in const contexts
 +    * Don't lint when yield expressions are used
 +    * Don't lint when the captures made by the would-be closure conflict with
 +      the other branch
 +    * Don't lint when a field of a local is used when the type could be
 +      potentially moved from
 +    * In some cases, don't lint when scrutinee expression conflicts with the
 +      captures of the would-be closure
 +* [`redundant_allocation`]: No longer lints on `Box<Box<dyn T>>` which replaces
 +  wide pointers with thin pointers
 +  [#7592](https://github.com/rust-lang/rust-clippy/pull/7592)
 +* [`bool_assert_comparison`]: No longer lints on types that do not implement the
 +  `Not` trait with `Output = bool`
 +  [#7605](https://github.com/rust-lang/rust-clippy/pull/7605)
 +* [`mut_range_bound`]: No longer lints on range bound mutations, that are
 +  immediately followed by a `break;`
 +  [#7607](https://github.com/rust-lang/rust-clippy/pull/7607)
 +* [`mutable_key_type`]: Improve accuracy and document remaining false positives
 +  and false negatives
 +  [#7640](https://github.com/rust-lang/rust-clippy/pull/7640)
 +* [`redundant_closure`]: Rewrite the lint to fix various false positives and
 +  false negatives [#7661](https://github.com/rust-lang/rust-clippy/pull/7661)
 +* [`large_enum_variant`]: No longer wrongly identifies the second largest
 +  variant [#7677](https://github.com/rust-lang/rust-clippy/pull/7677)
 +* [`needless_return`]: No longer lints on let-else expressions
 +  [#7685](https://github.com/rust-lang/rust-clippy/pull/7685)
 +* [`suspicious_else_formatting`]: No longer lints in proc-macros
 +  [#7707](https://github.com/rust-lang/rust-clippy/pull/7707)
 +* [`excessive_precision`]: No longer lints when in some cases the float was
 +  already written in the shortest form
 +  [#7722](https://github.com/rust-lang/rust-clippy/pull/7722)
 +* [`doc_markdown`]: No longer lints on intra-doc links
 +  [#7772](https://github.com/rust-lang/rust-clippy/pull/7772)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`unnecessary_operation`]: Recommend using an `assert!` instead of using a
 +  function call in an indexing operation
 +  [#7453](https://github.com/rust-lang/rust-clippy/pull/7453)
 +* [`manual_split_once`]: Produce semantically equivalent suggestion when
 +  `rsplitn` is used [#7663](https://github.com/rust-lang/rust-clippy/pull/7663)
 +* [`while_let_on_iterator`]: Produce correct suggestion when using `&mut`
 +  [#7690](https://github.com/rust-lang/rust-clippy/pull/7690)
 +* [`manual_assert`]: No better handles complex conditions
 +  [#7741](https://github.com/rust-lang/rust-clippy/pull/7741)
 +* Correctly handle signs in exponents in numeric literals lints
 +  [#7747](https://github.com/rust-lang/rust-clippy/pull/7747)
 +* [`suspicious_map`]: Now also suggests to use `inspect` as an alternative
 +  [#7770](https://github.com/rust-lang/rust-clippy/pull/7770)
 +* Drop exponent from suggestion if it is 0 in numeric literals lints
 +  [#7774](https://github.com/rust-lang/rust-clippy/pull/7774)
 +
 +### ICE Fixes
 +
 +* [`implicit_hasher`]
 +  [#7761](https://github.com/rust-lang/rust-clippy/pull/7761)
 +
 +### Others
 +
 +* Clippy now uses the 2021
 +  [Edition!](https://www.youtube.com/watch?v=q0aNduqb2Ro)
 +  [#7664](https://github.com/rust-lang/rust-clippy/pull/7664)
 +
 +## Rust 1.56
 +
 +Released 2021-10-21
 +
 +[74d1561...7bfc26e](https://github.com/rust-lang/rust-clippy/compare/74d1561...7bfc26e)
 +
 +### New Lints
 +
 +* [`unwrap_or_else_default`]
 +  [#7516](https://github.com/rust-lang/rust-clippy/pull/7516)
 +
 +### Enhancements
 +
 +* [`needless_continue`]: Now also lints in `loop { continue; }` case
 +  [#7477](https://github.com/rust-lang/rust-clippy/pull/7477)
 +* [`disallowed_types`]: Now also primitive types can be disallowed
 +  [#7488](https://github.com/rust-lang/rust-clippy/pull/7488)
 +* [`manual_swap`]: Now also lints on xor swaps
 +  [#7506](https://github.com/rust-lang/rust-clippy/pull/7506)
 +* [`map_flatten`]: Now also lints on the `Result` type
 +  [#7522](https://github.com/rust-lang/rust-clippy/pull/7522)
 +* [`no_effect`]: Now also lints on inclusive ranges
 +  [#7556](https://github.com/rust-lang/rust-clippy/pull/7556)
 +
 +### False Positive Fixes
 +
 +* [`nonstandard_macro_braces`]: No longer lints on similar named nested macros
 +  [#7478](https://github.com/rust-lang/rust-clippy/pull/7478)
 +* [`too_many_lines`]: No longer lints in closures to avoid duplicated diagnostics
 +  [#7534](https://github.com/rust-lang/rust-clippy/pull/7534)
 +* [`similar_names`]: No longer complains about `iter` and `item` being too
 +  similar [#7546](https://github.com/rust-lang/rust-clippy/pull/7546)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`similar_names`]: No longer suggests to insert or add an underscore as a fix
 +  [#7221](https://github.com/rust-lang/rust-clippy/pull/7221)
 +* [`new_without_default`]: No longer shows the full qualified type path when
 +  suggesting adding a `Default` implementation
 +  [#7493](https://github.com/rust-lang/rust-clippy/pull/7493)
 +* [`while_let_on_iterator`]: Now suggests re-borrowing mutable references
 +  [#7520](https://github.com/rust-lang/rust-clippy/pull/7520)
 +* [`extend_with_drain`]: Improve code suggestion for mutable and immutable
 +  references [#7533](https://github.com/rust-lang/rust-clippy/pull/7533)
 +* [`trivially_copy_pass_by_ref`]: Now properly handles `Self` type
 +  [#7535](https://github.com/rust-lang/rust-clippy/pull/7535)
 +* [`never_loop`]: Now suggests using `if let` instead of a `for` loop when
 +  applicable [#7541](https://github.com/rust-lang/rust-clippy/pull/7541)
 +
 +### Documentation Improvements
 +
 +* Clippy now uses a lint to generate its lint documentation. [Lints all the way
 +  down](https://en.wikipedia.org/wiki/Turtles_all_the_way_down).
 +  [#7502](https://github.com/rust-lang/rust-clippy/pull/7502)
 +* Reworked Clippy's website:
 +  [#7172](https://github.com/rust-lang/rust-clippy/issues/7172)
 +  [#7279](https://github.com/rust-lang/rust-clippy/pull/7279)
 +  * Added applicability information about lints
 +  * Added a link to jump into the implementation
 +  * Improved loading times
 +  * Adapted some styling
 +* `cargo clippy --help` now also explains the `--fix` and `--no-deps` flag
 +  [#7492](https://github.com/rust-lang/rust-clippy/pull/7492)
 +* [`unnested_or_patterns`]: Removed `or_patterns` feature gate in the code
 +  example [#7507](https://github.com/rust-lang/rust-clippy/pull/7507)
 +
 +## Rust 1.55
 +
 +Released 2021-09-09
 +
 +[3ae8faf...74d1561](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...74d1561)
 +
 +### Important Changes
 +
 +* Stabilized `cargo clippy --fix` :tada:
 +  [#7405](https://github.com/rust-lang/rust-clippy/pull/7405)
 +
 +### New Lints
 +
 +* [`rc_mutex`]
 +  [#7316](https://github.com/rust-lang/rust-clippy/pull/7316)
 +* [`nonstandard_macro_braces`]
 +  [#7299](https://github.com/rust-lang/rust-clippy/pull/7299)
 +* [`strlen_on_c_strings`]
 +  [#7243](https://github.com/rust-lang/rust-clippy/pull/7243)
 +* [`self_named_constructors`]
 +  [#7403](https://github.com/rust-lang/rust-clippy/pull/7403)
 +* [`disallowed_script_idents`]
 +  [#7400](https://github.com/rust-lang/rust-clippy/pull/7400)
 +* [`disallowed_types`]
 +  [#7315](https://github.com/rust-lang/rust-clippy/pull/7315)
 +* [`missing_enforced_import_renames`]
 +  [#7300](https://github.com/rust-lang/rust-clippy/pull/7300)
 +* [`extend_with_drain`]
 +  [#7270](https://github.com/rust-lang/rust-clippy/pull/7270)
 +
 +### Moves and Deprecations
 +
 +* Moved [`from_iter_instead_of_collect`] to `pedantic`
 +  [#7375](https://github.com/rust-lang/rust-clippy/pull/7375)
 +* Added `suspicious` as a new lint group for *code that is most likely wrong or useless*
 +  [#7350](https://github.com/rust-lang/rust-clippy/pull/7350)
 +  * Moved [`blanket_clippy_restriction_lints`] to `suspicious`
 +  * Moved [`empty_loop`] to `suspicious`
 +  * Moved [`eval_order_dependence`] to `suspicious`
 +  * Moved [`float_equality_without_abs`] to `suspicious`
 +  * Moved [`for_loops_over_fallibles`] to `suspicious`
 +  * Moved [`misrefactored_assign_op`] to `suspicious`
 +  * Moved [`mut_range_bound`] to `suspicious`
 +  * Moved [`mutable_key_type`] to `suspicious`
 +  * Moved [`suspicious_arithmetic_impl`] to `suspicious`
 +  * Moved [`suspicious_assignment_formatting`] to `suspicious`
 +  * Moved [`suspicious_else_formatting`] to `suspicious`
 +  * Moved [`suspicious_map`] to `suspicious`
 +  * Moved [`suspicious_op_assign_impl`] to `suspicious`
 +  * Moved [`suspicious_unary_op_formatting`] to `suspicious`
 +
 +### Enhancements
 +
 +* [`while_let_on_iterator`]: Now suggests `&mut iter` inside closures
 +  [#7262](https://github.com/rust-lang/rust-clippy/pull/7262)
 +* [`doc_markdown`]:
 +  * Now detects unbalanced ticks
 +    [#7357](https://github.com/rust-lang/rust-clippy/pull/7357)
 +  * Add `FreeBSD` to the default configuration as an allowed identifier
 +    [#7334](https://github.com/rust-lang/rust-clippy/pull/7334)
 +* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]: Now allows wildcards for enums with unstable
 +  or hidden variants
 +  [#7407](https://github.com/rust-lang/rust-clippy/pull/7407)
 +* [`redundant_allocation`]: Now additionally supports the `Arc<>` type
 +  [#7308](https://github.com/rust-lang/rust-clippy/pull/7308)
 +* [`disallowed_names`]: Now allows disallowed names in test code
 +  [#7379](https://github.com/rust-lang/rust-clippy/pull/7379)
 +* [`redundant_closure`]: Suggests `&mut` for `FnMut`
 +  [#7437](https://github.com/rust-lang/rust-clippy/pull/7437)
 +* [`disallowed_methods`], [`disallowed_types`]: The configuration values `disallowed-method` and `disallowed-type`
 +  no longer require fully qualified paths
 +  [#7345](https://github.com/rust-lang/rust-clippy/pull/7345)
 +* [`zst_offset`]: Fixed lint invocation after it was accidentally suppressed
 +  [#7396](https://github.com/rust-lang/rust-clippy/pull/7396)
 +
 +### False Positive Fixes
 +
 +* [`default_numeric_fallback`]: No longer lints on float literals as function arguments
 +  [#7446](https://github.com/rust-lang/rust-clippy/pull/7446)
 +* [`use_self`]: No longer lints on type parameters
 +  [#7288](https://github.com/rust-lang/rust-clippy/pull/7288)
 +* [`unimplemented`]: Now ignores the `assert` and `debug_assert` macros
 +  [#7439](https://github.com/rust-lang/rust-clippy/pull/7439)
 +* [`branches_sharing_code`]: Now always checks for block expressions
 +  [#7462](https://github.com/rust-lang/rust-clippy/pull/7462)
 +* [`field_reassign_with_default`]: No longer triggers in macros
 +  [#7160](https://github.com/rust-lang/rust-clippy/pull/7160)
 +* [`redundant_clone`]: No longer lints on required clones for borrowed data
 +  [#7346](https://github.com/rust-lang/rust-clippy/pull/7346)
 +* [`default_numeric_fallback`]: No longer triggers in external macros
 +  [#7325](https://github.com/rust-lang/rust-clippy/pull/7325)
 +* [`needless_bool`]: No longer lints in macros
 +  [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
 +* [`useless_format`]: No longer triggers when additional text is being appended
 +  [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
 +* [`assertions_on_constants`]: `cfg!(...)` is no longer considered to be a constant
 +  [#7319](https://github.com/rust-lang/rust-clippy/pull/7319)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`needless_collect`]: Now show correct lint messages for shadowed values
 +  [#7289](https://github.com/rust-lang/rust-clippy/pull/7289)
 +* [`wrong_pub_self_convention`]: The deprecated message now suggest the correct configuration value
 +  [#7382](https://github.com/rust-lang/rust-clippy/pull/7382)
 +* [`semicolon_if_nothing_returned`]: Allow missing semicolon in blocks with only one expression
 +  [#7326](https://github.com/rust-lang/rust-clippy/pull/7326)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#7470](https://github.com/rust-lang/rust-clippy/pull/7470)
 +* [`redundant_pattern_matching`]
 +  [#7471](https://github.com/rust-lang/rust-clippy/pull/7471)
 +* [`modulo_one`]
 +  [#7473](https://github.com/rust-lang/rust-clippy/pull/7473)
 +* [`use_self`]
 +  [#7428](https://github.com/rust-lang/rust-clippy/pull/7428)
 +
 +## Rust 1.54
 +
 +Released 2021-07-29
 +
 +[7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
 +
 +### New Lints
 +
 +- [`ref_binding_to_reference`]
 +  [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
 +- [`needless_bitwise_bool`]
 +  [#7133](https://github.com/rust-lang/rust-clippy/pull/7133)
 +- [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225)
 +- [`manual_str_repeat`]
 +  [#7265](https://github.com/rust-lang/rust-clippy/pull/7265)
 +- [`suspicious_splitn`]
 +  [#7292](https://github.com/rust-lang/rust-clippy/pull/7292)
 +
 +### Moves and Deprecations
 +
 +- Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of
 +  the new `avoid-breaking-exported-api` config option (see
 +  [Enhancements](#1-54-enhancements))
 +  [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
 +- Move [`inconsistent_struct_constructor`] to `pedantic`
 +  [#7193](https://github.com/rust-lang/rust-clippy/pull/7193)
 +- Move [`needless_borrow`] to `style` (now warn-by-default)
 +  [#7254](https://github.com/rust-lang/rust-clippy/pull/7254)
 +- Move [`suspicious_operation_groupings`] to `nursery`
 +  [#7266](https://github.com/rust-lang/rust-clippy/pull/7266)
 +- Move [`semicolon_if_nothing_returned`] to `pedantic`
 +  [#7268](https://github.com/rust-lang/rust-clippy/pull/7268)
 +
 +### Enhancements <a name="1-54-enhancements"></a>
 +
 +- [`while_let_on_iterator`]: Now also lints in nested loops
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix`
 +  [#7156](https://github.com/rust-lang/rust-clippy/pull/7156)
 +- [`needless_collect`]: Now also lints on assignments with type annotations
 +  [#7163](https://github.com/rust-lang/rust-clippy/pull/7163)
 +- [`if_then_some_else_none`]: Now works with the MSRV config
 +  [#7177](https://github.com/rust-lang/rust-clippy/pull/7177)
 +- Add `avoid-breaking-exported-api` config option for the lints
 +  [`enum_variant_names`], [`large_types_passed_by_value`],
 +  [`trivially_copy_pass_by_ref`], [`unnecessary_wraps`],
 +  [`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set
 +  this configuration option to `false` before a major release (1.0/2.0/...) to
 +  clean up the API [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
 +- [`needless_collect`]: Now lints on even more data structures
 +  [#7188](https://github.com/rust-lang/rust-clippy/pull/7188)
 +- [`missing_docs_in_private_items`]: No longer sees `#[<name> = "<value>"]` like
 +  attributes as sufficient documentation
 +  [#7281](https://github.com/rust-lang/rust-clippy/pull/7281)
 +- [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]:
 +  Now work as expected when used with `allow`
 +  [#7282](https://github.com/rust-lang/rust-clippy/pull/7282)
 +
 +### False Positive Fixes
 +
 +- [`implicit_return`]: Now takes all diverging functions in account to avoid
 +  false positives [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
 +- [`while_let_on_iterator`]: No longer lints when the iterator is a struct field
 +  and the struct is used in the loop
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`multiple_inherent_impl`]: No longer lints with generic arguments
 +  [#7089](https://github.com/rust-lang/rust-clippy/pull/7089)
 +- [`comparison_chain`]: No longer lints in a `const` context
 +  [#7118](https://github.com/rust-lang/rust-clippy/pull/7118)
 +- [`while_immutable_condition`]: Fix false positive where mutation in the loop
 +  variable wasn't picked up
 +  [#7144](https://github.com/rust-lang/rust-clippy/pull/7144)
 +- [`default_trait_access`]: No longer lints in macros
 +  [#7150](https://github.com/rust-lang/rust-clippy/pull/7150)
 +- [`needless_question_mark`]: No longer lints when the inner value is implicitly
 +  dereferenced [#7165](https://github.com/rust-lang/rust-clippy/pull/7165)
 +- [`unused_unit`]: No longer lints when multiple macro contexts are involved
 +  [#7167](https://github.com/rust-lang/rust-clippy/pull/7167)
 +- [`eval_order_dependence`]: Fix false positive in async context
 +  [#7174](https://github.com/rust-lang/rust-clippy/pull/7174)
 +- [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the
 +  type [#7175](https://github.com/rust-lang/rust-clippy/pull/7175)
 +- [`wrong_self_convention`]: No longer lints in trait implementations of
 +  non-`Copy` types [#7182](https://github.com/rust-lang/rust-clippy/pull/7182)
 +- [`suboptimal_flops`]: No longer lints on `powi(2)`
 +  [#7201](https://github.com/rust-lang/rust-clippy/pull/7201)
 +- [`wrong_self_convention`]: No longer lints if there is no implicit `self`
 +  [#7215](https://github.com/rust-lang/rust-clippy/pull/7215)
 +- [`option_if_let_else`]: No longer lints on `else if let` pattern
 +  [#7216](https://github.com/rust-lang/rust-clippy/pull/7216)
 +- [`use_self`], [`useless_conversion`]: Fix false positives when generic
 +  arguments are involved
 +  [#7223](https://github.com/rust-lang/rust-clippy/pull/7223)
 +- [`manual_unwrap_or`]: Fix false positive with deref coercion
 +  [#7233](https://github.com/rust-lang/rust-clippy/pull/7233)
 +- [`similar_names`]: No longer lints on `wparam`/`lparam`
 +  [#7255](https://github.com/rust-lang/rust-clippy/pull/7255)
 +- [`redundant_closure`]: No longer lints on using the `vec![]` macro in a
 +  closure [#7263](https://github.com/rust-lang/rust-clippy/pull/7263)
 +
 +### Suggestion Fixes/Improvements
 +
 +- [`implicit_return`]
 +  [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
 +    - Fix suggestion for async functions
 +    - Improve suggestion with macros
 +    - Suggest to change `break` to `return` when appropriate
 +- [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`match_single_binding`]: Improve suggestion when match scrutinee has side
 +  effects [#7095](https://github.com/rust-lang/rust-clippy/pull/7095)
 +- [`needless_borrow`]: Now suggests to also change usage sites as needed
 +  [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
 +- [`write_with_newline`]: Improve suggestion when only `\n` is written to the
 +  buffer [#7183](https://github.com/rust-lang/rust-clippy/pull/7183)
 +- [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also
 +  when a `<_ as Trait>::_` is involved
 +  [#7264](https://github.com/rust-lang/rust-clippy/pull/7264)
 +- [`not_unsafe_ptr_arg_deref`]: Improved error message
 +  [#7294](https://github.com/rust-lang/rust-clippy/pull/7294)
 +
 +### ICE Fixes
 +
 +- Fix ICE when running Clippy on `libstd`
 +  [#7140](https://github.com/rust-lang/rust-clippy/pull/7140)
 +- [`implicit_return`]
 +  [#7242](https://github.com/rust-lang/rust-clippy/pull/7242)
 +
 +## Rust 1.53
 +
 +Released 2021-06-17
 +
 +[6ed6f1e...7c7683c](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...7c7683c)
 +
 +### New Lints
 +
 +* [`option_filter_map`]
 +  [#6342](https://github.com/rust-lang/rust-clippy/pull/6342)
 +* [`branches_sharing_code`]
 +  [#6463](https://github.com/rust-lang/rust-clippy/pull/6463)
 +* [`needless_for_each`]
 +  [#6706](https://github.com/rust-lang/rust-clippy/pull/6706)
 +* [`if_then_some_else_none`]
 +  [#6859](https://github.com/rust-lang/rust-clippy/pull/6859)
 +* [`non_octal_unix_permissions`]
 +  [#7001](https://github.com/rust-lang/rust-clippy/pull/7001)
 +* [`unnecessary_self_imports`]
 +  [#7072](https://github.com/rust-lang/rust-clippy/pull/7072)
 +* [`bool_assert_comparison`]
 +  [#7083](https://github.com/rust-lang/rust-clippy/pull/7083)
 +* [`cloned_instead_of_copied`]
 +  [#7098](https://github.com/rust-lang/rust-clippy/pull/7098)
 +* [`flat_map_option`]
 +  [#7101](https://github.com/rust-lang/rust-clippy/pull/7101)
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`filter_map`] lint
 +  [#7059](https://github.com/rust-lang/rust-clippy/pull/7059)
 +* Move [`transmute_ptr_to_ptr`] to `pedantic`
 +  [#7102](https://github.com/rust-lang/rust-clippy/pull/7102)
 +
 +### Enhancements
 +
 +* [`mem_replace_with_default`]: Also lint on common std constructors
 +  [#6820](https://github.com/rust-lang/rust-clippy/pull/6820)
 +* [`wrong_self_convention`]: Also lint on `to_*_mut` methods
 +  [#6828](https://github.com/rust-lang/rust-clippy/pull/6828)
 +* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]:
 +  [#6863](https://github.com/rust-lang/rust-clippy/pull/6863)
 +    * Attempt to find a common path prefix in suggestion
 +    * Don't lint on `Option` and `Result`
 +    * Consider `Self` prefix
 +* [`explicit_deref_methods`]: Also lint on chained `deref` calls
 +  [#6865](https://github.com/rust-lang/rust-clippy/pull/6865)
 +* [`or_fun_call`]: Also lint on `unsafe` blocks
 +  [#6928](https://github.com/rust-lang/rust-clippy/pull/6928)
 +* [`vec_box`], [`linkedlist`], [`option_option`]: Also lint in `const` and
 +  `static` items [#6938](https://github.com/rust-lang/rust-clippy/pull/6938)
 +* [`search_is_some`]: Also check for `is_none`
 +  [#6942](https://github.com/rust-lang/rust-clippy/pull/6942)
 +* [`string_lit_as_bytes`]: Also lint on `into_bytes`
 +  [#6959](https://github.com/rust-lang/rust-clippy/pull/6959)
 +* [`len_without_is_empty`]: Also lint if function signatures of `len` and
 +  `is_empty` don't match
 +  [#6980](https://github.com/rust-lang/rust-clippy/pull/6980)
 +* [`redundant_pattern_matching`]: Also lint if the pattern is a `&` pattern
 +  [#6991](https://github.com/rust-lang/rust-clippy/pull/6991)
 +* [`clone_on_copy`]: Also lint on chained method calls taking `self` by value
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`missing_panics_doc`]: Also lint on `assert_eq!` and `assert_ne!`
 +  [#7029](https://github.com/rust-lang/rust-clippy/pull/7029)
 +* [`needless_return`]: Also lint in `async` functions
 +  [#7067](https://github.com/rust-lang/rust-clippy/pull/7067)
 +* [`unused_io_amount`]: Also lint on expressions like `_.read().ok()?`
 +  [#7100](https://github.com/rust-lang/rust-clippy/pull/7100)
 +* [`iter_cloned_collect`]: Also lint on large arrays, since const-generics are
 +  now stable [#7138](https://github.com/rust-lang/rust-clippy/pull/7138)
 +
 +### False Positive Fixes
 +
 +* [`upper_case_acronyms`]: No longer lints on public items
 +  [#6805](https://github.com/rust-lang/rust-clippy/pull/6805)
 +* [`suspicious_map`]: No longer lints when side effects may occur inside the
 +  `map` call [#6831](https://github.com/rust-lang/rust-clippy/pull/6831)
 +* [`manual_map`], [`manual_unwrap_or`]: No longer lints in `const` functions
 +  [#6917](https://github.com/rust-lang/rust-clippy/pull/6917)
 +* [`wrong_self_convention`]: Now respects `Copy` types
 +  [#6924](https://github.com/rust-lang/rust-clippy/pull/6924)
 +* [`needless_question_mark`]: No longer lints if the `?` and the `Some(..)` come
 +  from different macro contexts [#6935](https://github.com/rust-lang/rust-clippy/pull/6935)
 +* [`map_entry`]: Better detect if the entry API can be used
 +  [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
 +* [`or_fun_call`]: No longer lints on some `len` function calls
 +  [#6950](https://github.com/rust-lang/rust-clippy/pull/6950)
 +* [`new_ret_no_self`]: No longer lints when `Self` is returned with different
 +  generic arguments [#6952](https://github.com/rust-lang/rust-clippy/pull/6952)
 +* [`upper_case_acronyms`]: No longer lints on public items
 +  [#6981](https://github.com/rust-lang/rust-clippy/pull/6981)
 +* [`explicit_into_iter_loop`]: Only lint when `into_iter` is an implementation
 +  of `IntoIterator` [#6982](https://github.com/rust-lang/rust-clippy/pull/6982)
 +* [`expl_impl_clone_on_copy`]: Take generic constraints into account before
 +  suggesting to use `derive` instead
 +  [#6993](https://github.com/rust-lang/rust-clippy/pull/6993)
 +* [`missing_panics_doc`]: No longer lints when only debug-assertions are used
 +  [#6996](https://github.com/rust-lang/rust-clippy/pull/6996)
 +* [`clone_on_copy`]: Only lint when using the `Clone` trait
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`wrong_self_convention`]: No longer lints inside a trait implementation
 +  [#7002](https://github.com/rust-lang/rust-clippy/pull/7002)
 +* [`redundant_clone`]: No longer lints when the cloned value is modified while
 +  the clone is in use
 +  [#7011](https://github.com/rust-lang/rust-clippy/pull/7011)
 +* [`same_item_push`]: No longer lints if the `Vec` is used in the loop body
 +  [#7018](https://github.com/rust-lang/rust-clippy/pull/7018)
 +* [`cargo_common_metadata`]: Remove author requirement
 +  [#7026](https://github.com/rust-lang/rust-clippy/pull/7026)
 +* [`panic_in_result_fn`]: No longer lints on `debug_assert` family
 +  [#7060](https://github.com/rust-lang/rust-clippy/pull/7060)
 +* [`panic`]: No longer wrongfully lints on `debug_assert` with message
 +  [#7063](https://github.com/rust-lang/rust-clippy/pull/7063)
 +* [`wrong_self_convention`]: No longer lints in trait implementations where no
 +  `self` is involved [#7064](https://github.com/rust-lang/rust-clippy/pull/7064)
 +* [`missing_const_for_fn`]: No longer lints when unstable `const` function is
 +  involved [#7076](https://github.com/rust-lang/rust-clippy/pull/7076)
 +* [`suspicious_else_formatting`]: Allow Allman style braces
 +  [#7087](https://github.com/rust-lang/rust-clippy/pull/7087)
 +* [`inconsistent_struct_constructor`]: No longer lints in macros
 +  [#7097](https://github.com/rust-lang/rust-clippy/pull/7097)
 +* [`single_component_path_imports`]: No longer lints on macro re-exports
 +  [#7120](https://github.com/rust-lang/rust-clippy/pull/7120)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`redundant_pattern_matching`]: Add a note when applying this lint would
 +  change the drop order
 +  [#6568](https://github.com/rust-lang/rust-clippy/pull/6568)
 +* [`write_literal`], [`print_literal`]: Add auto-applicable suggestion
 +  [#6821](https://github.com/rust-lang/rust-clippy/pull/6821)
 +* [`manual_map`]: Fix suggestion for complex `if let ... else` chains
 +  [#6856](https://github.com/rust-lang/rust-clippy/pull/6856)
 +* [`inconsistent_struct_constructor`]: Make lint description and message clearer
 +  [#6892](https://github.com/rust-lang/rust-clippy/pull/6892)
 +* [`map_entry`]: Now suggests `or_insert`, `insert_with` or `match _.entry(_)`
 +  as appropriate [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
 +* [`manual_flatten`]: Suggest to insert `copied` if necessary
 +  [#6962](https://github.com/rust-lang/rust-clippy/pull/6962)
 +* [`redundant_slicing`]: Fix suggestion when a re-borrow might be required or
 +  when the value is from a macro call
 +  [#6975](https://github.com/rust-lang/rust-clippy/pull/6975)
 +* [`match_wildcard_for_single_variants`]: Fix suggestion for hidden variant
 +  [#6988](https://github.com/rust-lang/rust-clippy/pull/6988)
 +* [`clone_on_copy`]: Correct suggestion when the cloned value is a macro call
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`manual_map`]: Fix suggestion at the end of an if chain
 +  [#7004](https://github.com/rust-lang/rust-clippy/pull/7004)
 +* Fix needless parenthesis output in multiple lint suggestions
 +  [#7013](https://github.com/rust-lang/rust-clippy/pull/7013)
 +* [`needless_collect`]: Better explanation in the lint message
 +  [#7020](https://github.com/rust-lang/rust-clippy/pull/7020)
 +* [`useless_vec`]: Now considers mutability
 +  [#7036](https://github.com/rust-lang/rust-clippy/pull/7036)
 +* [`useless_format`]: Wrap the content in braces if necessary
 +  [#7092](https://github.com/rust-lang/rust-clippy/pull/7092)
 +* [`single_match`]: Don't suggest an equality check for types which don't
 +  implement `PartialEq`
 +  [#7093](https://github.com/rust-lang/rust-clippy/pull/7093)
 +* [`from_over_into`]: Mention type in help message
 +  [#7099](https://github.com/rust-lang/rust-clippy/pull/7099)
 +* [`manual_unwrap_or`]: Fix invalid code suggestion due to a macro call
 +  [#7136](https://github.com/rust-lang/rust-clippy/pull/7136)
 +
 +### ICE Fixes
 +
 +* [`macro_use_imports`]
 +  [#7022](https://github.com/rust-lang/rust-clippy/pull/7022)
 +* [`missing_panics_doc`]
 +  [#7034](https://github.com/rust-lang/rust-clippy/pull/7034)
 +* [`tabs_in_doc_comments`]
 +  [#7039](https://github.com/rust-lang/rust-clippy/pull/7039)
 +* [`missing_const_for_fn`]
 +  [#7128](https://github.com/rust-lang/rust-clippy/pull/7128)
 +
 +### Others
 +
 +* [Clippy's lint
 +  list](https://rust-lang.github.io/rust-clippy/master/index.html) now supports
 +  themes [#7030](https://github.com/rust-lang/rust-clippy/pull/7030)
 +* Lints that were uplifted to `rustc` now mention the new `rustc` name in the
 +  deprecation warning
 +  [#7056](https://github.com/rust-lang/rust-clippy/pull/7056)
 +
 +## Rust 1.52
 +
 +Released 2021-05-06
 +
 +[3e41797...6ed6f1e](https://github.com/rust-lang/rust-clippy/compare/3e41797...6ed6f1e)
 +
 +### New Lints
 +
 +* [`from_str_radix_10`]
 +  [#6717](https://github.com/rust-lang/rust-clippy/pull/6717)
 +* [`implicit_clone`]
 +  [#6730](https://github.com/rust-lang/rust-clippy/pull/6730)
 +* [`semicolon_if_nothing_returned`]
 +  [#6681](https://github.com/rust-lang/rust-clippy/pull/6681)
 +* [`manual_flatten`]
 +  [#6646](https://github.com/rust-lang/rust-clippy/pull/6646)
 +* [`inconsistent_struct_constructor`]
 +  [#6769](https://github.com/rust-lang/rust-clippy/pull/6769)
 +* [`iter_count`]
 +  [#6791](https://github.com/rust-lang/rust-clippy/pull/6791)
 +* [`default_numeric_fallback`]
 +  [#6662](https://github.com/rust-lang/rust-clippy/pull/6662)
 +* [`bytes_nth`]
 +  [#6695](https://github.com/rust-lang/rust-clippy/pull/6695)
 +* [`filter_map_identity`]
 +  [#6685](https://github.com/rust-lang/rust-clippy/pull/6685)
 +* [`manual_map`]
 +  [#6573](https://github.com/rust-lang/rust-clippy/pull/6573)
 +
 +### Moves and Deprecations
 +
 +* Moved [`upper_case_acronyms`] to `pedantic`
 +  [#6775](https://github.com/rust-lang/rust-clippy/pull/6775)
 +* Moved [`manual_map`] to `nursery`
 +  [#6796](https://github.com/rust-lang/rust-clippy/pull/6796)
 +* Moved [`unnecessary_wraps`] to `pedantic`
 +  [#6765](https://github.com/rust-lang/rust-clippy/pull/6765)
 +* Moved [`trivial_regex`] to `nursery`
 +  [#6696](https://github.com/rust-lang/rust-clippy/pull/6696)
 +* Moved [`naive_bytecount`] to `pedantic`
 +  [#6825](https://github.com/rust-lang/rust-clippy/pull/6825)
 +* Moved [`upper_case_acronyms`] to `style`
 +  [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
 +* Moved [`manual_map`] to `style`
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +
 +### Enhancements
 +
 +* [`disallowed_methods`]: Now supports functions in addition to methods
 +  [#6674](https://github.com/rust-lang/rust-clippy/pull/6674)
 +* [`upper_case_acronyms`]: Added a new configuration `upper-case-acronyms-aggressive` to
 +  trigger the lint if there is more than one uppercase character next to each other
 +  [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
 +* [`collapsible_match`]: Now supports block comparison with different value names
 +  [#6754](https://github.com/rust-lang/rust-clippy/pull/6754)
 +* [`unnecessary_wraps`]: Will now suggest removing unnecessary wrapped return unit type, like `Option<()>`
 +  [#6665](https://github.com/rust-lang/rust-clippy/pull/6665)
 +* Improved value usage detection in closures
 +  [#6698](https://github.com/rust-lang/rust-clippy/pull/6698)
 +
 +### False Positive Fixes
 +
 +* [`use_self`]: No longer lints in macros
 +  [#6833](https://github.com/rust-lang/rust-clippy/pull/6833)
 +* [`use_self`]: Fixed multiple false positives for: generics, associated types and derive implementations
 +  [#6179](https://github.com/rust-lang/rust-clippy/pull/6179)
 +* [`missing_inline_in_public_items`]: No longer lints for procedural macros
 +  [#6814](https://github.com/rust-lang/rust-clippy/pull/6814)
 +* [`inherent_to_string`]: No longer lints on functions with function generics
 +  [#6771](https://github.com/rust-lang/rust-clippy/pull/6771)
 +* [`doc_markdown`]: Add `OpenDNS` to the default configuration as an allowed identifier
 +  [#6783](https://github.com/rust-lang/rust-clippy/pull/6783)
 +* [`missing_panics_doc`]: No longer lints on [`unreachable!`](https://doc.rust-lang.org/std/macro.unreachable.html)
 +  [#6700](https://github.com/rust-lang/rust-clippy/pull/6700)
 +* [`collapsible_if`]: No longer lints on if statements with attributes
 +  [#6701](https://github.com/rust-lang/rust-clippy/pull/6701)
 +* [`match_same_arms`]: Only considers empty blocks as equal if the tokens contained are the same
 +  [#6843](https://github.com/rust-lang/rust-clippy/pull/6843)
 +* [`redundant_closure`]: Now ignores macros
 +  [#6871](https://github.com/rust-lang/rust-clippy/pull/6871)
 +* [`manual_map`]: Fixed false positives when control flow statements like `return`, `break` etc. are used
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +* [`vec_init_then_push`]: Fixed false positives for loops and if statements
 +  [#6697](https://github.com/rust-lang/rust-clippy/pull/6697)
 +* [`len_without_is_empty`]: Will now consider multiple impl blocks and `#[allow]` on
 +  the `len` method as well as the type definition.
 +  [#6853](https://github.com/rust-lang/rust-clippy/pull/6853)
 +* [`let_underscore_drop`]: Only lints on types which implement `Drop`
 +  [#6682](https://github.com/rust-lang/rust-clippy/pull/6682)
 +* [`unit_arg`]: No longer lints on unit arguments when they come from a path expression.
 +  [#6601](https://github.com/rust-lang/rust-clippy/pull/6601)
 +* [`cargo_common_metadata`]: No longer lints if
 +  [`publish = false`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field)
 +  is defined in the manifest
 +  [#6650](https://github.com/rust-lang/rust-clippy/pull/6650)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`collapsible_match`]: Fixed lint message capitalization
 +  [#6766](https://github.com/rust-lang/rust-clippy/pull/6766)
 +* [`or_fun_call`]: Improved suggestions for `or_insert(vec![])`
 +  [#6790](https://github.com/rust-lang/rust-clippy/pull/6790)
 +* [`manual_map`]: No longer expands macros in the suggestions
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +* Aligned Clippy's lint messages with the rustc dev guide
 +  [#6787](https://github.com/rust-lang/rust-clippy/pull/6787)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#6866](https://github.com/rust-lang/rust-clippy/pull/6866)
 +
 +### Documentation Improvements
 +
 +* [`useless_format`]: Improved the documentation example
 +  [#6854](https://github.com/rust-lang/rust-clippy/pull/6854)
 +* Clippy's [`README.md`]: Includes a new subsection on running Clippy as a rustc wrapper
 +  [#6782](https://github.com/rust-lang/rust-clippy/pull/6782)
 +
 +### Others
 +* Running `cargo clippy` after `cargo check` now works as expected
 +  (`cargo clippy` and `cargo check` no longer shares the same build cache)
 +  [#6687](https://github.com/rust-lang/rust-clippy/pull/6687)
 +* Cargo now re-runs Clippy if arguments after `--` provided to `cargo clippy` are changed.
 +  [#6834](https://github.com/rust-lang/rust-clippy/pull/6834)
 +* Extracted Clippy's `utils` module into the new `clippy_utils` crate
 +  [#6756](https://github.com/rust-lang/rust-clippy/pull/6756)
 +* Clippy lintcheck tool improvements
 +  [#6800](https://github.com/rust-lang/rust-clippy/pull/6800)
 +  [#6735](https://github.com/rust-lang/rust-clippy/pull/6735)
 +  [#6764](https://github.com/rust-lang/rust-clippy/pull/6764)
 +  [#6708](https://github.com/rust-lang/rust-clippy/pull/6708)
 +  [#6780](https://github.com/rust-lang/rust-clippy/pull/6780)
 +  [#6686](https://github.com/rust-lang/rust-clippy/pull/6686)
 +
 +## Rust 1.51
 +
 +Released 2021-03-25
 +
 +[4911ab1...3e41797](https://github.com/rust-lang/rust-clippy/compare/4911ab1...3e41797)
 +
 +### New Lints
 +
 +* [`upper_case_acronyms`]
 +  [#6475](https://github.com/rust-lang/rust-clippy/pull/6475)
 +* [`from_over_into`] [#6476](https://github.com/rust-lang/rust-clippy/pull/6476)
 +* [`case_sensitive_file_extension_comparisons`]
 +  [#6500](https://github.com/rust-lang/rust-clippy/pull/6500)
 +* [`needless_question_mark`]
 +  [#6507](https://github.com/rust-lang/rust-clippy/pull/6507)
 +* [`missing_panics_doc`]
 +  [#6523](https://github.com/rust-lang/rust-clippy/pull/6523)
 +* [`redundant_slicing`]
 +  [#6528](https://github.com/rust-lang/rust-clippy/pull/6528)
 +* [`vec_init_then_push`]
 +  [#6538](https://github.com/rust-lang/rust-clippy/pull/6538)
 +* [`ptr_as_ptr`] [#6542](https://github.com/rust-lang/rust-clippy/pull/6542)
 +* [`collapsible_else_if`] (split out from `collapsible_if`)
 +  [#6544](https://github.com/rust-lang/rust-clippy/pull/6544)
 +* [`inspect_for_each`] [#6577](https://github.com/rust-lang/rust-clippy/pull/6577)
 +* [`manual_filter_map`]
 +  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
 +* [`exhaustive_enums`]
 +  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
 +* [`exhaustive_structs`]
 +  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
 +
 +### Moves and Deprecations
 +
 +* Replace [`find_map`] with [`manual_find_map`]
 +  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
 +* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint
 +  [#6653](https://github.com/rust-lang/rust-clippy/pull/6653)
 +
 +### Enhancements
 +
 +* [`ptr_arg`] Now also suggests to use `&Path` instead of `&PathBuf`
 +  [#6506](https://github.com/rust-lang/rust-clippy/pull/6506)
 +* [`cast_ptr_alignment`] Also lint when the `pointer::cast` method is used
 +  [#6557](https://github.com/rust-lang/rust-clippy/pull/6557)
 +* [`collapsible_match`] Now also deals with `&` and `*` operators in the `match`
 +  scrutinee [#6619](https://github.com/rust-lang/rust-clippy/pull/6619)
 +
 +### False Positive Fixes
 +
 +* [`similar_names`] Ignore underscore prefixed names
 +  [#6403](https://github.com/rust-lang/rust-clippy/pull/6403)
 +* [`print_literal`] and [`write_literal`] No longer lint numeric literals
 +  [#6408](https://github.com/rust-lang/rust-clippy/pull/6408)
 +* [`large_enum_variant`] No longer lints in external macros
 +  [#6485](https://github.com/rust-lang/rust-clippy/pull/6485)
 +* [`empty_enum`] Only lint if `never_type` feature is enabled
 +  [#6513](https://github.com/rust-lang/rust-clippy/pull/6513)
 +* [`field_reassign_with_default`] No longer lints in macros
 +  [#6553](https://github.com/rust-lang/rust-clippy/pull/6553)
 +* [`size_of_in_element_count`] No longer lints when dividing by element size
 +  [#6578](https://github.com/rust-lang/rust-clippy/pull/6578)
 +* [`needless_return`] No longer lints in macros
 +  [#6586](https://github.com/rust-lang/rust-clippy/pull/6586)
 +* [`match_overlapping_arm`] No longer lint when first arm is completely included
 +  in second arm [#6603](https://github.com/rust-lang/rust-clippy/pull/6603)
 +* [`doc_markdown`] Add `WebGL` to the default configuration as an allowed
 +  identifier [#6605](https://github.com/rust-lang/rust-clippy/pull/6605)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`field_reassign_with_default`] Don't expand macro in lint suggestion
 +  [#6531](https://github.com/rust-lang/rust-clippy/pull/6531)
 +* [`match_like_matches_macro`] Strip references in suggestion
 +  [#6532](https://github.com/rust-lang/rust-clippy/pull/6532)
 +* [`single_match`] Suggest `if` over `if let` when possible
 +  [#6574](https://github.com/rust-lang/rust-clippy/pull/6574)
 +* `ref_in_deref` Use parentheses correctly in suggestion
 +  [#6609](https://github.com/rust-lang/rust-clippy/pull/6609)
 +* [`stable_sort_primitive`] Clarify error message
 +  [#6611](https://github.com/rust-lang/rust-clippy/pull/6611)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#6582](https://github.com/rust-lang/rust-clippy/pull/6582)
 +
 +### Documentation Improvements
 +
 +* Improve search performance on the Clippy website and make it possible to
 +  directly search for lints on the GitHub issue tracker
 +  [#6483](https://github.com/rust-lang/rust-clippy/pull/6483)
 +* Clean up `README.md` by removing outdated paragraph
 +  [#6488](https://github.com/rust-lang/rust-clippy/pull/6488)
 +* [`await_holding_refcell_ref`] and [`await_holding_lock`]
 +  [#6585](https://github.com/rust-lang/rust-clippy/pull/6585)
 +* [`as_conversions`] [#6608](https://github.com/rust-lang/rust-clippy/pull/6608)
 +
 +### Others
 +
 +* Clippy now has a [Roadmap] for 2021. If you like to get involved in a bigger
 +  project, take a look at the [Roadmap project page]. All issues listed there
 +  are actively mentored
 +  [#6462](https://github.com/rust-lang/rust-clippy/pull/6462)
 +* The Clippy version number now corresponds to the Rust version number
 +  [#6526](https://github.com/rust-lang/rust-clippy/pull/6526)
 +* Fix oversight which caused Clippy to lint deps in some environments, where
 +  `CLIPPY_TESTS=true` was set somewhere
 +  [#6575](https://github.com/rust-lang/rust-clippy/pull/6575)
 +* Add `cargo dev-lintcheck` tool to the Clippy Dev Tool
 +  [#6469](https://github.com/rust-lang/rust-clippy/pull/6469)
 +
 +[Roadmap]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/proposals/roadmap-2021.md
 +[Roadmap project page]: https://github.com/rust-lang/rust-clippy/projects/3
 +
 +## Rust 1.50
 +
 +Released 2021-02-11
 +
 +[b20d4c1...4bd77a1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4bd77a1)
 +
 +### New Lints
 +
 +* [`suspicious_operation_groupings`] [#6086](https://github.com/rust-lang/rust-clippy/pull/6086)
 +* [`size_of_in_element_count`] [#6394](https://github.com/rust-lang/rust-clippy/pull/6394)
 +* [`unnecessary_wraps`] [#6070](https://github.com/rust-lang/rust-clippy/pull/6070)
 +* [`let_underscore_drop`] [#6305](https://github.com/rust-lang/rust-clippy/pull/6305)
 +* [`collapsible_match`] [#6402](https://github.com/rust-lang/rust-clippy/pull/6402)
 +* [`redundant_else`] [#6330](https://github.com/rust-lang/rust-clippy/pull/6330)
 +* [`zero_sized_map_values`] [#6218](https://github.com/rust-lang/rust-clippy/pull/6218)
 +* [`print_stderr`] [#6367](https://github.com/rust-lang/rust-clippy/pull/6367)
 +* [`string_from_utf8_as_bytes`] [#6134](https://github.com/rust-lang/rust-clippy/pull/6134)
 +
 +### Moves and Deprecations
 +
 +* Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated
 +  as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333)
 +* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panics`
 +  [#6351](https://github.com/rust-lang/rust-clippy/pull/6351)
 +* Move [`map_err_ignore`] to `restriction`
 +  [#6416](https://github.com/rust-lang/rust-clippy/pull/6416)
 +* Move [`await_holding_refcell_ref`] to `pedantic`
 +  [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
 +* Move [`await_holding_lock`] to `pedantic`
 +  [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
 +
 +### Enhancements
 +
 +* Add the `unreadable-literal-lint-fractions` configuration to disable
 +  the `unreadable_literal` lint for fractions
 +  [#6421](https://github.com/rust-lang/rust-clippy/pull/6421)
 +* [`clone_on_copy`]: Now shows the type in the lint message
 +  [#6443](https://github.com/rust-lang/rust-clippy/pull/6443)
 +* [`redundant_pattern_matching`]: Now also lints on `std::task::Poll`
 +  [#6339](https://github.com/rust-lang/rust-clippy/pull/6339)
 +* [`redundant_pattern_matching`]: Additionally also lints on `std::net::IpAddr`
 +  [#6377](https://github.com/rust-lang/rust-clippy/pull/6377)
 +* [`search_is_some`]: Now suggests `contains` instead of `find(foo).is_some()`
 +  [#6119](https://github.com/rust-lang/rust-clippy/pull/6119)
 +* [`clone_double_ref`]: Now prints the reference type in the lint message
 +  [#6442](https://github.com/rust-lang/rust-clippy/pull/6442)
 +* [`modulo_one`]: Now also lints on -1.
 +  [#6360](https://github.com/rust-lang/rust-clippy/pull/6360)
 +* [`empty_loop`]: Now lints no_std crates, too
 +  [#6205](https://github.com/rust-lang/rust-clippy/pull/6205)
 +* [`or_fun_call`]: Now also lints when indexing `HashMap` or `BTreeMap`
 +  [#6267](https://github.com/rust-lang/rust-clippy/pull/6267)
 +* [`wrong_self_convention`]: Now also lints in trait definitions
 +  [#6316](https://github.com/rust-lang/rust-clippy/pull/6316)
 +* [`needless_borrow`]: Print the type in the lint message
 +  [#6449](https://github.com/rust-lang/rust-clippy/pull/6449)
 +
 +[msrv_readme]: https://github.com/rust-lang/rust-clippy#specifying-the-minimum-supported-rust-version
 +
 +### False Positive Fixes
 +
 +* [`manual_range_contains`]: No longer lints in `const fn`
 +  [#6382](https://github.com/rust-lang/rust-clippy/pull/6382)
 +* [`unnecessary_lazy_evaluations`]: No longer lints if closure argument is used
 +  [#6370](https://github.com/rust-lang/rust-clippy/pull/6370)
 +* [`match_single_binding`]: Now ignores cases with `#[cfg()]` macros
 +  [#6435](https://github.com/rust-lang/rust-clippy/pull/6435)
 +* [`match_like_matches_macro`]: No longer lints on arms with attributes
 +  [#6290](https://github.com/rust-lang/rust-clippy/pull/6290)
 +* [`map_clone`]: No longer lints with deref and clone
 +  [#6269](https://github.com/rust-lang/rust-clippy/pull/6269)
 +* [`map_clone`]: No longer lints in the case of &mut
 +  [#6301](https://github.com/rust-lang/rust-clippy/pull/6301)
 +* [`needless_update`]: Now ignores `non_exhaustive` structs
 +  [#6464](https://github.com/rust-lang/rust-clippy/pull/6464)
 +* [`needless_collect`]: No longer lints when a collect is needed multiple times
 +  [#6313](https://github.com/rust-lang/rust-clippy/pull/6313)
 +* [`unnecessary_cast`] No longer lints cfg-dependent types
 +  [#6369](https://github.com/rust-lang/rust-clippy/pull/6369)
 +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]:
 +  Both now ignore enums with frozen variants
 +  [#6110](https://github.com/rust-lang/rust-clippy/pull/6110)
 +* [`field_reassign_with_default`] No longer lint for private fields
 +  [#6537](https://github.com/rust-lang/rust-clippy/pull/6537)
 +
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`vec_box`]: Provide correct type scope suggestion
 +  [#6271](https://github.com/rust-lang/rust-clippy/pull/6271)
 +* [`manual_range_contains`]: Give correct suggestion when using floats
 +  [#6320](https://github.com/rust-lang/rust-clippy/pull/6320)
 +* [`unnecessary_lazy_evaluations`]: Don't always mark suggestion as MachineApplicable
 +  [#6272](https://github.com/rust-lang/rust-clippy/pull/6272)
 +* [`manual_async_fn`]: Improve suggestion formatting
 +  [#6294](https://github.com/rust-lang/rust-clippy/pull/6294)
 +* [`unnecessary_cast`]: Fix incorrectly formatted float literal suggestion
 +  [#6362](https://github.com/rust-lang/rust-clippy/pull/6362)
 +
 +### ICE Fixes
 +
 +* Fix a crash in [`from_iter_instead_of_collect`]
 +  [#6304](https://github.com/rust-lang/rust-clippy/pull/6304)
 +* Fix a silent crash when parsing doc comments in [`needless_doctest_main`]
 +  [#6458](https://github.com/rust-lang/rust-clippy/pull/6458)
 +
 +### Documentation Improvements
 +
 +* The lint website search has been improved ([#6477](https://github.com/rust-lang/rust-clippy/pull/6477)):
 +  * Searching for lints with dashes and spaces is possible now. For example
 +    `missing-errors-doc` and `missing errors doc` are now valid aliases for lint names
 +  * Improved fuzzy search in lint descriptions
 +* Various README improvements
 +  [#6287](https://github.com/rust-lang/rust-clippy/pull/6287)
 +* Add known problems to [`comparison_chain`] documentation
 +  [#6390](https://github.com/rust-lang/rust-clippy/pull/6390)
 +* Fix example used in [`cargo_common_metadata`]
 +  [#6293](https://github.com/rust-lang/rust-clippy/pull/6293)
 +* Improve [`map_clone`] documentation
 +  [#6340](https://github.com/rust-lang/rust-clippy/pull/6340)
 +
 +### Others
 +
 +* You can now tell Clippy about the MSRV your project supports. Please refer to
 +  the specific README section to learn more about MSRV support [here][msrv_readme]
 +  [#6201](https://github.com/rust-lang/rust-clippy/pull/6201)
 +* Add `--no-deps` option to avoid running on path dependencies in workspaces
 +  [#6188](https://github.com/rust-lang/rust-clippy/pull/6188)
 +
 +## Rust 1.49
 +
 +Released 2020-12-31
 +
 +[e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1)
 +
 +### New Lints
 +
 +* [`field_reassign_with_default`] [#5911](https://github.com/rust-lang/rust-clippy/pull/5911)
 +* [`await_holding_refcell_ref`] [#6029](https://github.com/rust-lang/rust-clippy/pull/6029)
 +* [`disallowed_methods`] [#6081](https://github.com/rust-lang/rust-clippy/pull/6081)
 +* [`inline_asm_x86_att_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 +* [`inline_asm_x86_intel_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 +* [`from_iter_instead_of_collect`] [#6101](https://github.com/rust-lang/rust-clippy/pull/6101)
 +* [`mut_mutex_lock`] [#6103](https://github.com/rust-lang/rust-clippy/pull/6103)
 +* [`single_element_loop`] [#6109](https://github.com/rust-lang/rust-clippy/pull/6109)
 +* [`manual_unwrap_or`] [#6123](https://github.com/rust-lang/rust-clippy/pull/6123)
 +* [`large_types_passed_by_value`] [#6135](https://github.com/rust-lang/rust-clippy/pull/6135)
 +* [`result_unit_err`] [#6157](https://github.com/rust-lang/rust-clippy/pull/6157)
 +* [`ref_option_ref`] [#6165](https://github.com/rust-lang/rust-clippy/pull/6165)
 +* [`manual_range_contains`] [#6177](https://github.com/rust-lang/rust-clippy/pull/6177)
 +* [`unusual_byte_groupings`] [#6183](https://github.com/rust-lang/rust-clippy/pull/6183)
 +* [`comparison_to_empty`] [#6226](https://github.com/rust-lang/rust-clippy/pull/6226)
 +* [`map_collect_result_unit`] [#6227](https://github.com/rust-lang/rust-clippy/pull/6227)
 +* [`manual_ok_or`] [#6233](https://github.com/rust-lang/rust-clippy/pull/6233)
 +
 +### Moves and Deprecations
 +
 +* Rename `single_char_push_str` to [`single_char_add_str`]
 +  [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 +* Rename `zero_width_space` to [`invisible_characters`]
 +  [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
 +* Deprecate `drop_bounds` (uplifted)
 +  [#6111](https://github.com/rust-lang/rust-clippy/pull/6111)
 +* Move [`string_lit_as_bytes`] to `nursery`
 +  [#6117](https://github.com/rust-lang/rust-clippy/pull/6117)
 +* Move [`rc_buffer`] to `restriction`
 +  [#6128](https://github.com/rust-lang/rust-clippy/pull/6128)
 +
 +### Enhancements
 +
 +* [`manual_memcpy`]: Also lint when there are loop counters (and produce a
 +  reliable suggestion)
 +  [#5727](https://github.com/rust-lang/rust-clippy/pull/5727)
 +* [`single_char_add_str`]: Also lint on `String::insert_str`
 +  [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 +* [`invisible_characters`]: Also lint the characters `\u{AD}` and `\u{2060}`
 +  [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
 +* [`eq_op`]: Also lint on the `assert_*!` macro family
 +  [#6167](https://github.com/rust-lang/rust-clippy/pull/6167)
 +* [`items_after_statements`]: Also lint in local macro expansions
 +  [#6176](https://github.com/rust-lang/rust-clippy/pull/6176)
 +* [`unnecessary_cast`]: Also lint casts on integer and float literals
 +  [#6187](https://github.com/rust-lang/rust-clippy/pull/6187)
 +* [`manual_unwrap_or`]: Also lint `Result::unwrap_or`
 +  [#6190](https://github.com/rust-lang/rust-clippy/pull/6190)
 +* [`match_like_matches_macro`]: Also lint when `match` has more than two arms
 +  [#6216](https://github.com/rust-lang/rust-clippy/pull/6216)
 +* [`integer_arithmetic`]: Better handle `/` an `%` operators
 +  [#6229](https://github.com/rust-lang/rust-clippy/pull/6229)
 +
 +### False Positive Fixes
 +
 +* [`needless_lifetimes`]: Bail out if the function has a `where` clause with the
 +  lifetime [#5978](https://github.com/rust-lang/rust-clippy/pull/5978)
 +* [`explicit_counter_loop`]: No longer lints, when loop counter is used after it
 +  is incremented [#6076](https://github.com/rust-lang/rust-clippy/pull/6076)
 +* [`or_fun_call`]: Revert changes addressing the handling of `const fn`
 +  [#6077](https://github.com/rust-lang/rust-clippy/pull/6077)
 +* [`needless_range_loop`]: No longer lints, when the iterable is used in the
 +  range [#6102](https://github.com/rust-lang/rust-clippy/pull/6102)
 +* [`inconsistent_digit_grouping`]: Fix bug when using floating point exponent
 +  [#6104](https://github.com/rust-lang/rust-clippy/pull/6104)
 +* [`mistyped_literal_suffixes`]: No longer lints on the fractional part of a
 +  float (e.g. `713.32_64`)
 +  [#6114](https://github.com/rust-lang/rust-clippy/pull/6114)
 +* [`invalid_regex`]: No longer lint on unicode characters within `bytes::Regex`
 +  [#6132](https://github.com/rust-lang/rust-clippy/pull/6132)
 +* [`boxed_local`]: No longer lints on `extern fn` arguments
 +  [#6133](https://github.com/rust-lang/rust-clippy/pull/6133)
 +* [`needless_lifetimes`]: Fix regression, where lifetime is used in `where`
 +  clause [#6198](https://github.com/rust-lang/rust-clippy/pull/6198)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`unnecessary_sort_by`]: Avoid dereferencing the suggested closure parameter
 +  [#6078](https://github.com/rust-lang/rust-clippy/pull/6078)
 +* [`needless_arbitrary_self_type`]: Correctly handle expanded code
 +  [#6093](https://github.com/rust-lang/rust-clippy/pull/6093)
 +* [`useless_format`]: Preserve raw strings in suggestion
 +  [#6151](https://github.com/rust-lang/rust-clippy/pull/6151)
 +* [`empty_loop`]: Suggest alternatives
 +  [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
 +* [`borrowed_box`]: Correctly add parentheses in suggestion
 +  [#6200](https://github.com/rust-lang/rust-clippy/pull/6200)
 +* [`unused_unit`]: Improve suggestion formatting
 +  [#6247](https://github.com/rust-lang/rust-clippy/pull/6247)
 +
 +### Documentation Improvements
 +
 +* Some doc improvements:
 +    * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090)
 +    * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
 +* [`doc_markdown`]: Document problematic link text style
 +  [#6107](https://github.com/rust-lang/rust-clippy/pull/6107)
 +
 +## Rust 1.48
 +
 +Released 2020-11-19
 +
 +[09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88)
 +
 +### New lints
 +
 +* [`self_assignment`] [#5894](https://github.com/rust-lang/rust-clippy/pull/5894)
 +* [`unnecessary_lazy_evaluations`] [#5720](https://github.com/rust-lang/rust-clippy/pull/5720)
 +* [`manual_strip`] [#6038](https://github.com/rust-lang/rust-clippy/pull/6038)
 +* [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998)
 +* [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044)
 +* `to_string_in_display` [#5831](https://github.com/rust-lang/rust-clippy/pull/5831)
 +* `single_char_push_str` [#5881](https://github.com/rust-lang/rust-clippy/pull/5881)
 +
 +### Moves and Deprecations
 +
 +* Downgrade [`verbose_bit_mask`] to pedantic
 +  [#6036](https://github.com/rust-lang/rust-clippy/pull/6036)
 +
 +### Enhancements
 +
 +* Extend [`precedence`] to handle chains of methods combined with unary negation
 +  [#5928](https://github.com/rust-lang/rust-clippy/pull/5928)
 +* [`useless_vec`]: add a configuration value for the maximum allowed size on the stack
 +  [#5907](https://github.com/rust-lang/rust-clippy/pull/5907)
 +* [`suspicious_arithmetic_impl`]: extend to implementations of `BitAnd`, `BitOr`, `BitXor`, `Rem`, `Shl`, and `Shr`
 +  [#5884](https://github.com/rust-lang/rust-clippy/pull/5884)
 +* `invalid_atomic_ordering`: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update`
 +  [#6025](https://github.com/rust-lang/rust-clippy/pull/6025)
 +* Avoid [`redundant_pattern_matching`] triggering in macros
 +  [#6069](https://github.com/rust-lang/rust-clippy/pull/6069)
 +* [`option_if_let_else`]: distinguish pure from impure `else` expressions
 +  [#5937](https://github.com/rust-lang/rust-clippy/pull/5937)
 +* [`needless_doctest_main`]: parse doctests instead of using textual search
 +  [#5912](https://github.com/rust-lang/rust-clippy/pull/5912)
 +* [`wildcard_imports`]: allow `prelude` to appear in any segment of an import
 +  [#5929](https://github.com/rust-lang/rust-clippy/pull/5929)
 +* Re-enable [`len_zero`] for ranges now that `range_is_empty` is stable
 +  [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
 +* [`option_as_ref_deref`]: catch fully-qualified calls to `Deref::deref` and `DerefMut::deref_mut`
 +  [#5933](https://github.com/rust-lang/rust-clippy/pull/5933)
 +
 +### False Positive Fixes
 +
 +* [`useless_attribute`]: permit allowing [`wildcard_imports`] and [`enum_glob_use`]
 +  [#5994](https://github.com/rust-lang/rust-clippy/pull/5994)
 +* [`transmute_ptr_to_ptr`]: avoid suggesting dereferencing raw pointers in const contexts
 +  [#5999](https://github.com/rust-lang/rust-clippy/pull/5999)
 +* [`redundant_closure_call`]: take into account usages of the closure in nested functions and closures
 +  [#5920](https://github.com/rust-lang/rust-clippy/pull/5920)
 +* Fix false positive in [`borrow_interior_mutable_const`] when referencing a field behind a pointer
 +  [#5949](https://github.com/rust-lang/rust-clippy/pull/5949)
 +* [`doc_markdown`]: allow using "GraphQL" without backticks
 +  [#5996](https://github.com/rust-lang/rust-clippy/pull/5996)
 +* `to_string_in_display`: avoid linting when calling `to_string()` on anything that is not `self`
 +  [#5971](https://github.com/rust-lang/rust-clippy/pull/5971)
 +* [`indexing_slicing`] and [`out_of_bounds_indexing`] treat references to arrays as arrays
 +  [#6034](https://github.com/rust-lang/rust-clippy/pull/6034)
 +* [`should_implement_trait`]: ignore methods with lifetime parameters
 +  [#5725](https://github.com/rust-lang/rust-clippy/pull/5725)
 +* [`needless_return`]: avoid linting if a temporary borrows a local variable
 +  [#5903](https://github.com/rust-lang/rust-clippy/pull/5903)
 +* Restrict [`unnecessary_sort_by`] to non-reference, Copy types
 +  [#6006](https://github.com/rust-lang/rust-clippy/pull/6006)
 +* Avoid suggesting `from_bits`/`to_bits` in const contexts in [`transmute_int_to_float`]
 +  [#5919](https://github.com/rust-lang/rust-clippy/pull/5919)
 +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]: improve detection of interior mutable types
 +  [#6046](https://github.com/rust-lang/rust-clippy/pull/6046)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`let_and_return`]: add a cast to the suggestion when the return expression has adjustments
 +  [#5946](https://github.com/rust-lang/rust-clippy/pull/5946)
 +* [`useless_conversion`]: show the type in the error message
 +  [#6035](https://github.com/rust-lang/rust-clippy/pull/6035)
 +* [`unnecessary_mut_passed`]: discriminate between functions and methods in the error message
 +  [#5892](https://github.com/rust-lang/rust-clippy/pull/5892)
 +* [`float_cmp`] and [`float_cmp_const`]: change wording to make margin of error less ambiguous
 +  [#6043](https://github.com/rust-lang/rust-clippy/pull/6043)
 +* [`default_trait_access`]: do not use unnecessary type parameters in the suggestion
 +  [#5993](https://github.com/rust-lang/rust-clippy/pull/5993)
 +* [`collapsible_if`]: don't use expanded code in the suggestion
 +  [#5992](https://github.com/rust-lang/rust-clippy/pull/5992)
 +* Do not suggest empty format strings in [`print_with_newline`] and [`write_with_newline`]
 +  [#6042](https://github.com/rust-lang/rust-clippy/pull/6042)
 +* [`unit_arg`]: improve the readability of the suggestion
 +  [#5931](https://github.com/rust-lang/rust-clippy/pull/5931)
 +* [`stable_sort_primitive`]: print the type that is being sorted in the lint message
 +  [#5935](https://github.com/rust-lang/rust-clippy/pull/5935)
 +* Show line count and max lines in [`too_many_lines`] lint message
 +  [#6009](https://github.com/rust-lang/rust-clippy/pull/6009)
 +* Keep parentheses in the suggestion of [`useless_conversion`] where applicable
 +  [#5900](https://github.com/rust-lang/rust-clippy/pull/5900)
 +* [`option_map_unit_fn`] and [`result_map_unit_fn`]: print the unit type `()` explicitly
 +  [#6024](https://github.com/rust-lang/rust-clippy/pull/6024)
 +* [`redundant_allocation`]: suggest replacing `Rc<Box<T>>` with `Rc<T>`
 +  [#5899](https://github.com/rust-lang/rust-clippy/pull/5899)
 +* Make lint messages adhere to rustc dev guide conventions
 +  [#5893](https://github.com/rust-lang/rust-clippy/pull/5893)
 +
 +### ICE Fixes
 +
 +* Fix ICE in [`repeat_once`]
 +  [#5948](https://github.com/rust-lang/rust-clippy/pull/5948)
 +
 +### Documentation Improvements
 +
 +* [`mutable_key_type`]: explain potential for false positives when the interior mutable type is not accessed in the `Hash` implementation
 +  [#6019](https://github.com/rust-lang/rust-clippy/pull/6019)
 +* [`unnecessary_mut_passed`]: fix typo
 +  [#5913](https://github.com/rust-lang/rust-clippy/pull/5913)
 +* Add example of false positive to [`ptr_arg`] docs.
 +  [#5885](https://github.com/rust-lang/rust-clippy/pull/5885)
 +* [`box_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection), [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
 +  [#6023](https://github.com/rust-lang/rust-clippy/pull/6023)
 +
 +## Rust 1.47
 +
 +Released 2020-10-08
 +
 +[c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400)
 +
 +### New lints
 +
 +* [`derive_ord_xor_partial_ord`] [#5848](https://github.com/rust-lang/rust-clippy/pull/5848)
 +* [`trait_duplication_in_bounds`] [#5852](https://github.com/rust-lang/rust-clippy/pull/5852)
 +* [`map_identity`] [#5694](https://github.com/rust-lang/rust-clippy/pull/5694)
 +* [`unit_return_expecting_ord`] [#5737](https://github.com/rust-lang/rust-clippy/pull/5737)
 +* [`pattern_type_mismatch`] [#4841](https://github.com/rust-lang/rust-clippy/pull/4841)
 +* [`repeat_once`] [#5773](https://github.com/rust-lang/rust-clippy/pull/5773)
 +* [`same_item_push`] [#5825](https://github.com/rust-lang/rust-clippy/pull/5825)
 +* [`needless_arbitrary_self_type`] [#5869](https://github.com/rust-lang/rust-clippy/pull/5869)
 +* [`match_like_matches_macro`] [#5769](https://github.com/rust-lang/rust-clippy/pull/5769)
 +* [`stable_sort_primitive`] [#5809](https://github.com/rust-lang/rust-clippy/pull/5809)
 +* [`blanket_clippy_restriction_lints`] [#5750](https://github.com/rust-lang/rust-clippy/pull/5750)
 +* [`option_if_let_else`] [#5301](https://github.com/rust-lang/rust-clippy/pull/5301)
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`regex_macro`] lint
 +  [#5760](https://github.com/rust-lang/rust-clippy/pull/5760)
 +* Move [`range_minus_one`] to `pedantic`
 +  [#5752](https://github.com/rust-lang/rust-clippy/pull/5752)
 +
 +### Enhancements
 +
 +* Improve [`needless_collect`] by catching `collect` calls followed by `iter` or `into_iter` calls
 +  [#5837](https://github.com/rust-lang/rust-clippy/pull/5837)
 +* [`panic`], [`todo`], [`unimplemented`] and [`unreachable`] now detect calls with formatting
 +  [#5811](https://github.com/rust-lang/rust-clippy/pull/5811)
 +* Detect more cases of [`suboptimal_flops`] and [`imprecise_flops`]
 +  [#5443](https://github.com/rust-lang/rust-clippy/pull/5443)
 +* Handle asymmetrical implementations of `PartialEq` in [`cmp_owned`]
 +  [#5701](https://github.com/rust-lang/rust-clippy/pull/5701)
 +* Make it possible to allow [`unsafe_derive_deserialize`]
 +  [#5870](https://github.com/rust-lang/rust-clippy/pull/5870)
 +* Catch `ord.min(a).max(b)` where a < b in [`min_max`]
 +  [#5871](https://github.com/rust-lang/rust-clippy/pull/5871)
 +* Make [`clone_on_copy`] suggestion machine applicable
 +  [#5745](https://github.com/rust-lang/rust-clippy/pull/5745)
 +* Enable [`len_zero`] on ranges now that `is_empty` is stable on them
 +  [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
 +
 +### False Positive Fixes
 +
 +* Avoid triggering [`or_fun_call`] with const fns that take no arguments
 +  [#5889](https://github.com/rust-lang/rust-clippy/pull/5889)
 +* Fix [`redundant_closure_call`] false positive for closures that have multiple calls
 +  [#5800](https://github.com/rust-lang/rust-clippy/pull/5800)
 +* Don't lint cases involving `ManuallyDrop` in [`redundant_clone`]
 +  [#5824](https://github.com/rust-lang/rust-clippy/pull/5824)
 +* Treat a single expression the same as a single statement in the 2nd arm of a match in [`single_match_else`]
 +  [#5771](https://github.com/rust-lang/rust-clippy/pull/5771)
 +* Don't trigger [`unnested_or_patterns`] if the feature `or_patterns` is not enabled
 +  [#5758](https://github.com/rust-lang/rust-clippy/pull/5758)
 +* Avoid linting if key borrows in [`unnecessary_sort_by`]
 +  [#5756](https://github.com/rust-lang/rust-clippy/pull/5756)
 +* Consider `Try` impl for `Poll` when generating suggestions in [`try_err`]
 +  [#5857](https://github.com/rust-lang/rust-clippy/pull/5857)
 +* Take input lifetimes into account in `manual_async_fn`
 +  [#5859](https://github.com/rust-lang/rust-clippy/pull/5859)
 +* Fix multiple false positives in [`type_repetition_in_bounds`] and add a configuration option
 +  [#5761](https://github.com/rust-lang/rust-clippy/pull/5761)
 +* Limit the [`suspicious_arithmetic_impl`] lint to one binary operation
 +  [#5820](https://github.com/rust-lang/rust-clippy/pull/5820)
 +
 +### Suggestion Fixes/Improvements
 +
 +* Improve readability of [`shadow_unrelated`] suggestion by truncating the RHS snippet
 +  [#5788](https://github.com/rust-lang/rust-clippy/pull/5788)
 +* Suggest `filter_map` instead of `flat_map` when mapping to `Option` in [`map_flatten`]
 +  [#5846](https://github.com/rust-lang/rust-clippy/pull/5846)
 +* Ensure suggestion is shown correctly for long method call chains in [`iter_nth_zero`]
 +  [#5793](https://github.com/rust-lang/rust-clippy/pull/5793)
 +* Drop borrow operator in suggestions of [`redundant_pattern_matching`]
 +  [#5815](https://github.com/rust-lang/rust-clippy/pull/5815)
 +* Add suggestion for [`iter_skip_next`]
 +  [#5843](https://github.com/rust-lang/rust-clippy/pull/5843)
 +* Improve [`collapsible_if`] fix suggestion
 +  [#5732](https://github.com/rust-lang/rust-clippy/pull/5732)
 +
 +### ICE Fixes
 +
 +* Fix ICE caused by [`needless_collect`]
 +  [#5877](https://github.com/rust-lang/rust-clippy/pull/5877)
 +* Fix ICE caused by [`unnested_or_patterns`]
 +  [#5784](https://github.com/rust-lang/rust-clippy/pull/5784)
 +
 +### Documentation Improvements
 +
 +* Fix grammar of [`await_holding_lock`] documentation
 +  [#5748](https://github.com/rust-lang/rust-clippy/pull/5748)
 +
 +### Others
 +
 +* Make lints adhere to the rustc dev guide
 +  [#5888](https://github.com/rust-lang/rust-clippy/pull/5888)
 +
 +## Rust 1.46
 +
 +Released 2020-08-27
 +
 +[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa)
 +
 +### New lints
 +
 +* [`unnested_or_patterns`] [#5378](https://github.com/rust-lang/rust-clippy/pull/5378)
 +* [`iter_next_slice`] [#5597](https://github.com/rust-lang/rust-clippy/pull/5597)
 +* [`unnecessary_sort_by`] [#5623](https://github.com/rust-lang/rust-clippy/pull/5623)
 +* [`vec_resize_to_zero`] [#5637](https://github.com/rust-lang/rust-clippy/pull/5637)
 +
 +### Moves and Deprecations
 +
 +* Move [`cast_ptr_alignment`] to pedantic [#5667](https://github.com/rust-lang/rust-clippy/pull/5667)
 +
 +### Enhancements
 +
 +* Improve [`mem_replace_with_uninit`] lint [#5695](https://github.com/rust-lang/rust-clippy/pull/5695)
 +
 +### False Positive Fixes
 +
 +* [`len_zero`]: Avoid linting ranges when the `range_is_empty` feature is not enabled
 +  [#5656](https://github.com/rust-lang/rust-clippy/pull/5656)
 +* [`let_and_return`]: Don't lint if a temporary borrow is involved
 +  [#5680](https://github.com/rust-lang/rust-clippy/pull/5680)
 +* [`reversed_empty_ranges`]: Avoid linting `N..N` in for loop arguments in
 +  [#5692](https://github.com/rust-lang/rust-clippy/pull/5692)
 +* [`if_same_then_else`]: Don't assume multiplication is always commutative
 +  [#5702](https://github.com/rust-lang/rust-clippy/pull/5702)
 +* [`disallowed_names`]: Remove `bar` from the default configuration
 +  [#5712](https://github.com/rust-lang/rust-clippy/pull/5712)
 +* [`redundant_pattern_matching`]: Avoid suggesting non-`const fn` calls in const contexts
 +  [#5724](https://github.com/rust-lang/rust-clippy/pull/5724)
 +
 +### Suggestion Fixes/Improvements
 +
 +* Fix suggestion of [`unit_arg`] lint, so that it suggest semantic equivalent code
 +  [#4455](https://github.com/rust-lang/rust-clippy/pull/4455)
 +* Add auto applicable suggestion to [`macro_use_imports`]
 +  [#5279](https://github.com/rust-lang/rust-clippy/pull/5279)
 +
 +### ICE Fixes
 +
 +* Fix ICE in the `consts` module of Clippy [#5709](https://github.com/rust-lang/rust-clippy/pull/5709)
 +
 +### Documentation Improvements
 +
 +* Improve code examples across multiple lints [#5664](https://github.com/rust-lang/rust-clippy/pull/5664)
 +
 +### Others
 +
 +* Introduce a `--rustc` flag to `clippy-driver`, which turns `clippy-driver`
 +  into `rustc` and passes all the given arguments to `rustc`. This is especially
 +  useful for tools that need the `rustc` version Clippy was compiled with,
 +  instead of the Clippy version. E.g. `clippy-driver --rustc --version` will
 +  print the output of `rustc --version`.
 +  [#5178](https://github.com/rust-lang/rust-clippy/pull/5178)
 +* New issue templates now make it easier to complain if Clippy is too annoying
 +  or not annoying enough! [#5735](https://github.com/rust-lang/rust-clippy/pull/5735)
 +
 +## Rust 1.45
 +
 +Released 2020-07-16
 +
 +[891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1)
 +
 +### New lints
 +
 +* [`match_wildcard_for_single_variants`] [#5582](https://github.com/rust-lang/rust-clippy/pull/5582)
 +* [`unsafe_derive_deserialize`] [#5493](https://github.com/rust-lang/rust-clippy/pull/5493)
 +* [`if_let_mutex`] [#5332](https://github.com/rust-lang/rust-clippy/pull/5332)
 +* [`mismatched_target_os`] [#5506](https://github.com/rust-lang/rust-clippy/pull/5506)
 +* [`await_holding_lock`] [#5439](https://github.com/rust-lang/rust-clippy/pull/5439)
 +* [`match_on_vec_items`] [#5522](https://github.com/rust-lang/rust-clippy/pull/5522)
 +* [`manual_async_fn`] [#5576](https://github.com/rust-lang/rust-clippy/pull/5576)
 +* [`reversed_empty_ranges`] [#5583](https://github.com/rust-lang/rust-clippy/pull/5583)
 +* [`manual_non_exhaustive`] [#5550](https://github.com/rust-lang/rust-clippy/pull/5550)
 +
 +### Moves and Deprecations
 +
 +* Downgrade [`match_bool`] to pedantic [#5408](https://github.com/rust-lang/rust-clippy/pull/5408)
 +* Downgrade [`match_wild_err_arm`] to pedantic and update help messages. [#5622](https://github.com/rust-lang/rust-clippy/pull/5622)
 +* Downgrade [`useless_let_if_seq`] to nursery. [#5599](https://github.com/rust-lang/rust-clippy/pull/5599)
 +* Generalize `option_and_then_some` and rename to [`bind_instead_of_map`]. [#5529](https://github.com/rust-lang/rust-clippy/pull/5529)
 +* Rename `identity_conversion` to [`useless_conversion`]. [#5568](https://github.com/rust-lang/rust-clippy/pull/5568)
 +* Merge `block_in_if_condition_expr` and `block_in_if_condition_stmt` into [`blocks_in_if_conditions`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_map_unwrap_or`, `option_map_unwrap_or_else` and `result_map_unwrap_or_else` into [`map_unwrap_or`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_unwrap_used` and `result_unwrap_used` into [`unwrap_used`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_expect_used` and `result_expect_used` into [`expect_used`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `for_loop_over_option` and `for_loop_over_result` into [`for_loops_over_fallibles`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +
 +### Enhancements
 +
 +* Avoid running cargo lints when not enabled to improve performance. [#5505](https://github.com/rust-lang/rust-clippy/pull/5505)
 +* Extend [`useless_conversion`] with `TryFrom` and `TryInto`. [#5631](https://github.com/rust-lang/rust-clippy/pull/5631)
 +* Lint also in type parameters and where clauses in [`unused_unit`]. [#5592](https://github.com/rust-lang/rust-clippy/pull/5592)
 +* Do not suggest deriving `Default` in [`new_without_default`]. [#5616](https://github.com/rust-lang/rust-clippy/pull/5616)
 +
 +### False Positive Fixes
 +
 +* [`while_let_on_iterator`] [#5525](https://github.com/rust-lang/rust-clippy/pull/5525)
 +* [`empty_line_after_outer_attr`] [#5609](https://github.com/rust-lang/rust-clippy/pull/5609)
 +* [`unnecessary_unwrap`] [#5558](https://github.com/rust-lang/rust-clippy/pull/5558)
 +* [`comparison_chain`] [#5596](https://github.com/rust-lang/rust-clippy/pull/5596)
 +* Don't trigger [`used_underscore_binding`] in await desugaring. [#5535](https://github.com/rust-lang/rust-clippy/pull/5535)
 +* Don't trigger [`borrowed_box`] on mutable references. [#5491](https://github.com/rust-lang/rust-clippy/pull/5491)
 +* Allow `1 << 0` in [`identity_op`]. [#5602](https://github.com/rust-lang/rust-clippy/pull/5602)
 +* Allow `use super::*;` glob imports in [`wildcard_imports`]. [#5564](https://github.com/rust-lang/rust-clippy/pull/5564)
 +* Whitelist more words in [`doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611)
 +* Skip dev and build deps in [`multiple_crate_versions`]. [#5636](https://github.com/rust-lang/rust-clippy/pull/5636)
 +* Honor `allow` attribute on arguments in [`ptr_arg`]. [#5647](https://github.com/rust-lang/rust-clippy/pull/5647)
 +* Honor lint level attributes for [`redundant_field_names`], [`just_underscores_and_digits`], [`many_single_char_names`]
 +and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/5651)
 +* Ignore calls to `len` in [`or_fun_call`]. [#4429](https://github.com/rust-lang/rust-clippy/pull/4429)
 +
 +### Suggestion Improvements
 +
 +* Simplify suggestions in [`manual_memcpy`]. [#5536](https://github.com/rust-lang/rust-clippy/pull/5536)
 +* Fix suggestion in [`redundant_pattern_matching`] for macros. [#5511](https://github.com/rust-lang/rust-clippy/pull/5511)
 +* Avoid suggesting `copied()` for mutable references in [`map_clone`]. [#5530](https://github.com/rust-lang/rust-clippy/pull/5530)
 +* Improve help message for [`clone_double_ref`]. [#5547](https://github.com/rust-lang/rust-clippy/pull/5547)
 +
 +### ICE Fixes
 +
 +* Fix ICE caused in unwrap module. [#5590](https://github.com/rust-lang/rust-clippy/pull/5590)
 +* Fix ICE on rustc test issue-69020-assoc-const-arith-overflow.rs [#5499](https://github.com/rust-lang/rust-clippy/pull/5499)
 +
 +### Documentation
 +
 +* Clarify the documentation of [`unnecessary_mut_passed`]. [#5639](https://github.com/rust-lang/rust-clippy/pull/5639)
 +* Extend example for [`unneeded_field_pattern`]. [#5541](https://github.com/rust-lang/rust-clippy/pull/5541)
 +
 +## Rust 1.44
 +
 +Released 2020-06-04
 +
 +[204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8)
 +
 +### New lints
 +
 +* [`explicit_deref_methods`] [#5226](https://github.com/rust-lang/rust-clippy/pull/5226)
 +* [`implicit_saturating_sub`] [#5427](https://github.com/rust-lang/rust-clippy/pull/5427)
 +* [`macro_use_imports`] [#5230](https://github.com/rust-lang/rust-clippy/pull/5230)
 +* [`verbose_file_reads`] [#5272](https://github.com/rust-lang/rust-clippy/pull/5272)
 +* [`future_not_send`] [#5423](https://github.com/rust-lang/rust-clippy/pull/5423)
 +* [`redundant_pub_crate`] [#5319](https://github.com/rust-lang/rust-clippy/pull/5319)
 +* [`large_const_arrays`] [#5248](https://github.com/rust-lang/rust-clippy/pull/5248)
 +* [`result_map_or_into_option`] [#5415](https://github.com/rust-lang/rust-clippy/pull/5415)
 +* [`redundant_allocation`] [#5349](https://github.com/rust-lang/rust-clippy/pull/5349)
 +* [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
 +* [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
 +
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380)
 +* Move [`cognitive_complexity`] to nursery [#5428](https://github.com/rust-lang/rust-clippy/pull/5428)
 +* Move [`useless_transmute`] to nursery [#5364](https://github.com/rust-lang/rust-clippy/pull/5364)
 +* Downgrade [`inefficient_to_string`] to pedantic [#5412](https://github.com/rust-lang/rust-clippy/pull/5412)
 +* Downgrade [`option_option`] to pedantic [#5401](https://github.com/rust-lang/rust-clippy/pull/5401)
 +* Downgrade [`unreadable_literal`] to pedantic [#5419](https://github.com/rust-lang/rust-clippy/pull/5419)
 +* Downgrade [`let_unit_value`] to pedantic [#5409](https://github.com/rust-lang/rust-clippy/pull/5409)
 +* Downgrade [`trivially_copy_pass_by_ref`] to pedantic [#5410](https://github.com/rust-lang/rust-clippy/pull/5410)
 +* Downgrade [`implicit_hasher`] to pedantic [#5411](https://github.com/rust-lang/rust-clippy/pull/5411)
 +
 +### Enhancements
 +
 +* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to
 +  auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363)
 +* Make [`redundant_clone`] also trigger on cases where the cloned value is not
 +  consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304)
 +* Expand [`integer_arithmetic`] to also disallow bit-shifting [#5430](https://github.com/rust-lang/rust-clippy/pull/5430)
 +* [`option_as_ref_deref`] now detects more deref cases [#5425](https://github.com/rust-lang/rust-clippy/pull/5425)
 +* [`large_enum_variant`] now report the sizes of the largest and second-largest variants [#5466](https://github.com/rust-lang/rust-clippy/pull/5466)
 +* [`bool_comparison`] now also checks for inequality comparisons that can be
 +  written more concisely [#5365](https://github.com/rust-lang/rust-clippy/pull/5365)
 +* Expand [`clone_on_copy`] to work in method call arguments as well [#5441](https://github.com/rust-lang/rust-clippy/pull/5441)
 +* [`redundant_pattern_matching`] now also handles `while let` [#5483](https://github.com/rust-lang/rust-clippy/pull/5483)
 +* [`integer_arithmetic`] now also lints references of integers [#5329](https://github.com/rust-lang/rust-clippy/pull/5329)
 +* Expand [`float_cmp_const`] to also work on arrays [#5345](https://github.com/rust-lang/rust-clippy/pull/5345)
 +* Trigger [`map_flatten`] when map is called on an `Option` [#5473](https://github.com/rust-lang/rust-clippy/pull/5473)
 +
 +### False Positive Fixes
 +
 +* [`many_single_char_names`] [#5468](https://github.com/rust-lang/rust-clippy/pull/5468)
 +* [`should_implement_trait`] [#5437](https://github.com/rust-lang/rust-clippy/pull/5437)
 +* [`unused_self`] [#5387](https://github.com/rust-lang/rust-clippy/pull/5387)
 +* [`redundant_clone`] [#5453](https://github.com/rust-lang/rust-clippy/pull/5453)
 +* [`precedence`] [#5445](https://github.com/rust-lang/rust-clippy/pull/5445)
 +* [`suspicious_op_assign_impl`] [#5424](https://github.com/rust-lang/rust-clippy/pull/5424)
 +* [`needless_lifetimes`] [#5293](https://github.com/rust-lang/rust-clippy/pull/5293)
 +* [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287)
 +* [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451)
 +
 +
 +### Suggestion Improvements
 +
 +* Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481)
 +* Improve the suggested placeholder in [`option_map_unit_fn`] [#5292](https://github.com/rust-lang/rust-clippy/pull/5292)
 +* Improve suggestion for [`match_single_binding`] when triggered inside a closure [#5350](https://github.com/rust-lang/rust-clippy/pull/5350)
 +
 +### ICE Fixes
 +
 +* Handle the unstable `trivial_bounds` feature [#5296](https://github.com/rust-lang/rust-clippy/pull/5296)
 +* `shadow_*` lints [#5297](https://github.com/rust-lang/rust-clippy/pull/5297)
 +
 +### Documentation
 +
 +* Fix documentation generation for configurable lints [#5353](https://github.com/rust-lang/rust-clippy/pull/5353)
 +* Update documentation for [`new_ret_no_self`] [#5448](https://github.com/rust-lang/rust-clippy/pull/5448)
 +* The documentation for [`option_option`] now suggest using a tri-state enum [#5403](https://github.com/rust-lang/rust-clippy/pull/5403)
 +* Fix bit mask example in [`verbose_bit_mask`] documentation [#5454](https://github.com/rust-lang/rust-clippy/pull/5454)
 +* [`wildcard_imports`] documentation now mentions that `use ...::prelude::*` is
 +  not linted [#5312](https://github.com/rust-lang/rust-clippy/pull/5312)
 +
 +## Rust 1.43
 +
 +Released 2020-04-23
 +
 +[4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b)
 +
 +### New lints
 +
 +* [`imprecise_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
 +* [`suboptimal_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
 +* [`wildcard_imports`] [#5029](https://github.com/rust-lang/rust-clippy/pull/5029)
 +* [`single_component_path_imports`] [#5058](https://github.com/rust-lang/rust-clippy/pull/5058)
 +* [`match_single_binding`] [#5061](https://github.com/rust-lang/rust-clippy/pull/5061)
 +* [`let_underscore_lock`] [#5101](https://github.com/rust-lang/rust-clippy/pull/5101)
 +* [`struct_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
 +* [`fn_params_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
 +* [`option_env_unwrap`] [#5148](https://github.com/rust-lang/rust-clippy/pull/5148)
 +* [`lossy_float_literal`] [#5202](https://github.com/rust-lang/rust-clippy/pull/5202)
 +* [`rest_pat_in_fully_bound_structs`] [#5258](https://github.com/rust-lang/rust-clippy/pull/5258)
 +
 +### Moves and Deprecations
 +
 +* Move [`unneeded_field_pattern`] to pedantic group [#5200](https://github.com/rust-lang/rust-clippy/pull/5200)
 +
 +### Enhancements
 +
 +* Make [`missing_errors_doc`] lint also trigger on `async` functions
 +  [#5181](https://github.com/rust-lang/rust-clippy/pull/5181)
 +* Add more constants to [`approx_constant`] [#5193](https://github.com/rust-lang/rust-clippy/pull/5193)
 +* Extend [`question_mark`] lint [#5266](https://github.com/rust-lang/rust-clippy/pull/5266)
 +
 +### False Positive Fixes
 +
 +* [`use_debug`] [#5047](https://github.com/rust-lang/rust-clippy/pull/5047)
 +* [`unnecessary_unwrap`] [#5132](https://github.com/rust-lang/rust-clippy/pull/5132)
 +* [`zero_prefixed_literal`] [#5170](https://github.com/rust-lang/rust-clippy/pull/5170)
 +* [`missing_const_for_fn`] [#5216](https://github.com/rust-lang/rust-clippy/pull/5216)
 +
 +### Suggestion Improvements
 +
 +* Improve suggestion when blocks of code are suggested [#5134](https://github.com/rust-lang/rust-clippy/pull/5134)
 +
 +### ICE Fixes
 +
 +* `misc_early` lints [#5129](https://github.com/rust-lang/rust-clippy/pull/5129)
 +* [`missing_errors_doc`] [#5213](https://github.com/rust-lang/rust-clippy/pull/5213)
 +* Fix ICE when evaluating `usize`s [#5256](https://github.com/rust-lang/rust-clippy/pull/5256)
 +
 +### Documentation
 +
 +* Improve documentation of [`iter_nth_zero`]
 +* Add documentation pages for stable releases [#5171](https://github.com/rust-lang/rust-clippy/pull/5171)
 +
 +### Others
 +
 +* Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190)
 +
 +
 +## Rust 1.42
 +
 +Released 2020-03-12
 +
 +[69f99e7...4ee1206](https://github.com/rust-lang/rust-clippy/compare/69f99e7...4ee1206)
 +
 +### New lints
 +
 +* [`filetype_is_file`] [#4543](https://github.com/rust-lang/rust-clippy/pull/4543)
 +* [`let_underscore_must_use`] [#4823](https://github.com/rust-lang/rust-clippy/pull/4823)
 +* [`modulo_arithmetic`] [#4867](https://github.com/rust-lang/rust-clippy/pull/4867)
 +* [`mem_replace_with_default`] [#4881](https://github.com/rust-lang/rust-clippy/pull/4881)
 +* [`mutable_key_type`] [#4885](https://github.com/rust-lang/rust-clippy/pull/4885)
 +* [`option_as_ref_deref`] [#4945](https://github.com/rust-lang/rust-clippy/pull/4945)
 +* [`wildcard_in_or_patterns`] [#4960](https://github.com/rust-lang/rust-clippy/pull/4960)
 +* [`iter_nth_zero`] [#4966](https://github.com/rust-lang/rust-clippy/pull/4966)
 +* `invalid_atomic_ordering` [#4999](https://github.com/rust-lang/rust-clippy/pull/4999)
 +* [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067)
 +
 +### Moves and Deprecations
 +
 +* Move [`transmute_float_to_int`] from nursery to complexity group
 +  [#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
 +* Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
 +* Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
 +
 +### Enhancements
 +
 +* Lint vectored IO in [`unused_io_amount`] [#5027](https://github.com/rust-lang/rust-clippy/pull/5027)
 +* Make [`vec_box`] configurable by adding a size threshold [#5081](https://github.com/rust-lang/rust-clippy/pull/5081)
 +* Also lint constants in [`cmp_nan`] [#4910](https://github.com/rust-lang/rust-clippy/pull/4910)
 +* Fix false negative in [`expect_fun_call`] [#4915](https://github.com/rust-lang/rust-clippy/pull/4915)
 +* Fix false negative in [`redundant_clone`] [#5017](https://github.com/rust-lang/rust-clippy/pull/5017)
 +
 +### False Positive Fixes
 +
 +* [`map_clone`] [#4937](https://github.com/rust-lang/rust-clippy/pull/4937)
 +* [`replace_consts`] [#4977](https://github.com/rust-lang/rust-clippy/pull/4977)
 +* [`let_and_return`] [#5008](https://github.com/rust-lang/rust-clippy/pull/5008)
 +* [`eq_op`] [#5079](https://github.com/rust-lang/rust-clippy/pull/5079)
 +* [`possible_missing_comma`] [#5083](https://github.com/rust-lang/rust-clippy/pull/5083)
 +* [`debug_assert_with_mut_call`] [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Don't trigger [`let_underscore_must_use`] in external macros
 +  [#5082](https://github.com/rust-lang/rust-clippy/pull/5082)
 +* Don't trigger [`empty_loop`] in `no_std` crates [#5086](https://github.com/rust-lang/rust-clippy/pull/5086)
 +
 +### Suggestion Improvements
 +
 +* `option_map_unwrap_or` [#4634](https://github.com/rust-lang/rust-clippy/pull/4634)
 +* [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
 +* [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
 +* [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
 +* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
 +* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
 +* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
 +* `if_let_some_result` [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
 +
 +### ICE fixes
 +
 +* [`unsound_collection_transmute`] [#4975](https://github.com/rust-lang/rust-clippy/pull/4975)
 +
 +### Documentation
 +
 +* Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`]
 +
 +
 +## Rust 1.41
 +
 +Released 2020-01-30
 +
 +[c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7)
 +
 +* New Lints:
 +  * [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697)
 +  * [`to_digit_is_some`] [#4801](https://github.com/rust-lang/rust-clippy/pull/4801)
 +  * [`tabs_in_doc_comments`] [#4806](https://github.com/rust-lang/rust-clippy/pull/4806)
 +  * [`large_stack_arrays`] [#4807](https://github.com/rust-lang/rust-clippy/pull/4807)
 +  * [`same_functions_in_if_condition`] [#4814](https://github.com/rust-lang/rust-clippy/pull/4814)
 +  * [`zst_offset`] [#4816](https://github.com/rust-lang/rust-clippy/pull/4816)
 +  * [`as_conversions`] [#4821](https://github.com/rust-lang/rust-clippy/pull/4821)
 +  * [`missing_errors_doc`] [#4884](https://github.com/rust-lang/rust-clippy/pull/4884)
 +  * [`transmute_float_to_int`] [#4889](https://github.com/rust-lang/rust-clippy/pull/4889)
 +* Remove plugin interface, see
 +  [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
 +  details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
 +* Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
 +* Deprecate `into_iter_on_array` [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
 +* Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
 +  [#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
 +* Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
 +* Fix false positive in `while_immutable_condition` [#4730](https://github.com/rust-lang/rust-clippy/pull/4730)
 +* Fix false positive in `explicit_counter_loop` [#4803](https://github.com/rust-lang/rust-clippy/pull/4803)
 +* Fix false positive in `must_use_candidate` [#4794](https://github.com/rust-lang/rust-clippy/pull/4794)
 +* Fix false positive in `print_with_newline` and `write_with_newline`
 +  [#4769](https://github.com/rust-lang/rust-clippy/pull/4769)
 +* Fix false positive in `derive_hash_xor_eq` [#4766](https://github.com/rust-lang/rust-clippy/pull/4766)
 +* Fix false positive in `missing_inline_in_public_items` [#4870](https://github.com/rust-lang/rust-clippy/pull/4870)
 +* Fix false positive in `string_add` [#4880](https://github.com/rust-lang/rust-clippy/pull/4880)
 +* Fix false positive in `float_arithmetic` [#4851](https://github.com/rust-lang/rust-clippy/pull/4851)
 +* Fix false positive in `cast_sign_loss` [#4883](https://github.com/rust-lang/rust-clippy/pull/4883)
 +* Fix false positive in `manual_swap` [#4877](https://github.com/rust-lang/rust-clippy/pull/4877)
 +* Fix ICEs occurring while checking some block expressions [#4772](https://github.com/rust-lang/rust-clippy/pull/4772)
 +* Fix ICE in `use_self` [#4776](https://github.com/rust-lang/rust-clippy/pull/4776)
 +* Fix ICEs related to `const_generics` [#4780](https://github.com/rust-lang/rust-clippy/pull/4780)
 +* Display help when running `clippy-driver` without arguments, instead of ICEing
 +  [#4810](https://github.com/rust-lang/rust-clippy/pull/4810)
 +* Clippy has its own ICE message now [#4588](https://github.com/rust-lang/rust-clippy/pull/4588)
 +* Show deprecated lints in the documentation again [#4757](https://github.com/rust-lang/rust-clippy/pull/4757)
 +* Improve Documentation by adding positive examples to some lints
 +  [#4832](https://github.com/rust-lang/rust-clippy/pull/4832)
 +
 +## Rust 1.40
 +
 +Released 2019-12-19
 +
 +[4e7e71b...c8e3cfb](https://github.com/rust-lang/rust-clippy/compare/4e7e71b...c8e3cfb)
 +
 +* New Lints:
 +  * [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537)
 +  * [`needless_doctest_main`] [#4603](https://github.com/rust-lang/rust-clippy/pull/4603)
 +  * [`suspicious_unary_op_formatting`] [#4615](https://github.com/rust-lang/rust-clippy/pull/4615)
 +  * [`debug_assert_with_mut_call`] [#4680](https://github.com/rust-lang/rust-clippy/pull/4680)
 +  * [`unused_self`] [#4619](https://github.com/rust-lang/rust-clippy/pull/4619)
 +  * [`inefficient_to_string`] [#4683](https://github.com/rust-lang/rust-clippy/pull/4683)
 +  * [`must_use_unit`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`must_use_candidate`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`double_must_use`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`comparison_chain`] [#4569](https://github.com/rust-lang/rust-clippy/pull/4569)
 +  * [`unsound_collection_transmute`] [#4592](https://github.com/rust-lang/rust-clippy/pull/4592)
 +  * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `option_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `result_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +* Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736)
 +* Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613)
 +* Expand `integer_arithmetic` to also detect mutating arithmetic like `+=` [#4585](https://github.com/rust-lang/rust-clippy/pull/4585)
 +* Fix false positive in `nonminimal_bool` [#4568](https://github.com/rust-lang/rust-clippy/pull/4568)
 +* Fix false positive in `missing_safety_doc` [#4611](https://github.com/rust-lang/rust-clippy/pull/4611)
 +* Fix false positive in `cast_sign_loss` [#4614](https://github.com/rust-lang/rust-clippy/pull/4614)
 +* Fix false positive in `redundant_clone` [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Fix false positive in `try_err` [#4721](https://github.com/rust-lang/rust-clippy/pull/4721)
 +* Fix false positive in `toplevel_ref_arg` [#4570](https://github.com/rust-lang/rust-clippy/pull/4570)
 +* Fix false positive in `multiple_inherent_impl` [#4593](https://github.com/rust-lang/rust-clippy/pull/4593)
 +* Improve more suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4575](https://github.com/rust-lang/rust-clippy/pull/4575)
 +* Improve suggestion for `zero_ptr` [#4599](https://github.com/rust-lang/rust-clippy/pull/4599)
 +* Improve suggestion for `explicit_counter_loop` [#4691](https://github.com/rust-lang/rust-clippy/pull/4691)
 +* Improve suggestion for `mul_add` [#4602](https://github.com/rust-lang/rust-clippy/pull/4602)
 +* Improve suggestion for `assertions_on_constants` [#4635](https://github.com/rust-lang/rust-clippy/pull/4635)
 +* Fix ICE in `use_self` [#4671](https://github.com/rust-lang/rust-clippy/pull/4671)
 +* Fix ICE when encountering const casts [#4590](https://github.com/rust-lang/rust-clippy/pull/4590)
 +
 +## Rust 1.39
 +
 +Released 2019-11-07
 +
 +[3aea860...4e7e71b](https://github.com/rust-lang/rust-clippy/compare/3aea860...4e7e71b)
 +
 +* New Lints:
 +  * [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479)
 +  * [`flat_map_identity`] [#4231](https://github.com/rust-lang/rust-clippy/pull/4231)
 +  * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535)
 +  * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511)
 +  * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394)
 +  * `option_and_then_some` [#4386](https://github.com/rust-lang/rust-clippy/pull/4386)
 +  * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498)
 +* Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348)
 +* Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403)
 +* Move `cast_lossless` to pedantic group [#4539](https://github.com/rust-lang/rust-clippy/pull/4539)
 +* `temporary_cstring_as_ptr` now catches more cases [#4425](https://github.com/rust-lang/rust-clippy/pull/4425)
 +* `use_self` now works in constructors, too [#4525](https://github.com/rust-lang/rust-clippy/pull/4525)
 +* `cargo_common_metadata` now checks for license files [#4518](https://github.com/rust-lang/rust-clippy/pull/4518)
 +* `cognitive_complexity` now includes the measured complexity in the warning message [#4469](https://github.com/rust-lang/rust-clippy/pull/4469)
 +* Fix false positives in `block_in_if_*` lints [#4458](https://github.com/rust-lang/rust-clippy/pull/4458)
 +* Fix false positive in `cast_lossless` [#4473](https://github.com/rust-lang/rust-clippy/pull/4473)
 +* Fix false positive in `clone_on_copy` [#4411](https://github.com/rust-lang/rust-clippy/pull/4411)
 +* Fix false positive in `deref_addrof` [#4487](https://github.com/rust-lang/rust-clippy/pull/4487)
 +* Fix false positive in `too_many_lines` [#4490](https://github.com/rust-lang/rust-clippy/pull/4490)
 +* Fix false positive in `new_ret_no_self` [#4365](https://github.com/rust-lang/rust-clippy/pull/4365)
 +* Fix false positive in `manual_swap` [#4478](https://github.com/rust-lang/rust-clippy/pull/4478)
 +* Fix false positive in `missing_const_for_fn` [#4450](https://github.com/rust-lang/rust-clippy/pull/4450)
 +* Fix false positive in `extra_unused_lifetimes` [#4477](https://github.com/rust-lang/rust-clippy/pull/4477)
 +* Fix false positive in `inherent_to_string` [#4460](https://github.com/rust-lang/rust-clippy/pull/4460)
 +* Fix false positive in `map_entry` [#4495](https://github.com/rust-lang/rust-clippy/pull/4495)
 +* Fix false positive in `unused_unit` [#4445](https://github.com/rust-lang/rust-clippy/pull/4445)
 +* Fix false positive in `redundant_pattern` [#4489](https://github.com/rust-lang/rust-clippy/pull/4489)
 +* Fix false positive in `wrong_self_convention` [#4369](https://github.com/rust-lang/rust-clippy/pull/4369)
 +* Improve various suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4558](https://github.com/rust-lang/rust-clippy/pull/4558)
 +* Improve suggestions for `redundant_pattern_matching` [#4352](https://github.com/rust-lang/rust-clippy/pull/4352)
 +* Improve suggestions for `explicit_write` [#4544](https://github.com/rust-lang/rust-clippy/pull/4544)
 +* Improve suggestion for `or_fun_call` [#4522](https://github.com/rust-lang/rust-clippy/pull/4522)
 +* Improve suggestion for `match_as_ref` [#4446](https://github.com/rust-lang/rust-clippy/pull/4446)
 +* Improve suggestion for `unnecessary_fold_span` [#4382](https://github.com/rust-lang/rust-clippy/pull/4382)
 +* Add suggestions for `unseparated_literal_suffix` [#4401](https://github.com/rust-lang/rust-clippy/pull/4401)
 +* Add suggestions for `char_lit_as_u8` [#4418](https://github.com/rust-lang/rust-clippy/pull/4418)
 +
 +## Rust 1.38
 +
 +Released 2019-09-26
 +
 +[e3cb40e...3aea860](https://github.com/rust-lang/rust-clippy/compare/e3cb40e...3aea860)
 +
 +* New Lints:
 +  * [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203)
 +  * [`inherent_to_string`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`inherent_to_string_shadow_display`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`type_repetition_in_bounds`] [#3766](https://github.com/rust-lang/rust-clippy/pull/3766)
 +  * [`try_err`] [#4222](https://github.com/rust-lang/rust-clippy/pull/4222)
 +* Move `{unnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
 +* Extend the `use_self` lint to suggest uses of `Self::Variant` [#4308](https://github.com/rust-lang/rust-clippy/pull/4308)
 +* Improve suggestion for needless return [#4262](https://github.com/rust-lang/rust-clippy/pull/4262)
 +* Add auto-fixable suggestion for `let_unit` [#4337](https://github.com/rust-lang/rust-clippy/pull/4337)
 +* Fix false positive in `pub_enum_variant_names` and `enum_variant_names` [#4345](https://github.com/rust-lang/rust-clippy/pull/4345)
 +* Fix false positive in `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
 +* Fix false positive in `string_lit_as_bytes` [#4233](https://github.com/rust-lang/rust-clippy/pull/4233)
 +* Fix false positive in `needless_lifetimes` [#4266](https://github.com/rust-lang/rust-clippy/pull/4266)
 +* Fix false positive in `float_cmp` [#4275](https://github.com/rust-lang/rust-clippy/pull/4275)
 +* Fix false positives in `needless_return` [#4274](https://github.com/rust-lang/rust-clippy/pull/4274)
 +* Fix false negative in `match_same_arms` [#4246](https://github.com/rust-lang/rust-clippy/pull/4246)
 +* Fix incorrect suggestion for `needless_bool` [#4335](https://github.com/rust-lang/rust-clippy/pull/4335)
 +* Improve suggestion for `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
 +* Improve suggestion for `single_char_literal` [#4361](https://github.com/rust-lang/rust-clippy/pull/4361)
 +* Improve suggestion for `len_zero` [#4314](https://github.com/rust-lang/rust-clippy/pull/4314)
 +* Fix ICE in `implicit_hasher` [#4268](https://github.com/rust-lang/rust-clippy/pull/4268)
 +* Fix allow bug in `trivially_copy_pass_by_ref` [#4250](https://github.com/rust-lang/rust-clippy/pull/4250)
 +
 +## Rust 1.37
 +
 +Released 2019-08-15
 +
 +[082cfa7...e3cb40e](https://github.com/rust-lang/rust-clippy/compare/082cfa7...e3cb40e)
 +
 +* New Lints:
 +  * [`checked_conversions`] [#4088](https://github.com/rust-lang/rust-clippy/pull/4088)
 +  * [`get_last_with_len`] [#3832](https://github.com/rust-lang/rust-clippy/pull/3832)
 +  * [`integer_division`] [#4195](https://github.com/rust-lang/rust-clippy/pull/4195)
 +* Renamed Lint: `const_static_lifetime` is now called [`redundant_static_lifetimes`].
 +  The lint now covers statics in addition to consts [#4162](https://github.com/rust-lang/rust-clippy/pull/4162)
 +* [`match_same_arms`] now warns for all identical arms, instead of only the first one [#4102](https://github.com/rust-lang/rust-clippy/pull/4102)
 +* [`needless_return`] now works with void functions [#4220](https://github.com/rust-lang/rust-clippy/pull/4220)
 +* Fix false positive in [`redundant_closure`] [#4190](https://github.com/rust-lang/rust-clippy/pull/4190)
 +* Fix false positive in [`useless_attribute`] [#4107](https://github.com/rust-lang/rust-clippy/pull/4107)
 +* Fix incorrect suggestion for [`float_cmp`] [#4214](https://github.com/rust-lang/rust-clippy/pull/4214)
 +* Add suggestions for [`print_with_newline`] and [`write_with_newline`] [#4136](https://github.com/rust-lang/rust-clippy/pull/4136)
 +* Improve suggestions for `option_map_unwrap_or_else` and `result_map_unwrap_or_else` [#4164](https://github.com/rust-lang/rust-clippy/pull/4164)
 +* Improve suggestions for [`non_ascii_literal`] [#4119](https://github.com/rust-lang/rust-clippy/pull/4119)
 +* Improve diagnostics for [`let_and_return`] [#4137](https://github.com/rust-lang/rust-clippy/pull/4137)
 +* Improve diagnostics for [`trivially_copy_pass_by_ref`] [#4071](https://github.com/rust-lang/rust-clippy/pull/4071)
 +* Add macro check for [`unreadable_literal`] [#4099](https://github.com/rust-lang/rust-clippy/pull/4099)
 +
 +## Rust 1.36
 +
 +Released 2019-07-04
 +
 +[eb9f9b1...082cfa7](https://github.com/rust-lang/rust-clippy/compare/eb9f9b1...082cfa7)
 +
 +* New lints: [`find_map`], [`filter_map_next`] [#4039](https://github.com/rust-lang/rust-clippy/pull/4039)
 +* New lint: [`path_buf_push_overwrite`] [#3954](https://github.com/rust-lang/rust-clippy/pull/3954)
 +* Move `path_buf_push_overwrite` to the nursery [#4013](https://github.com/rust-lang/rust-clippy/pull/4013)
 +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 +* Allow allowing of [`toplevel_ref_arg`] lint [#4007](https://github.com/rust-lang/rust-clippy/pull/4007)
 +* Fix false negative in [`or_fun_call`] pertaining to nested constructors [#4084](https://github.com/rust-lang/rust-clippy/pull/4084)
 +* Fix false positive in [`or_fun_call`] pertaining to enum variant constructors [#4018](https://github.com/rust-lang/rust-clippy/pull/4018)
 +* Fix false positive in [`useless_let_if_seq`] pertaining to interior mutability [#4035](https://github.com/rust-lang/rust-clippy/pull/4035)
 +* Fix false positive in [`redundant_closure`] pertaining to non-function types [#4008](https://github.com/rust-lang/rust-clippy/pull/4008)
 +* Fix false positive in [`let_and_return`] pertaining to attributes on `let`s [#4024](https://github.com/rust-lang/rust-clippy/pull/4024)
 +* Fix false positive in [`module_name_repetitions`] lint pertaining to attributes [#4006](https://github.com/rust-lang/rust-clippy/pull/4006)
 +* Fix false positive on [`assertions_on_constants`] pertaining to `debug_assert!` [#3989](https://github.com/rust-lang/rust-clippy/pull/3989)
 +* Improve suggestion in [`map_clone`] to suggest `.copied()` where applicable  [#3970](https://github.com/rust-lang/rust-clippy/pull/3970) [#4043](https://github.com/rust-lang/rust-clippy/pull/4043)
 +* Improve suggestion for [`search_is_some`] [#4049](https://github.com/rust-lang/rust-clippy/pull/4049)
 +* Improve suggestion applicability for [`naive_bytecount`] [#3984](https://github.com/rust-lang/rust-clippy/pull/3984)
 +* Improve suggestion applicability for [`while_let_loop`] [#3975](https://github.com/rust-lang/rust-clippy/pull/3975)
 +* Improve diagnostics for [`too_many_arguments`] [#4053](https://github.com/rust-lang/rust-clippy/pull/4053)
 +* Improve diagnostics for [`cast_lossless`] [#4021](https://github.com/rust-lang/rust-clippy/pull/4021)
 +* Deal with macro checks in desugarings better [#4082](https://github.com/rust-lang/rust-clippy/pull/4082)
 +* Add macro check for [`unnecessary_cast`]  [#4026](https://github.com/rust-lang/rust-clippy/pull/4026)
 +* Remove [`approx_constant`]'s documentation's "Known problems" section. [#4027](https://github.com/rust-lang/rust-clippy/pull/4027)
 +* Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960)
 +* Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931)
 +
 +
 +## Rust 1.35
 +
 +Released 2019-05-20
 +
 +[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
 +
 +* New lint: `drop_bounds` to detect `T: Drop` bounds
 +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 +* Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code
 +* Move [`get_unwrap`] to the restriction category
 +* Improve suggestions for [`iter_cloned_collect`]
 +* Improve suggestions for [`cast_lossless`] to suggest suffixed literals
 +* Fix false positives in [`print_with_newline`] and [`write_with_newline`] pertaining to raw strings
 +* Fix false positive in [`needless_range_loop`] pertaining to structs without a `.iter()`
 +* Fix false positive in [`bool_comparison`] pertaining to non-bool types
 +* Fix false positive in [`redundant_closure`] pertaining to differences in borrows
 +* Fix false positive in `option_map_unwrap_or` on non-copy types
 +* Fix false positives in [`missing_const_for_fn`] pertaining to macros and trait method impls
 +* Fix false positive in [`needless_pass_by_value`] pertaining to procedural macros
 +* Fix false positive in [`needless_continue`] pertaining to loop labels
 +* Fix false positive for [`boxed_local`] pertaining to arguments moved into closures
 +* Fix false positive for [`use_self`] in nested functions
 +* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846)
 +* Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables
 +* Fix suggestion for [`single_char_pattern`] to correctly escape single quotes
 +* Avoid triggering [`redundant_closure`] in macros
 +* ICE fixes: [#3805](https://github.com/rust-lang/rust-clippy/pull/3805), [#3772](https://github.com/rust-lang/rust-clippy/pull/3772), [#3741](https://github.com/rust-lang/rust-clippy/pull/3741)
 +
 +## Rust 1.34
 +
 +Released 2019-04-10
 +
 +[1b89724...1fac380](https://github.com/rust-lang/rust-clippy/compare/1b89724...1fac380)
 +
 +* New lint: [`assertions_on_constants`] to detect for example `assert!(true)`
 +* New lint: [`dbg_macro`] to detect uses of the `dbg!` macro
 +* New lint: [`missing_const_for_fn`] that can suggest functions to be made `const`
 +* New lint: [`too_many_lines`] to detect functions with excessive LOC. It can be
 +  configured using the `too-many-lines-threshold` configuration.
 +* New lint: [`wildcard_enum_match_arm`] to check for wildcard enum matches using `_`
 +* Expand `redundant_closure` to also work for methods (not only functions)
 +* Fix ICEs in `vec_box`, `needless_pass_by_value` and `implicit_hasher`
 +* Fix false positive in `cast_sign_loss`
 +* Fix false positive in `integer_arithmetic`
 +* Fix false positive in `unit_arg`
 +* Fix false positives in `implicit_return`
 +* Add suggestion to `explicit_write`
 +* Improve suggestions for `question_mark` lint
 +* Fix incorrect suggestion for `cast_lossless`
 +* Fix incorrect suggestion for `expect_fun_call`
 +* Fix incorrect suggestion for `needless_bool`
 +* Fix incorrect suggestion for `needless_range_loop`
 +* Fix incorrect suggestion for `use_self`
 +* Fix incorrect suggestion for `while_let_on_iterator`
 +* Clippy is now slightly easier to invoke in non-cargo contexts. See
 +  [#3665][pull3665] for more details.
 +* We now have [improved documentation][adding_lints] on how to add new lints
 +
 +## Rust 1.33
 +
 +Released 2019-02-26
 +
 +[b2601be...1b89724](https://github.com/rust-lang/rust-clippy/compare/b2601be...1b89724)
 +
 +* New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`]
 +* The `rust-clippy` repository is now part of the `rust-lang` org.
 +* Rename `stutter` to `module_name_repetitions`
 +* Merge `new_without_default_derive` into `new_without_default` lint
 +* Move `large_digit_groups` from `style` group to `pedantic`
 +* Expand `bool_comparison` to check for `<`, `<=`, `>`, `>=`, and `!=`
 +  comparisons against booleans
 +* Expand `no_effect` to detect writes to constants such as `A_CONST.field = 2`
 +* Expand `redundant_clone` to work on struct fields
 +* Expand `suspicious_else_formatting` to detect `if .. {..} {..}`
 +* Expand `use_self` to work on tuple structs and also in local macros
 +* Fix ICE in `result_map_unit_fn` and `option_map_unit_fn`
 +* Fix false positives in `implicit_return`
 +* Fix false positives in `use_self`
 +* Fix false negative in `clone_on_copy`
 +* Fix false positive in `doc_markdown`
 +* Fix false positive in `empty_loop`
 +* Fix false positive in `if_same_then_else`
 +* Fix false positive in `infinite_iter`
 +* Fix false positive in `question_mark`
 +* Fix false positive in `useless_asref`
 +* Fix false positive in `wildcard_dependencies`
 +* Fix false positive in `write_with_newline`
 +* Add suggestion to `explicit_write`
 +* Improve suggestions for `question_mark` lint
 +* Fix incorrect suggestion for `get_unwrap`
 +
 +## Rust 1.32
 +
 +Released 2019-01-17
 +
 +[2e26fdc2...b2601be](https://github.com/rust-lang/rust-clippy/compare/2e26fdc2...b2601be)
 +
 +* New lints: [`slow_vector_initialization`], `mem_discriminant_non_enum`,
 +  [`redundant_clone`], [`wildcard_dependencies`],
 +  [`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`],
 +  [`cargo_common_metadata`]
 +* Add support for `u128` and `i128` to integer related lints
 +* Add float support to `mistyped_literal_suffixes`
 +* Fix false positives in `use_self`
 +* Fix false positives in `missing_comma`
 +* Fix false positives in `new_ret_no_self`
 +* Fix false positives in `possible_missing_comma`
 +* Fix false positive in `integer_arithmetic` in constant items
 +* Fix false positive in `needless_borrow`
 +* Fix false positive in `out_of_bounds_indexing`
 +* Fix false positive in `new_without_default_derive`
 +* Fix false positive in `string_lit_as_bytes`
 +* Fix false negative in `out_of_bounds_indexing`
 +* Fix false negative in `use_self`. It will now also check existential types
 +* Fix incorrect suggestion for `redundant_closure_call`
 +* Fix various suggestions that contained expanded macros
 +* Fix `bool_comparison` triggering 3 times on on on the same code
 +* Expand `trivially_copy_pass_by_ref` to work on trait methods
 +* Improve suggestion for `needless_range_loop`
 +* Move `needless_pass_by_value` from `pedantic` group to `style`
 +
 +## Rust 1.31
 +
 +Released 2018-12-06
 +
 +[125907ad..2e26fdc2](https://github.com/rust-lang/rust-clippy/compare/125907ad..2e26fdc2)
 +
 +* Clippy has been relicensed under a dual MIT / Apache license.
 +  See [#3093](https://github.com/rust-lang/rust-clippy/issues/3093) for more
 +  information.
 +* With Rust 1.31, Clippy is no longer available via crates.io. The recommended
 +  installation method is via `rustup component add clippy`.
 +* New lints: [`redundant_pattern_matching`], [`unnecessary_filter_map`],
 +  [`unused_unit`], [`map_flatten`], [`mem_replace_option_with_none`]
 +* Fix ICE in `if_let_redundant_pattern_matching`
 +* Fix ICE in `needless_pass_by_value` when encountering a generic function
 +  argument with a lifetime parameter
 +* Fix ICE in `needless_range_loop`
 +* Fix ICE in `single_char_pattern` when encountering a constant value
 +* Fix false positive in `assign_op_pattern`
 +* Fix false positive in `boxed_local` on trait implementations
 +* Fix false positive in `cmp_owned`
 +* Fix false positive in `collapsible_if` when conditionals have comments
 +* Fix false positive in `double_parens`
 +* Fix false positive in `excessive_precision`
 +* Fix false positive in `explicit_counter_loop`
 +* Fix false positive in `fn_to_numeric_cast_with_truncation`
 +* Fix false positive in `map_clone`
 +* Fix false positive in `new_ret_no_self`
 +* Fix false positive in `new_without_default` when `new` is unsafe
 +* Fix false positive in `type_complexity` when using extern types
 +* Fix false positive in `useless_format`
 +* Fix false positive in `wrong_self_convention`
 +* Fix incorrect suggestion for `excessive_precision`
 +* Fix incorrect suggestion for `expect_fun_call`
 +* Fix incorrect suggestion for `get_unwrap`
 +* Fix incorrect suggestion for `useless_format`
 +* `fn_to_numeric_cast_with_truncation` lint can be disabled again
 +* Improve suggestions for `manual_memcpy`
 +* Improve help message for `needless_lifetimes`
 +
 +## Rust 1.30
 +
 +Released 2018-10-25
 +
 +[14207503...125907ad](https://github.com/rust-lang/rust-clippy/compare/14207503...125907ad)
 +
 +* Deprecate `assign_ops` lint
 +* New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`],
 +  [`needless_collect`], [`copy_iterator`]
 +* `cargo clippy -V` now includes the Clippy commit hash of the Rust
 +  Clippy component
 +* Fix ICE in `implicit_hasher`
 +* Fix ICE when encountering `println!("{}" a);`
 +* Fix ICE when encountering a macro call in match statements
 +* Fix false positive in `default_trait_access`
 +* Fix false positive in `trivially_copy_pass_by_ref`
 +* Fix false positive in `similar_names`
 +* Fix false positive in `redundant_field_name`
 +* Fix false positive in `expect_fun_call`
 +* Fix false negative in `identity_conversion`
 +* Fix false negative in `explicit_counter_loop`
 +* Fix `range_plus_one` suggestion and false negative
 +* `print_with_newline` / `write_with_newline`: don't warn about string with several `\n`s in them
 +* Fix `useless_attribute` to also whitelist `unused_extern_crates`
 +* Fix incorrect suggestion for `single_char_pattern`
 +* Improve suggestion for `identity_conversion` lint
 +* Move `explicit_iter_loop` and `explicit_into_iter_loop` from `style` group to `pedantic`
 +* Move `range_plus_one` and `range_minus_one` from `nursery` group to `complexity`
 +* Move `shadow_unrelated` from `restriction` group to `pedantic`
 +* Move `indexing_slicing` from `pedantic` group to `restriction`
 +
 +## Rust 1.29
 +
 +Released 2018-09-13
 +
 +[v0.0.212...14207503](https://github.com/rust-lang/rust-clippy/compare/v0.0.212...14207503)
 +
 +* :tada: :tada: **Rust 1.29 is the first stable Rust that includes a bundled Clippy** :tada:
 +  :tada:
 +  You can now run `rustup component add clippy-preview` and then `cargo
 +  clippy` to run Clippy. This should put an end to the continuous nightly
 +  upgrades for Clippy users.
 +* Clippy now follows the Rust versioning scheme instead of its own
 +* Fix ICE when encountering a `while let (..) = x.iter()` construct
 +* Fix false positives in `use_self`
 +* Fix false positive in `trivially_copy_pass_by_ref`
 +* Fix false positive in `useless_attribute` lint
 +* Fix false positive in `print_literal`
 +* Fix `use_self` regressions
 +* Improve lint message for `neg_cmp_op_on_partial_ord`
 +* Improve suggestion highlight for `single_char_pattern`
 +* Improve suggestions for various print/write macro lints
 +* Improve website header
 +
 +## 0.0.212 (2018-07-10)
 +* Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)*
 +
 +## 0.0.211
 +* Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)*
 +
 +## 0.0.210
 +* Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)*
 +
 +## 0.0.209
 +* Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)*
 +
 +## 0.0.208
 +* Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)*
 +
 +## 0.0.207
 +* Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)*
 +
 +## 0.0.206
 +* Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)*
 +
 +## 0.0.205
 +* Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)*
 +* Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint
 +
 +## 0.0.204
 +* Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)*
 +
 +## 0.0.203
 +* Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)*
 +* Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)`
 +
 +## 0.0.202
 +* Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)*
 +
 +## 0.0.201
 +* Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)*
 +
 +## 0.0.200
 +* Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)*
 +
 +## 0.0.199
 +* Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)*
 +
 +## 0.0.198
 +* Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)*
 +
 +## 0.0.197
 +* Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)*
 +
 +## 0.0.196
 +* Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)*
 +
 +## 0.0.195
 +* Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)*
 +
 +## 0.0.194
 +* Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)*
 +* New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`]
 +
 +## 0.0.193
 +* Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)*
 +
 +## 0.0.192
 +* Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)*
 +* New lint: [`print_literal`]
 +
 +## 0.0.191
 +* Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)*
 +* Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction.
 +
 +## 0.0.190
 +* Fix a bunch of intermittent cargo bugs
 +
 +## 0.0.189
 +* Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)*
 +
 +## 0.0.188
 +* Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)*
 +* New lint: [`while_immutable_condition`]
 +
 +## 0.0.187
 +* Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)*
 +* New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`]
 +
 +## 0.0.186
 +* Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)*
 +* Various false positive fixes
 +
 +## 0.0.185
 +* Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)*
 +* New lint: [`question_mark`]
 +
 +## 0.0.184
 +* Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)*
 +* New lints: [`double_comparisons`], [`empty_line_after_outer_attr`]
 +
 +## 0.0.183
 +* Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)*
 +* New lint: [`misaligned_transmute`]
 +
 +## 0.0.182
 +* Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)*
 +* New lint: [`decimal_literal_representation`]
 +
 +## 0.0.181
 +* Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)*
 +* New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`]
 +* Removed `unit_expr`
 +* Various false positive fixes for [`needless_pass_by_value`]
 +
 +## 0.0.180
 +* Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)*
 +
 +## 0.0.179
 +* Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)*
 +
 +## 0.0.178
 +* Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)*
 +
 +## 0.0.177
 +* Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)*
 +* New lint: [`match_as_ref`]
 +
 +## 0.0.176
 +* Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)*
 +
 +## 0.0.175
 +* Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)*
 +
 +## 0.0.174
 +* Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)*
 +
 +## 0.0.173
 +* Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)*
 +
 +## 0.0.172
 +* Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)*
 +
 +## 0.0.171
 +* Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)*
 +
 +## 0.0.170
 +* Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)*
 +
 +## 0.0.169
 +* Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)*
 +* New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`]
 +
 +## 0.0.168
 +* Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)*
 +
 +## 0.0.167
 +* Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)*
 +* New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`]
 +
 +## 0.0.166
 +* Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)*
 +* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`],
 +  [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`],
 +  [`transmute_int_to_float`]
 +
 +## 0.0.165
 +* Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27)
 +* New lint: [`mut_range_bound`]
 +
 +## 0.0.164
 +* Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)*
 +* New lint: [`int_plus_one`]
 +
 +## 0.0.163
 +* Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)*
 +
 +## 0.0.162
 +* Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)*
 +* New lint: [`chars_last_cmp`]
 +* Improved suggestions for [`needless_borrow`], [`ptr_arg`],
 +
 +## 0.0.161
 +* Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)*
 +
 +## 0.0.160
 +* Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)*
 +
 +## 0.0.159
 +* Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)*
 +* New lint: [`clone_on_ref_ptr`]
 +
 +## 0.0.158
 +* New lint: [`manual_memcpy`]
 +* [`cast_lossless`] no longer has redundant parentheses in its suggestions
 +* Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)*
 +
 +## 0.0.157 - 2017-09-04
 +* Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)*
 +* New lint: `unit_expr`
 +
 +## 0.0.156 - 2017-09-03
 +* Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)*
 +
 +## 0.0.155
 +* Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)*
 +* New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`]
 +
 +## 0.0.154
 +* Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)*
 +* Fix [`use_self`] triggering inside derives
 +* Add support for linting an entire workspace with `cargo clippy --all`
 +* New lint: [`naive_bytecount`]
 +
 +## 0.0.153
 +* Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)*
 +* New lint: [`use_self`]
 +
 +## 0.0.152
 +* Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)*
 +
 +## 0.0.151
 +* Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)*
 +
 +## 0.0.150
 +* Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)*
 +
 +## 0.0.148
 +* Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)*
 +* New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`]
 +
 +## 0.0.147
 +* Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)*
 +
 +## 0.0.146
 +* Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)*
 +* Fixes false positives in `inline_always`
 +* Fixes false negatives in `panic_params`
 +
 +## 0.0.145
 +* Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)*
 +
 +## 0.0.144
 +* Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)*
 +
 +## 0.0.143
 +* Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)*
 +* Fix `cargo clippy` crashing on `dylib` projects
 +* Fix false positives around `nested_while_let` and `never_loop`
 +
 +## 0.0.142
 +* Update to *rustc 1.20.0-nightly (067971139 2017-07-02)*
 +
 +## 0.0.141
 +* Rewrite of the `doc_markdown` lint.
 +* Deprecated [`range_step_by_zero`]
 +* New lint: [`iterator_step_by_zero`]
 +* New lint: [`needless_borrowed_reference`]
 +* Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)*
 +
 +## 0.0.140 - 2017-06-16
 +* Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)*
 +
 +## 0.0.139 — 2017-06-10
 +* Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)*
 +* Fix bugs with for loop desugaring
 +* Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`]
 +
 +## 0.0.138 — 2017-06-05
 +* Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)*
 +
 +## 0.0.137 — 2017-06-05
 +* Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)*
 +
 +## 0.0.136 — 2017—05—26
 +* Update to *rustc 1.19.0-nightly (557967766 2017-05-26)*
 +
 +## 0.0.135 — 2017—05—24
 +* Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)*
 +
 +## 0.0.134 — 2017—05—19
 +* Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)*
 +
 +## 0.0.133 — 2017—05—14
 +* Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)*
 +
 +## 0.0.132 — 2017—05—05
 +* Fix various bugs and some ices
 +
 +## 0.0.131 — 2017—05—04
 +* Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)*
 +
 +## 0.0.130 — 2017—05—03
 +* Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)*
 +
 +## 0.0.129 — 2017-05-01
 +* Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)*
 +
 +## 0.0.128 — 2017-04-28
 +* Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)*
 +
 +## 0.0.127 — 2017-04-27
 +* Update to *rustc 1.18.0-nightly (036983201 2017-04-26)*
 +* New lint: [`needless_continue`]
 +
 +## 0.0.126 — 2017-04-24
 +* Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)*
 +
 +## 0.0.125 — 2017-04-19
 +* Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)*
 +
 +## 0.0.124 — 2017-04-16
 +* Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)*
 +
 +## 0.0.123 — 2017-04-07
 +* Fix various false positives
 +
 +## 0.0.122 — 2017-04-07
 +* Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)*
 +* New lint: [`op_ref`]
 +
 +## 0.0.121 — 2017-03-21
 +* Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)*
 +
 +## 0.0.120 — 2017-03-17
 +* Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)*
 +
 +## 0.0.119 — 2017-03-13
 +* Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)*
 +
 +## 0.0.118 — 2017-03-05
 +* Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)*
 +
 +## 0.0.117 — 2017-03-01
 +* Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)*
 +
 +## 0.0.116 — 2017-02-28
 +* Fix `cargo clippy` on 64 bit windows systems
 +
 +## 0.0.115 — 2017-02-27
 +* Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)*
 +* New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`]
 +
 +## 0.0.114 — 2017-02-08
 +* Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)*
 +* Tests are now ui tests (testing the exact output of rustc)
 +
 +## 0.0.113 — 2017-02-04
 +* Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)*
 +* New lint: [`large_enum_variant`]
 +* `explicit_into_iter_loop` provides suggestions
 +
 +## 0.0.112 — 2017-01-27
 +* Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)*
 +
 +## 0.0.111 — 2017-01-21
 +* Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)*
 +
 +## 0.0.110 — 2017-01-20
 +* Add badges and categories to `Cargo.toml`
 +
 +## 0.0.109 — 2017-01-19
 +* Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)*
 +
 +## 0.0.108 — 2017-01-12
 +* Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)*
 +
 +## 0.0.107 — 2017-01-11
 +* Update regex dependency
 +* Fix FP when matching `&&mut` by `&ref`
 +* Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()`
 +* New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`]
 +
 +## 0.0.106 — 2017-01-04
 +* Fix FP introduced by rustup in [`wrong_self_convention`]
 +
 +## 0.0.105 — 2017-01-04
 +* Update to *rustc 1.16.0-nightly (468227129 2017-01-03)*
 +* New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`]
 +* Fix suggestion in [`new_without_default`]
 +* FP fix in [`absurd_extreme_comparisons`]
 +
 +## 0.0.104 — 2016-12-15
 +* Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)*
 +
 +## 0.0.103 — 2016-11-25
 +* Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)*
 +
 +## 0.0.102 — 2016-11-24
 +* Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)*
 +
 +## 0.0.101 — 2016-11-23
 +* Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)*
 +* New lint: [`string_extend_chars`]
 +
 +## 0.0.100 — 2016-11-20
 +* Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)*
 +
 +## 0.0.99 — 2016-11-18
 +* Update to rustc 1.15.0-nightly (0ed951993 2016-11-14)
 +* New lint: [`get_unwrap`]
 +
 +## 0.0.98 — 2016-11-08
 +* Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy`
 +
 +## 0.0.97 — 2016-11-03
 +* For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was
 +  previously added for a short time under the name `clippy` but removed for
 +  compatibility.
 +* `cargo clippy --help` is more helping (and less helpful :smile:)
 +* Rustup to *rustc 1.14.0-nightly (5665bdf3e 2016-11-02)*
 +* New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`]
 +
 +## 0.0.96 — 2016-10-22
 +* Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)*
 +* New lint: [`iter_skip_next`]
 +
 +## 0.0.95 — 2016-10-06
 +* Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)*
 +
 +## 0.0.94 — 2016-10-04
 +* Fixes bustage on Windows due to forbidden directory name
 +
 +## 0.0.93 — 2016-10-03
 +* Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)*
 +* `option_map_unwrap_or` and `option_map_unwrap_or_else` are now
 +  allowed by default.
 +* New lint: [`explicit_into_iter_loop`]
 +
 +## 0.0.92 — 2016-09-30
 +* Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)*
 +
 +## 0.0.91 — 2016-09-28
 +* Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)*
 +
 +## 0.0.90 — 2016-09-09
 +* Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)*
 +
 +## 0.0.89 — 2016-09-06
 +* Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)*
 +
 +## 0.0.88 — 2016-09-04
 +* Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)*
 +* The following lints are not new but were only usable through the `clippy`
 +  lint groups: [`filter_next`], `for_loop_over_option`,
 +  `for_loop_over_result` and [`match_overlapping_arm`]. You should now be
 +  able to `#[allow/deny]` them individually and they are available directly
 +  through `cargo clippy`.
 +
 +## 0.0.87 — 2016-08-31
 +* Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)*
 +* New lints: [`builtin_type_shadow`]
 +* Fix FP in [`zero_prefixed_literal`] and `0b`/`0o`
 +
 +## 0.0.86 — 2016-08-28
 +* Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)*
 +* New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`]
 +
 +## 0.0.85 — 2016-08-19
 +* Fix ICE with [`useless_attribute`]
 +* [`useless_attribute`] ignores `unused_imports` on `use` statements
 +
 +## 0.0.84 — 2016-08-18
 +* Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)*
 +
 +## 0.0.83 — 2016-08-17
 +* Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)*
 +* New lints: [`print_with_newline`], [`useless_attribute`]
 +
 +## 0.0.82 — 2016-08-17
 +* Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)*
 +* New lint: [`module_inception`]
 +
 +## 0.0.81 — 2016-08-14
 +* Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)*
 +* New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`]
 +* False positive fix in [`too_many_arguments`]
 +* Addition of functionality to [`needless_borrow`]
 +* Suggestions for [`clone_on_copy`]
 +* Bug fix in [`wrong_self_convention`]
 +* Doc improvements
 +
 +## 0.0.80 — 2016-07-31
 +* Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)*
 +* New lints: [`misrefactored_assign_op`], [`serde_api_misuse`]
 +
 +## 0.0.79 — 2016-07-10
 +* Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)*
 +* Major suggestions refactoring
 +
 +## 0.0.78 — 2016-07-02
 +* Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)*
 +* New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`]
 +* For compatibility, `cargo clippy` does not defines the `clippy` feature
 +  introduced in 0.0.76 anymore
 +* [`collapsible_if`] now considers `if let`
 +
 +## 0.0.77 — 2016-06-21
 +* Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)*
 +* New lints: `stutter` and [`iter_nth`]
 +
 +## 0.0.76 — 2016-06-10
 +* Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)*
 +* `cargo clippy` now automatically defines the `clippy` feature
 +* New lint: [`not_unsafe_ptr_arg_deref`]
 +
 +## 0.0.75 — 2016-06-08
 +* Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)*
 +
 +## 0.0.74 — 2016-06-07
 +* Fix bug with `cargo-clippy` JSON parsing
 +* Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the
 +  “for further information visit *lint-link*” message.
 +
 +## 0.0.73 — 2016-06-05
 +* Fix false positives in [`useless_let_if_seq`]
 +
 +## 0.0.72 — 2016-06-04
 +* Fix false positives in [`useless_let_if_seq`]
 +
 +## 0.0.71 — 2016-05-31
 +* Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)*
 +* New lint: [`useless_let_if_seq`]
 +
 +## 0.0.70 — 2016-05-28
 +* Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)*
 +* [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`,
 +  `RegexBuilder::new` and byte regexes
 +
 +## 0.0.69 — 2016-05-20
 +* Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)*
 +* [`used_underscore_binding`] has been made `Allow` temporarily
 +
 +## 0.0.68 — 2016-05-17
 +* Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)*
 +* New lint: [`unnecessary_operation`]
 +
 +## 0.0.67 — 2016-05-12
 +* Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)*
 +
 +## 0.0.66 — 2016-05-11
 +* New `cargo clippy` subcommand
 +* New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`]
 +
 +## 0.0.65 — 2016-05-08
 +* Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)*
 +* New lints: [`float_arithmetic`], [`integer_arithmetic`]
 +
 +## 0.0.64 — 2016-04-26
 +* Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)*
 +* New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`]
 +
 +## 0.0.63 — 2016-04-08
 +* Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)*
 +
 +## 0.0.62 — 2016-04-07
 +* Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)*
 +
 +## 0.0.61 — 2016-04-03
 +* Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)*
 +* New lint: [`invalid_upcast_comparisons`]
 +
 +## 0.0.60 — 2016-04-01
 +* Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)*
 +
 +## 0.0.59 — 2016-03-31
 +* Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)*
 +* New lints: [`logic_bug`], [`nonminimal_bool`]
 +* Fixed: [`match_same_arms`] now ignores arms with guards
 +* Improved: [`useless_vec`] now warns on `for … in vec![…]`
 +
 +## 0.0.58 — 2016-03-27
 +* Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)*
 +* New lint: [`doc_markdown`]
 +
 +## 0.0.57 — 2016-03-27
 +* Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)*
 +* Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`]
 +* New lint: [`crosspointer_transmute`]
 +
 +## 0.0.56 — 2016-03-23
 +* Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)*
 +* New lints: [`many_single_char_names`] and [`similar_names`]
 +
 +## 0.0.55 — 2016-03-21
 +* Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)*
 +
 +## 0.0.54 — 2016-03-16
 +* Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)*
 +
 +## 0.0.53 — 2016-03-15
 +* Add a [configuration file]
 +
 +## ~~0.0.52~~
 +
 +## 0.0.51 — 2016-03-13
 +* Add `str` to types considered by [`len_zero`]
 +* New lints: [`indexing_slicing`]
 +
 +## 0.0.50 — 2016-03-11
 +* Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)*
 +
 +## 0.0.49 — 2016-03-09
 +* Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)*
 +* New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`]
 +
 +## 0.0.48 — 2016-03-07
 +* Fixed: ICE in [`needless_range_loop`] with globals
 +
 +## 0.0.47 — 2016-03-07
 +* Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)*
 +* New lint: [`redundant_closure_call`]
 +
 +[`AsMut`]: https://doc.rust-lang.org/std/convert/trait.AsMut.html
 +[`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
 +[configuration file]: ./rust-clippy#configuration
 +[pull3665]: https://github.com/rust-lang/rust-clippy/pull/3665
 +[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/adding_lints.md
 +[`README.md`]: https://github.com/rust-lang/rust-clippy/blob/master/README.md
 +
 +<!-- lint disable no-unused-definitions -->
 +<!-- begin autogenerated links to lint list -->
 +[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
 +[`alloc_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#alloc_instead_of_core
 +[`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason
 +[`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range
 +[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
 +[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
 +[`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects
 +[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
++[`as_ptr_cast_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_ptr_cast_mut
 +[`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore
 +[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
 +[`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states
 +[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
 +[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
 +[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
 +[`await_holding_invalid_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type
 +[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
 +[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
 +[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
 +[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
 +[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
 +[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
 +[`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr
 +[`block_in_if_condition_stmt`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_stmt
 +[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
 +[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
 +[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
 +[`bool_to_int_with_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_to_int_with_if
 +[`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr
 +[`borrow_deref_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_deref_ref
 +[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
 +[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
 +[`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
 +[`box_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_default
 +[`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec
 +[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
 +[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
 +[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
 +[`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len
 +[`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
 +[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
 +[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
 +[`cast_abs_to_unsigned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned
 +[`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor
 +[`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation
 +[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
++[`cast_nan_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_nan_to_int
 +[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
 +[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
 +[`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss
 +[`cast_ptr_alignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ptr_alignment
 +[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
 +[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
 +[`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes
 +[`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts
 +[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
 +[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
 +[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
 +[`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions
 +[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
 +[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
 +[`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
 +[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied
 +[`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
 +[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
 +[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
 +[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
 +[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
 +[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
 +[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
 +[`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace
 +[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
 +[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
 +[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
 +[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
 +[`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def
 +[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
 +[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
 +[`cyclomatic_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cyclomatic_complexity
 +[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
 +[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
 +[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
 +[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
 +[`default_instead_of_iter_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_instead_of_iter_empty
 +[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
 +[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
 +[`default_union_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_union_representation
 +[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
 +[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
 +[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
 +[`deref_by_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_by_slicing
 +[`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls
 +[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
 +[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
 +[`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq
 +[`disallowed_macros`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros
 +[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
 +[`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods
 +[`disallowed_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names
 +[`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents
 +[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
 +[`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
 +[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
 +[`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes
 +[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
 +[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
 +[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
 +[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
 +[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
 +[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
 +[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
 +[`drop_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_non_drop
 +[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
 +[`duplicate_mod`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_mod
 +[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
 +[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
 +[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
 +[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop
 +[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
 +[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
 +[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
 +[`empty_structs_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_structs_with_brackets
 +[`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant
 +[`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use
 +[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
 +[`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op
 +[`equatable_if_let`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_if_let
 +[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
 +[`err_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#err_expect
 +[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
 +[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
 +[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
 +[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs
 +[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
 +[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
 +[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used
 +[`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy
 +[`explicit_auto_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_auto_deref
 +[`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop
 +[`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods
 +[`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop
 +[`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop
 +[`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write
 +[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
 +[`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain
 +[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
 +[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
 +[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
 +[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
 +[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
 +[`filter_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_identity
 +[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
 +[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
 +[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
 +[`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
 +[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option
 +[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
 +[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
 +[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
 +[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs
 +[`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons
 +[`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools
 +[`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast
 +[`fn_to_numeric_cast_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_any
 +[`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation
 +[`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map
 +[`for_loop_over_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_option
 +[`for_loop_over_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_result
 +[`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles
 +[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
 +[`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop
 +[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
 +[`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args
 +[`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string
 +[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
 +[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
 +[`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10
 +[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
 +[`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first
 +[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
 +[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
 +[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion
 +[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
 +[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
 +[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
 +[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
 +[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
 +[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
 +[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
 +[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
 +[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
 +[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
 +[`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
 +[`implicit_saturating_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_add
 +[`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
 +[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
 +[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
 +[`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
 +[`index_refutable_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice
 +[`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
 +[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
 +[`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
 +[`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
 +[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
 +[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
 +[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
 +[`init_numbered_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#init_numbered_fields
 +[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
 +[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax
 +[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax
 +[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
 +[`inspect_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#inspect_for_each
 +[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
 +[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
 +[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
 +[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
 +[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
 +[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
 +[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
 +[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
 +[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
 +[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
 +[`invalid_utf8_in_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_utf8_in_unchecked
 +[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
 +[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
 +[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
 +[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
 +[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
 +[`iter_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map
 +[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
 +[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice
 +[`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator
 +[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
 +[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
 +[`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections
 +[`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items
 +[`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
 +[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
 +[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
 +[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
 +[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
 +[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
 +[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
 +[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
 +[`large_include_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file
 +[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
 +[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
 +[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
 +[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
 +[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
 +[`let_underscore_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop
 +[`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock
 +[`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
 +[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
 +[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
 +[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
 +[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
 +[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
 +[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
 +[`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
 +[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
 +[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits
 +[`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp
++[`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter
 +[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
 +[`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find
 +[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
 +[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
 +[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
 +[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
 +[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
 +[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
 +[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
 +[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
 +[`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid
 +[`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain
 +[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
 +[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
 +[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
 +[`manual_string_new`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new
 +[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
 +[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
 +[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
 +[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
 +[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
 +[`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit
 +[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
 +[`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore
 +[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
 +[`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity
 +[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or
 +[`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
 +[`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
 +[`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
 +[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
 +[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
 +[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
 +[`match_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_result_ok
 +[`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
 +[`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
 +[`match_str_case_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_str_case_mismatch
 +[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
 +[`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants
 +[`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter
 +[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
 +[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
 +[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
 +[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default
 +[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
 +[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
 +[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
 +[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
 +[`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order
 +[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
 +[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
 +[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
 +[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
 +[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
 +[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
 +[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
 +[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
 +[`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop
++[`missing_trait_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_trait_methods
 +[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
 +[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
 +[`mixed_read_write_in_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_read_write_in_expression
 +[`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files
 +[`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
 +[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
 +[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
 +[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
 +[`multi_assignments`]: https://rust-lang.github.io/rust-clippy/master/index.html#multi_assignments
 +[`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
 +[`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl
 +[`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate
 +[`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit
 +[`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref
 +[`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut
 +[`mut_mutex_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mutex_lock
 +[`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound
 +[`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type
 +[`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic
 +[`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
 +[`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
 +[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
 +[`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool
 +[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
 +[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
 +[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
 +[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
 +[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
 +[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
 +[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
 +[`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
 +[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
 +[`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
 +[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
 +[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take
 +[`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals
 +[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
 +[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
 +[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
 +[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
 +[`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn
 +[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
 +[`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
 +[`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
 +[`negative_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#negative_feature_names
 +[`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop
 +[`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self
 +[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
 +[`new_without_default_derive`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default_derive
 +[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
 +[`no_effect_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_replace
 +[`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding
 +[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
 +[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
 +[`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty
 +[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
 +[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
 +[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
 +[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
 +[`obfuscated_if_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#obfuscated_if_else
 +[`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes
 +[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
 +[`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion
 +[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
 +[`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some
 +[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
 +[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
 +[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used
 +[`option_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_filter_map
 +[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
 +[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
 +[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
 +[`option_map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or
 +[`option_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or_else
 +[`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
 +[`option_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_used
 +[`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call
 +[`or_then_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_then_unwrap
 +[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
 +[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
 +[`overly_complex_bool_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#overly_complex_bool_expr
 +[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
 +[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
 +[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
 +[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
++[`partial_pub_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#partial_pub_fields
 +[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
 +[`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none
 +[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
 +[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
 +[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
 +[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
 +[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
 +[`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl
 +[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
 +[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr
 +[`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
 +[`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
 +[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
 +[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
 +[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr
 +[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
 +[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
 +[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
 +[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
 +[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
 +[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
 +[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
 +[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
 +[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
 +[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
 +[`rc_clone_in_vec_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_clone_in_vec_init
 +[`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex
 +[`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec
 +[`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
 +[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
 +[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
 +[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
 +[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
 +[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
 +[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
 +[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
 +[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
 +[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
 +[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
 +[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
 +[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
 +[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
 +[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
 +[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
 +[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
 +[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
 +[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
 +[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
 +[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
 +[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
 +[`result_large_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err
 +[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
 +[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
 +[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else
 +[`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err
 +[`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used
 +[`return_self_not_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_self_not_must_use
 +[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
 +[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
 +[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
 +[`same_name_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_name_method
 +[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
 +[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
 +[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
 +[`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files
 +[`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
 +[`separated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#separated_literal_suffix
 +[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
 +[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
 +[`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same
 +[`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
 +[`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement
 +[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq
 +[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
 +[`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee
 +[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
 +[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
 +[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names
 +[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
 +[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str
 +[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
 +[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
 +[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
 +[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
 +[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
 +[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
 +[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
 +[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
 +[`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc
 +[`std_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_core
 +[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
 +[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
 +[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
 +[`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars
 +[`string_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes
 +[`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
 +[`string_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_slice
 +[`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
 +[`strlen_on_c_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#strlen_on_c_strings
 +[`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
 +[`stutter`]: https://rust-lang.github.io/rust-clippy/master/index.html#stutter
 +[`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops
 +[`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
 +[`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting
 +[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
 +[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
 +[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
 +[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
 +[`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn
 +[`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned
 +[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
 +[`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref
 +[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
 +[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
 +[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
 +[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
 +[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
 +[`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
 +[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
 +[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
 +[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
 +[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
 +[`trailing_empty_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#trailing_empty_array
 +[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds
 +[`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str
 +[`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int
 +[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
 +[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
 +[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
 +[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
 +[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
 +[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
 +[`transmute_undefined_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_undefined_repr
 +[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
 +[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
 +[`trim_split_whitespace`]: https://rust-lang.github.io/rust-clippy/master/index.html#trim_split_whitespace
 +[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
 +[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
 +[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
 +[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
 +[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
 +[`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks
 +[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops
 +[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
 +[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
 +[`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init
 +[`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec
 +[`uninlined_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args
 +[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
 +[`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp
 +[`unit_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash
 +[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
 +[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
 +[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
 +[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
 +[`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map
 +[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
 +[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
 +[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
 +[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
 +[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
 +[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
 +[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
 +[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
 +[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned
 +[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
 +[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
 +[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
 +[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
 +[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
 +[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable
 +[`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal
 +[`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize
 +[`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name
 +[`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization
 +[`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix
 +[`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute
 +[`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice
 +[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
 +[`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async
 +[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
++[`unused_format_specs`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_format_specs
 +[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
 +[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
 +[`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable
 +[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding
 +[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
 +[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
 +[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
 +[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result
 +[`unwrap_or_else_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_or_else_default
 +[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used
 +[`upper_case_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms
 +[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
 +[`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
 +[`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding
 +[`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref
 +[`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute
 +[`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
 +[`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format
 +[`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq
 +[`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute
 +[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
 +[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box
 +[`vec_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push
 +[`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero
 +[`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask
 +[`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads
 +[`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons
 +[`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition
 +[`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop
 +[`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator
 +[`wildcard_dependencies`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_dependencies
 +[`wildcard_enum_match_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_enum_match_arm
 +[`wildcard_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports
 +[`wildcard_in_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_in_or_patterns
 +[`write_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_literal
 +[`write_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_with_newline
 +[`writeln_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#writeln_empty_string
 +[`wrong_pub_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_pub_self_convention
 +[`wrong_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention
 +[`wrong_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_transmute
 +[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
 +[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
 +[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
 +[`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values
 +[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
 +[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
 +<!-- end autogenerated links to lint list -->
index 6c977b2cacab537db551adb40801a1d277acc0ca,0000000000000000000000000000000000000000..85f94a74ad91d30629d055e8d3f7b357a3537662
mode 100644,000000..100644
--- /dev/null
@@@ -1,253 -1,0 +1,253 @@@
- If you're new to Clippy and don't know where to start the [Clippy book] includes
 +# Contributing to Clippy
 +
 +Hello fellow Rustacean! Great to see your interest in compiler internals and lints!
 +
 +**First**: if you're unsure or afraid of _anything_, just ask or submit the issue or pull request anyway. You won't be
 +yelled at for giving it your best effort. The worst that can happen is that you'll be politely asked to change
 +something. We appreciate any sort of contributions, and don't want a wall of rules to get in the way of that.
 +
 +Clippy welcomes contributions from everyone. There are many ways to contribute to Clippy and the following document
 +explains how you can contribute and how to get started.  If you have any questions about contributing or need help with
 +anything, feel free to ask questions on issues or visit the `#clippy` on [Zulip].
 +
 +All contributors are expected to follow the [Rust Code of Conduct].
 +
 +- [Contributing to Clippy](#contributing-to-clippy)
 +  - [The Clippy book](#the-clippy-book)
 +  - [High level approach](#high-level-approach)
 +  - [Finding something to fix/improve](#finding-something-to-fiximprove)
 +  - [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work)
 +    - [IntelliJ Rust](#intellij-rust)
 +    - [Rust Analyzer](#rust-analyzer)
 +  - [How Clippy works](#how-clippy-works)
 +  - [Issue and PR triage](#issue-and-pr-triage)
 +  - [Bors and Homu](#bors-and-homu)
 +  - [Contributions](#contributions)
 +
 +[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy
 +[Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct
 +
 +## The Clippy book
 +
++If you're new to Clippy and don't know where to start, the [Clippy book] includes
 +a [developer guide] and is a good place to start your journey.
 +
 +[Clippy book]: https://doc.rust-lang.org/nightly/clippy/index.html
 +[developer guide]: https://doc.rust-lang.org/nightly/clippy/development/index.html
 +
 +## High level approach
 +
 +1. Find something to fix/improve
 +2. Change code (likely some file in `clippy_lints/src/`)
 +3. Follow the instructions in the [Basics docs](book/src/development/basics.md)
 +   to get set up
 +4. Run `cargo test` in the root directory and wiggle code until it passes
 +5. Open a PR (also can be done after 2. if you run into problems)
 +
 +## Finding something to fix/improve
 +
 +All issues on Clippy are mentored, if you want help simply ask someone from the
 +Clippy team directly by mentioning them in the issue or over on [Zulip]. All
 +currently active team members can be found
 +[here](https://github.com/rust-lang/highfive/blob/master/highfive/configs/rust-lang/rust-clippy.json#L3)
 +
 +Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy
 +issues. You can use `@rustbot claim` to assign the issue to yourself.
 +
 +There are also some abandoned PRs, marked with [`S-inactive-closed`].
 +Pretty often these PRs are nearly completed and just need some extra steps
 +(formatting, addressing review comments, ...) to be merged. If you want to
 +complete such a PR, please leave a comment in the PR and open a new one based
 +on it.
 +
 +Issues marked [`T-AST`] involve simple matching of the syntax tree structure,
 +and are generally easier than [`T-middle`] issues, which involve types
 +and resolved paths.
 +
 +[`T-AST`] issues will generally need you to match against a predefined syntax structure.
 +To figure out how this syntax structure is encoded in the AST, it is recommended to run
 +`rustc -Z unpretty=ast-tree` on an example of the structure and compare with the [nodes in the AST docs].
 +Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting].
 +But we can make it nest-less by using [let chains], [like this][nest-less].
 +
 +[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good-first-issue`]
 +first. Sometimes they are only somewhat involved code wise, but not difficult per-se.
 +Note that [`E-medium`] issues may require some knowledge of Clippy internals or some
 +debugging to find the actual problem behind the issue.
 +
 +[`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a
 +lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of
 +an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful.
 +
 +[`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue
 +[`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed
 +[`T-AST`]: https://github.com/rust-lang/rust-clippy/labels/T-AST
 +[`T-middle`]: https://github.com/rust-lang/rust-clippy/labels/T-middle
 +[`E-medium`]: https://github.com/rust-lang/rust-clippy/labels/E-medium
 +[`ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty
 +[nodes in the AST docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/
 +[deep-nesting]: https://github.com/rust-lang/rust-clippy/blob/5e4f0922911536f80d9591180fa604229ac13939/clippy_lints/src/mem_forget.rs#L31-L45
 +[let chains]: https://github.com/rust-lang/rust/pull/94927
 +[nest-less]: https://github.com/rust-lang/rust-clippy/blob/5e4f0922911536f80d9591180fa604229ac13939/clippy_lints/src/bit_mask.rs#L133-L159
 +
 +## Getting code-completion for rustc internals to work
 +
 +### IntelliJ Rust
 +Unfortunately, [`IntelliJ Rust`][IntelliJ_rust_homepage] does not (yet?) understand how Clippy uses compiler-internals
 +using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not
 +available via a `rustup` component at the time of writing.
 +To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via
 +`git clone https://github.com/rust-lang/rust/`.
 +Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies
 +which `IntelliJ Rust` will be able to understand.
 +Run `cargo dev setup intellij --repo-path <repo-path>` where `<repo-path>` is a path to the rustc repo
 +you just cloned.
 +The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
 +Clippy's `Cargo.toml`s and should allow `IntelliJ Rust` to understand most of the types that Clippy uses.
 +Just make sure to remove the dependencies again before finally making a pull request!
 +
 +[rustc_repo]: https://github.com/rust-lang/rust/
 +[IntelliJ_rust_homepage]: https://intellij-rust.github.io/
 +
 +### Rust Analyzer
 +For [`rust-analyzer`][ra_homepage] to work correctly make sure that in the `rust-analyzer` configuration you set
 +
 +```json
 +{ "rust-analyzer.rustc.source": "discover" }
 +```
 +
 +You should be able to see information on things like `Expr` or `EarlyContext` now if you hover them, also
 +a lot more type hints.
 +
 +To have `rust-analyzer` also work in the `clippy_dev` and `lintcheck` crates, add the following configuration
 +
 +```json
 +{
 +    "rust-analyzer.linkedProjects": [
 +        "./Cargo.toml",
 +        "clippy_dev/Cargo.toml",
 +        "lintcheck/Cargo.toml",
 +    ]
 +}
 +```
 +
 +[ra_homepage]: https://rust-analyzer.github.io/
 +
 +## How Clippy works
 +
 +[`clippy_lints/src/lib.rs`][lint_crate_entry] imports all the different lint modules and registers in the [`LintStore`].
 +For example, the [`else_if_without_else`][else_if_without_else] lint is registered like this:
 +
 +```rust
 +// ./clippy_lints/src/lib.rs
 +
 +// ...
 +pub mod else_if_without_else;
 +// ...
 +
 +pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
 +    // ...
 +    store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse);
 +    // ...
 +
 +    store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
 +        // ...
 +        LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
 +        // ...
 +    ]);
 +}
 +```
 +
 +The [`rustc_lint::LintStore`][`LintStore`] provides two methods to register lints:
 +[register_early_pass][reg_early_pass] and [register_late_pass][reg_late_pass]. Both take an object
 +that implements an [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass] respectively. This is done in
 +every single lint. It's worth noting that the majority of `clippy_lints/src/lib.rs` is autogenerated by `cargo dev
 +update_lints`. When you are writing your own lint, you can use that script to save you some time.
 +
 +```rust
 +// ./clippy_lints/src/else_if_without_else.rs
 +
 +use rustc_lint::{EarlyLintPass, EarlyContext};
 +
 +// ...
 +
 +pub struct ElseIfWithoutElse;
 +
 +// ...
 +
 +impl EarlyLintPass for ElseIfWithoutElse {
 +    // ... the functions needed, to make the lint work
 +}
 +```
 +
 +The difference between `EarlyLintPass` and `LateLintPass` is that the methods of the `EarlyLintPass` trait only provide
 +AST information. The methods of the `LateLintPass` trait are executed after type checking and contain type information
 +via the `LateContext` parameter.
 +
 +That's why the `else_if_without_else` example uses the `register_early_pass` function. Because the
 +[actual lint logic][else_if_without_else] does not depend on any type information.
 +
 +[lint_crate_entry]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs
 +[else_if_without_else]: https://github.com/rust-lang/rust-clippy/blob/4253aa7137cb7378acc96133c787e49a345c2b3c/clippy_lints/src/else_if_without_else.rs
 +[`LintStore`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html
 +[reg_early_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_early_pass
 +[reg_late_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_late_pass
 +[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
 +
 +## Issue and PR triage
 +
 +Clippy is following the [Rust triage procedure][triage] for issues and pull
 +requests.
 +
 +However, we are a smaller project with all contributors being volunteers
 +currently. Between writing new lints, fixing issues, reviewing pull requests and
 +responding to issues there may not always be enough time to stay on top of it
 +all.
 +
 +Our highest priority is fixing [crashes][l-crash] and [bugs][l-bug], for example
 +an ICE in a popular crate that many other crates depend on. We don't
 +want Clippy to crash on your code and we want it to be as reliable as the
 +suggestions from Rust compiler errors.
 +
 +We have prioritization labels and a sync-blocker label, which are described below.
 +- [P-low][p-low]: Requires attention (fix/response/evaluation) by a team member but isn't urgent.
 +- [P-medium][p-medium]: Should be addressed by a team member until the next sync.
 +- [P-high][p-high]: Should be immediately addressed and will require an out-of-cycle sync or a backport.
 +- [L-sync-blocker][l-sync-blocker]: An issue that "blocks" a sync.
 +Or rather: before the sync this should be addressed,
 +e.g. by removing a lint again, so it doesn't hit beta/stable.
 +
 +## Bors and Homu
 +
 +We use a bot powered by [Homu][homu] to help automate testing and landing of pull
 +requests in Clippy. The bot's username is @bors.
 +
 +You can find the Clippy bors queue [here][homu_queue].
 +
 +If you have @bors permissions, you can find an overview of the available
 +commands [here][homu_instructions].
 +
 +[triage]: https://forge.rust-lang.org/release/triage-procedure.html
 +[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash
 +[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug
 +[p-low]: https://github.com/rust-lang/rust-clippy/labels/P-low
 +[p-medium]: https://github.com/rust-lang/rust-clippy/labels/P-medium
 +[p-high]: https://github.com/rust-lang/rust-clippy/labels/P-high
 +[l-sync-blocker]: https://github.com/rust-lang/rust-clippy/labels/L-sync-blocker
 +[homu]: https://github.com/rust-lang/homu
 +[homu_instructions]: https://bors.rust-lang.org/
 +[homu_queue]: https://bors.rust-lang.org/queue/clippy
 +
 +## Contributions
 +
 +Contributions to Clippy should be made in the form of GitHub pull requests. Each pull request will
 +be reviewed by a core contributor (someone with permission to land patches) and either landed in the
 +main tree or given feedback for changes that would be required.
 +
 +All code in this repository is under the [Apache-2.0] or the [MIT] license.
 +
 +<!-- adapted from https://github.com/servo/servo/blob/master/CONTRIBUTING.md -->
 +
 +[Apache-2.0]: https://www.apache.org/licenses/LICENSE-2.0
 +[MIT]: https://opensource.org/licenses/MIT
index b1e843bc7f4c86173dd8db2893921374abe5d384,0000000000000000000000000000000000000000..3c3f368a529b15755ca687375d1becf2a635a8a0
mode 100644,000000..100644
--- /dev/null
@@@ -1,740 -1,0 +1,759 @@@
- `tests/ui/min_rust_version_attr.rs` which verifies that the lint isn't emitted
- if the project's MSRV is lower.
 +# 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)
 +    - [Defining Our Lint](#defining-our-lint)
 +      - [Standalone](#standalone)
 +      - [Specific Type](#specific-type)
 +      - [Tests Location](#tests-location)
 +  - [Testing](#testing)
 +    - [Cargo lints](#cargo-lints)
 +  - [Rustfix tests](#rustfix-tests)
 +  - [Testing manually](#testing-manually)
 +  - [Lint declaration](#lint-declaration)
 +  - [Lint registration](#lint-registration)
 +  - [Lint passes](#lint-passes)
 +  - [Emitting a lint](#emitting-a-lint)
 +  - [Adding the lint logic](#adding-the-lint-logic)
 +  - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv)
 +  - [Author lint](#author-lint)
 +  - [Print HIR lint](#print-hir-lint)
 +  - [Documentation](#documentation)
 +  - [Running rustfmt](#running-rustfmt)
 +  - [Debugging](#debugging)
 +  - [PR Checklist](#pr-checklist)
 +  - [Adding configuration to a lint](#adding-configuration-to-a-lint)
 +  - [Cheat Sheet](#cheat-sheet)
 +
 +## Setup
 +
 +See the [Basics](basics.md#get-the-code) documentation.
 +
 +## Getting Started
 +
 +There is a bit of boilerplate code that needs to be set up when creating a new
 +lint. Fortunately, you can use the Clippy dev tools to handle this for you. We
 +are naming our new lint `foo_functions` (lints are generally written in snake
 +case), and we don't need type information, so it will have an early pass type
 +(more on this later). If you're unsure if the name you chose fits the lint,
 +take a look at our [lint naming guidelines][lint_naming].
 +
 +## Defining Our Lint
 +To get started, there are two ways to define our lint.
 +
 +### Standalone
 +Command: `cargo dev new_lint --name=foo_functions --pass=early --category=pedantic`
 +(category will default to nursery if not provided)
 +
 +This command will create a new file: `clippy_lints/src/foo_functions.rs`, as well
 +as [register the lint](#lint-registration).
 +
 +### Specific Type
 +Command: `cargo dev new_lint --name=foo_functions --type=functions --category=pedantic`
 +
 +This command will create a new file: `clippy_lints/src/{type}/foo_functions.rs`.
 +
 +Notice how this command has a `--type` flag instead of `--pass`. Unlike a standalone
 +definition, this lint won't be registered in the traditional sense. Instead, you will
 +call your lint from within the type's lint pass, found in `clippy_lints/src/{type}/mod.rs`.
 +
 +A "type" is just the name of a directory in `clippy_lints/src`, like `functions` in
 +the example command. These are groupings of lints with common behaviors, so if your
 +lint falls into one, it would be best to add it to that type.
 +
 +### Tests Location
 +Both commands will create a file: `tests/ui/foo_functions.rs`. 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
 +#![allow(unused)]
 +#![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:_ 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
 +
 +## 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`].
 +This can be accessed later as `msrvs::STR_STRIP_PREFIX`, for example.
 +
 +```rust
 +msrv_aliases! {
 +    ..
 +    1,45,0 { STR_STRIP_PREFIX }
 +}
 +```
 +
 +In order to access the project-configured MSRV, you need to have an `msrv` field
 +in the LintPass struct, and a constructor to initialize the field. The `msrv`
 +value is passed to the constructor in `clippy_lints/lib.rs`.
 +
 +```rust
 +pub struct ManualStrip {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl ManualStrip {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +```
 +
 +The project's MSRV can then be matched against the feature MSRV in the LintPass
 +using the `meets_msrv` utility function.
 +
 +``` rust
 +if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) {
 +    return;
 +}
 +```
 +
 +The project's MSRV can also be specified as an inner attribute, which overrides
 +the value from `clippy.toml`. This can be accounted for using the
 +`extract_msrv_attr!(LintContext)` macro and passing
 +`LateContext`/`EarlyContext`.
 +
 +```rust
 +impl<'tcx> LateLintPass<'tcx> for ManualStrip {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        ...
 +    }
 +    extract_msrv_attr!(LateContext);
 +}
 +```
 +
 +Once the `msrv` is added to the lint, a relevant test case should be added to
++the lint's test file, `tests/ui/manual_strip.rs` in this example. It should
++have a case for the version below the MSRV and one with the same contents but
++for the MSRV version itself.
++
++```rust
++#![feature(custom_inner_attributes)]
++
++...
++
++fn msrv_1_44() {
++    #![clippy::msrv = "1.44"]
++
++    /* something that would trigger the lint */
++}
++
++fn msrv_1_45() {
++    #![clippy::msrv = "1.45"]
++
++    /* something that would trigger the lint */
++}
++```
 +
 +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),
 +    ...
 +}
 +```
 +
 +[`clippy_utils::msrvs`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/msrvs/index.html
 +
 +## Author lint
 +
 +If you have trouble implementing your lint, there is also the internal `author`
 +lint to generate Clippy code that detects the offending pattern. It does not
 +work for all of the Rust syntax, but can give a good starting point.
 +
 +The quickest way to use it, is the [Rust playground:
 +play.rust-lang.org][author_example]. Put the code you want to lint into the
 +editor and add the `#[clippy::author]` attribute above the item. Then run Clippy
 +via `Tools -> Clippy` and you should see the generated code in the output below.
 +
 +[Here][author_example] is an example on the playground.
 +
 +If the command was executed successfully, you can copy the code over to where
 +you are implementing your lint.
 +
 +[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
 +
 +## Print HIR lint
 +
 +To implement a lint, it's helpful to first understand the internal
 +representation that rustc uses. Clippy has the `#[clippy::dump]` attribute that
 +prints the [_High-Level Intermediate Representation (HIR)_] of the item,
 +statement, or expression that the attribute is attached to. To attach the
 +attribute to expressions you often need to enable
 +`#![feature(stmt_expr_attributes)]`.
 +
 +[Here][print_hir_example] you can find an example, just select _Tools_ and run
 +_Clippy_.
 +
 +[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html
 +[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb
 +
 +## Documentation
 +
 +The final thing before submitting our PR is to add some documentation to our
 +lint declaration.
 +
 +Please document your lint with a doc comment akin to the following:
 +
 +```rust
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for ... (describe what the lint matches).
 +    ///
 +    /// ### Why is this bad?
 +    /// Supply the reason for linting the code.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust,ignore
 +    /// // A short example of code that triggers the lint
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// // A short example of improved code that doesn't trigger the lint
 +    /// ```
 +    #[clippy::version = "1.29.0"]
 +    pub FOO_FUNCTIONS,
 +    pedantic,
 +    "function named `foo`, which is not a descriptive name"
 +}
 +```
 +
 +Once your lint is merged, this documentation will show up in the [lint
 +list][lint_list].
 +
 +[lint_list]: https://rust-lang.github.io/rust-clippy/master/index.html
 +
 +## Running rustfmt
 +
 +[Rustfmt] is a tool for formatting Rust code according to style guidelines. Your
 +code has to be formatted by `rustfmt` before a PR can be merged. Clippy uses
 +nightly `rustfmt` in the CI.
 +
 +It can be installed via `rustup`:
 +
 +```bash
 +rustup component add rustfmt --toolchain=nightly
 +```
 +
 +Use `cargo dev fmt` to format the whole codebase. Make sure that `rustfmt` is
 +installed for the nightly toolchain.
 +
 +[Rustfmt]: https://github.com/rust-lang/rustfmt
 +
 +## Debugging
 +
 +If you want to debug parts of your lint implementation, you can use the [`dbg!`]
 +macro anywhere in your code. Running the tests should then include the debug
 +output in the `stdout` part.
 +
 +[`dbg!`]: https://doc.rust-lang.org/std/macro.dbg.html
 +
 +## PR Checklist
 +
 +Before submitting your PR make sure you followed all of the basic requirements:
 +
 +<!-- Sync this with `.github/PULL_REQUEST_TEMPLATE` -->
 +
 +- \[ ] Followed [lint naming conventions][lint_naming]
 +- \[ ] Added passing UI tests (including committed `.stderr` file)
 +- \[ ] `cargo test` passes locally
 +- \[ ] Executed `cargo dev update_lints`
 +- \[ ] Added lint documentation
 +- \[ ] Run `cargo dev fmt`
 +
 +## Adding configuration to a lint
 +
 +Clippy supports the configuration of lints values using a `clippy.toml` file in
 +the workspace directory. Adding a configuration to a lint can be useful for
 +thresholds or to constrain some behavior that can be seen as a false positive
 +for some users. Adding a configuration is done in the following steps:
 +
 +1. Adding a new configuration entry to [`clippy_lints::utils::conf`] 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]. 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`].
 +    2. The configuration itself will be tested separately in [`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.
 +
 +[`clippy_lints::utils::conf`]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/conf.rs
 +[`clippy_lints` lib file]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs
 +[`tests/ui`]: https://github.com/rust-lang/rust-clippy/blob/master/tests/ui
 +[`tests/ui-toml`]: https://github.com/rust-lang/rust-clippy/blob/master/tests/ui-toml
 +
 +## Cheat Sheet
 +
 +Here are some pointers to things you are likely going to need for every lint:
 +
 +* [Clippy utils][utils] - Various helper functions. Maybe the function you need
 +  is already in here ([`is_type_diagnostic_item`], [`implements_trait`],
 +  [`snippet`], etc)
 +* [Clippy diagnostics][diagnostics]
 +* [Let chains][let-chains]
 +* [`from_expansion`][from_expansion] and
 +  [`in_external_macro`][in_external_macro]
 +* [`Span`][span]
 +* [`Applicability`][applicability]
 +* [Common tools for writing lints](common_tools_writing_lints.md) helps with
 +  common operations
 +* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler
 +  concepts
 +* [The nightly rustc docs][nightly_docs] which has been linked to throughout
 +  this guide
 +
 +For `EarlyLintPass` lints:
 +
 +* [`EarlyLintPass`][early_lint_pass]
 +* [`rustc_ast::ast`][ast]
 +
 +For `LateLintPass` lints:
 +
 +* [`LateLintPass`][late_lint_pass]
 +* [`Ty::TyKind`][ty]
 +
 +While most of Clippy's lint utils are documented, most of rustc's internals lack
 +documentation currently. This is unfortunate, but in most cases you can probably
 +get away with copying things from existing similar lints. If you are stuck,
 +don't hesitate to ask on [Zulip] or in the issue/PR.
 +
 +[utils]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html
 +[`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html
 +[`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html
 +[`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html
 +[let-chains]: https://github.com/rust-lang/rust/pull/94927
 +[from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
 +[in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html
 +[span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html
 +[applicability]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html
 +[rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/
 +[nightly_docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/
 +[ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html
 +[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/index.html
 +[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy
index 44ba6e32755e319b24d5797c0b95c42baddaad20,0000000000000000000000000000000000000000..6fb53236e6f1aee1e6e5355aae584aee07785df5
mode 100644,000000..100644
--- /dev/null
@@@ -1,192 -1,0 +1,193 @@@
- For example, this is necessary, if you fix a typo in an error message of a lint
 +# Basics for hacking on Clippy
 +
 +This document explains the basics for hacking on Clippy. Besides others, this
 +includes how to build and test Clippy. For a more in depth description on the
 +codebase take a look at [Adding Lints] or [Common Tools].
 +
 +[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/adding_lints.md
 +[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md
 +
 +- [Basics for hacking on Clippy](#basics-for-hacking-on-clippy)
 +  - [Get the Code](#get-the-code)
 +  - [Building and Testing](#building-and-testing)
 +  - [`cargo dev`](#cargo-dev)
 +  - [lintcheck](#lintcheck)
 +  - [PR](#pr)
 +  - [Common Abbreviations](#common-abbreviations)
 +  - [Install from source](#install-from-source)
 +
 +## Get the Code
 +
 +First, make sure you have checked out the latest version of Clippy. If this is
 +your first time working on Clippy, create a fork of the repository and clone it
 +afterwards with the following command:
 +
 +```bash
 +git clone git@github.com:<your-username>/rust-clippy
 +```
 +
 +If you've already cloned Clippy in the past, update it to the latest version:
 +
 +```bash
 +# If the upstream remote has not been added yet
 +git remote add upstream https://github.com/rust-lang/rust-clippy
 +# upstream has to be the remote of the rust-lang/rust-clippy repo
 +git fetch upstream
 +# make sure that you are on the master branch
 +git checkout master
 +# rebase your master branch on the upstream master
 +git rebase upstream/master
 +# push to the master branch of your fork
 +git push
 +```
 +
 +## Building and Testing
 +
 +You can build and test Clippy like every other Rust project:
 +
 +```bash
 +cargo build  # builds Clippy
 +cargo test   # tests Clippy
 +```
 +
 +Since Clippy's test suite is pretty big, there are some commands that only run a
 +subset of Clippy's tests:
 +
 +```bash
 +# only run UI tests
 +cargo uitest
 +# only run UI tests starting with `test_`
 +TESTNAME="test_" cargo uitest
 +# only run dogfood tests
 +cargo dev dogfood
 +```
 +
 +If the output of a [UI test] differs from the expected output, you can update
 +the reference file with:
 +
 +```bash
 +cargo dev bless
 +```
 +
- More about intellij command usage and reasons
- [here](https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md#intellij-rust)
++For example, this is necessary if you fix a typo in an error message of a lint,
 +or if you modify a test file to add a test case.
 +
 +> _Note:_ This command may update more files than you intended. In that case
 +> only commit the files you wanted to update.
 +
 +[UI test]: https://rustc-dev-guide.rust-lang.org/tests/adding.html#guide-to-the-ui-tests
 +
 +## `cargo dev`
 +
 +Clippy has some dev tools to make working on Clippy more convenient. These tools
 +can be accessed through the `cargo dev` command. Available tools are listed
 +below. To get more information about these commands, just call them with
 +`--help`.
 +
 +```bash
 +# formats the whole Clippy codebase and all tests
 +cargo dev fmt
 +# register or update lint names/groups/...
 +cargo dev update_lints
 +# create a new lint and register it
 +cargo dev new_lint
 +# deprecate a lint and attempt to remove code relating to it
 +cargo dev deprecate
 +# automatically formatting all code before each commit
 +cargo dev setup git-hook
 +# (experimental) Setup Clippy to work with IntelliJ-Rust
 +cargo dev setup intellij
 +# runs the `dogfood` tests
 +cargo dev dogfood
 +```
 +
++More about [intellij] command usage and reasons.
++
++[intellij]: https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md#intellij-rust
 +
 +## lintcheck
 +
 +`cargo lintcheck` will build and run clippy on a fixed set of crates and
 +generate a log of the results.  You can `git diff` the updated log against its
 +previous version and see what impact your lint made on a small set of crates.
 +If you add a new lint, please audit the resulting warnings and make sure there
 +are no false positives and that the suggestions are valid.
 +
 +Refer to the tools [README] for more details.
 +
 +[README]: https://github.com/rust-lang/rust-clippy/blob/master/lintcheck/README.md
 +
 +## PR
 +
 +We follow a rustc no merge-commit policy. See
 +<https://rustc-dev-guide.rust-lang.org/contributing.html#opening-a-pr>.
 +
 +## Common Abbreviations
 +
 +| Abbreviation | Meaning                                |
 +| ------------ | -------------------------------------- |
 +| UB           | Undefined Behavior                     |
 +| FP           | False Positive                         |
 +| FN           | False Negative                         |
 +| ICE          | Internal Compiler Error                |
 +| AST          | Abstract Syntax Tree                   |
 +| MIR          | Mid-Level Intermediate Representation  |
 +| HIR          | High-Level Intermediate Representation |
 +| TCX          | Type context                           |
 +
 +This is a concise list of abbreviations that can come up during Clippy
 +development. An extensive general list can be found in the [rustc-dev-guide
 +glossary][glossary]. Always feel free to ask if an abbreviation or meaning is
 +unclear to you.
 +
 +## Install from source
 +
 +If you are hacking on Clippy and want to install it from source, do the
 +following:
 +
 +First, take note of the toolchain
 +[override](https://rust-lang.github.io/rustup/overrides.html) in
 +`/rust-toolchain`. We will use this override to install Clippy into the right
 +toolchain.
 +
 +> Tip: You can view the active toolchain for the current directory with `rustup
 +> show active-toolchain`.
 +
 +From the Clippy project root, run the following command to build the Clippy
 +binaries and copy them into the toolchain directory. This will override the
 +currently installed Clippy component.
 +
 +```terminal
 +cargo build --release --bin cargo-clippy --bin clippy-driver -Zunstable-options --out-dir "$(rustc --print=sysroot)/bin"
 +```
 +
 +Now you may run `cargo clippy` in any project, using the toolchain where you
 +just installed Clippy.
 +
 +```terminal
 +cd my-project
 +cargo +nightly-2021-07-01 clippy
 +```
 +
 +...or `clippy-driver`
 +
 +```terminal
 +clippy-driver +nightly-2021-07-01 <filename>
 +```
 +
 +If you need to restore the default Clippy installation, run the following (from
 +the Clippy project root).
 +
 +```terminal
 +rustup component remove clippy
 +rustup component add clippy
 +```
 +
 +> **DO NOT** install using `cargo install --path . --force` since this will
 +> overwrite rustup
 +> [proxies](https://rust-lang.github.io/rustup/concepts/proxies.html). That is,
 +> `~/.cargo/bin/cargo-clippy` and `~/.cargo/bin/clippy-driver` should be hard or
 +> soft links to `~/.cargo/bin/rustup`. You can repair these by running `rustup
 +> update`.
 +
 +[glossary]: https://rustc-dev-guide.rust-lang.org/appendix/glossary.html
index 2e0794f12fa19c2eab6c7f1463ed8e0c9530637c,0000000000000000000000000000000000000000..535c25e69f1bb1df546fbf84ebd8bd2d3984329c
mode 100644,000000..100644
--- /dev/null
@@@ -1,65 -1,0 +1,65 @@@
-             .map(|entry| mtime(&entry.path()))
 +use std::ffi::OsStr;
 +use std::num::ParseIntError;
 +use std::path::Path;
 +use std::process::Command;
 +use std::thread;
 +use std::time::{Duration, SystemTime};
 +
 +/// # Panics
 +///
 +/// Panics if the python commands could not be spawned
 +pub fn run(port: u16, lint: Option<&String>) -> ! {
 +    let mut url = Some(match lint {
 +        None => format!("http://localhost:{port}"),
 +        Some(lint) => format!("http://localhost:{port}/#{lint}"),
 +    });
 +
 +    loop {
 +        if mtime("util/gh-pages/lints.json") < mtime("clippy_lints/src") {
 +            Command::new("cargo")
 +                .arg("collect-metadata")
 +                .spawn()
 +                .unwrap()
 +                .wait()
 +                .unwrap();
 +        }
 +        if let Some(url) = url.take() {
 +            thread::spawn(move || {
 +                Command::new("python3")
 +                    .arg("-m")
 +                    .arg("http.server")
 +                    .arg(port.to_string())
 +                    .current_dir("util/gh-pages")
 +                    .spawn()
 +                    .unwrap();
 +                // Give some time for python to start
 +                thread::sleep(Duration::from_millis(500));
 +                // Launch browser after first export.py has completed and http.server is up
 +                let _result = opener::open(url);
 +            });
 +        }
 +        thread::sleep(Duration::from_millis(1000));
 +    }
 +}
 +
 +fn mtime(path: impl AsRef<Path>) -> SystemTime {
 +    let path = path.as_ref();
 +    if path.is_dir() {
 +        path.read_dir()
 +            .into_iter()
 +            .flatten()
 +            .flatten()
++            .map(|entry| mtime(entry.path()))
 +            .max()
 +            .unwrap_or(SystemTime::UNIX_EPOCH)
 +    } else {
 +        path.metadata()
 +            .and_then(|metadata| metadata.modified())
 +            .unwrap_or(SystemTime::UNIX_EPOCH)
 +    }
 +}
 +
 +#[allow(clippy::missing_errors_doc)]
 +pub fn validate_port(arg: &OsStr) -> Result<(), ParseIntError> {
 +    arg.to_string_lossy().parse::<u16>().map(|_| ())
 +}
index b64e79733eb2453ecc1c1741b71eb6d12a7e858a,0000000000000000000000000000000000000000..efdb158c21e9be447bf11c0e8ba8262f48bfd805
mode 100644,000000..100644
--- /dev/null
@@@ -1,216 -1,0 +1,209 @@@
-     let rustc_source_dir = match check_and_get_rustc_dir(rustc_path) {
-         Ok(path) => path,
-         Err(_) => return,
 +use std::fs;
 +use std::fs::File;
 +use std::io::prelude::*;
 +use std::path::{Path, PathBuf};
 +
 +// This module takes an absolute path to a rustc repo and alters the dependencies to point towards
 +// the respective rustc subcrates instead of using extern crate xyz.
 +// This allows IntelliJ to analyze rustc internals and show proper information inside Clippy
 +// code. See https://github.com/rust-lang/rust-clippy/issues/5514 for details
 +
 +const RUSTC_PATH_SECTION: &str = "[target.'cfg(NOT_A_PLATFORM)'.dependencies]";
 +const DEPENDENCIES_SECTION: &str = "[dependencies]";
 +
 +const CLIPPY_PROJECTS: &[ClippyProjectInfo] = &[
 +    ClippyProjectInfo::new("root", "Cargo.toml", "src/driver.rs"),
 +    ClippyProjectInfo::new("clippy_lints", "clippy_lints/Cargo.toml", "clippy_lints/src/lib.rs"),
 +    ClippyProjectInfo::new("clippy_utils", "clippy_utils/Cargo.toml", "clippy_utils/src/lib.rs"),
 +];
 +
 +/// Used to store clippy project information to later inject the dependency into.
 +struct ClippyProjectInfo {
 +    /// Only used to display information to the user
 +    name: &'static str,
 +    cargo_file: &'static str,
 +    lib_rs_file: &'static str,
 +}
 +
 +impl ClippyProjectInfo {
 +    const fn new(name: &'static str, cargo_file: &'static str, lib_rs_file: &'static str) -> Self {
 +        Self {
 +            name,
 +            cargo_file,
 +            lib_rs_file,
 +        }
 +    }
 +}
 +
 +pub fn setup_rustc_src(rustc_path: &str) {
-     let mut cargo_content = if let Ok(content) = read_project_file(project.cargo_file) {
-         content
-     } else {
++    let Ok(rustc_source_dir) = check_and_get_rustc_dir(rustc_path) else {
++        return
 +    };
 +
 +    for project in CLIPPY_PROJECTS {
 +        if inject_deps_into_project(&rustc_source_dir, project).is_err() {
 +            return;
 +        }
 +    }
 +
 +    println!("info: the source paths can be removed again with `cargo dev remove intellij`");
 +}
 +
 +fn check_and_get_rustc_dir(rustc_path: &str) -> Result<PathBuf, ()> {
 +    let mut path = PathBuf::from(rustc_path);
 +
 +    if path.is_relative() {
 +        match path.canonicalize() {
 +            Ok(absolute_path) => {
 +                println!("info: the rustc path was resolved to: `{}`", absolute_path.display());
 +                path = absolute_path;
 +            },
 +            Err(err) => {
 +                eprintln!("error: unable to get the absolute path of rustc ({err})");
 +                return Err(());
 +            },
 +        };
 +    }
 +
 +    let path = path.join("compiler");
 +    println!("info: looking for compiler sources at: {}", path.display());
 +
 +    if !path.exists() {
 +        eprintln!("error: the given path does not exist");
 +        return Err(());
 +    }
 +
 +    if !path.is_dir() {
 +        eprintln!("error: the given path is not a directory");
 +        return Err(());
 +    }
 +
 +    Ok(path)
 +}
 +
 +fn inject_deps_into_project(rustc_source_dir: &Path, project: &ClippyProjectInfo) -> Result<(), ()> {
 +    let cargo_content = read_project_file(project.cargo_file)?;
 +    let lib_content = read_project_file(project.lib_rs_file)?;
 +
 +    if inject_deps_into_manifest(rustc_source_dir, project.cargo_file, &cargo_content, &lib_content).is_err() {
 +        eprintln!(
 +            "error: unable to inject dependencies into {} with the Cargo file {}",
 +            project.name, project.cargo_file
 +        );
 +        Err(())
 +    } else {
 +        Ok(())
 +    }
 +}
 +
 +/// `clippy_dev` expects to be executed in the root directory of Clippy. This function
 +/// loads the given file or returns an error. Having it in this extra function ensures
 +/// that the error message looks nice.
 +fn read_project_file(file_path: &str) -> Result<String, ()> {
 +    let path = Path::new(file_path);
 +    if !path.exists() {
 +        eprintln!("error: unable to find the file `{file_path}`");
 +        return Err(());
 +    }
 +
 +    match fs::read_to_string(path) {
 +        Ok(content) => Ok(content),
 +        Err(err) => {
 +            eprintln!("error: the file `{file_path}` could not be read ({err})");
 +            Err(())
 +        },
 +    }
 +}
 +
 +fn inject_deps_into_manifest(
 +    rustc_source_dir: &Path,
 +    manifest_path: &str,
 +    cargo_toml: &str,
 +    lib_rs: &str,
 +) -> std::io::Result<()> {
 +    // do not inject deps if we have already done so
 +    if cargo_toml.contains(RUSTC_PATH_SECTION) {
 +        eprintln!("warn: dependencies are already setup inside {manifest_path}, skipping file");
 +        return Ok(());
 +    }
 +
 +    let extern_crates = lib_rs
 +        .lines()
 +        // only take dependencies starting with `rustc_`
 +        .filter(|line| line.starts_with("extern crate rustc_"))
 +        // we have something like "extern crate foo;", we only care about the "foo"
 +        // extern crate rustc_middle;
 +        //              ^^^^^^^^^^^^
 +        .map(|s| &s[13..(s.len() - 1)]);
 +
 +    let new_deps = extern_crates.map(|dep| {
 +        // format the dependencies that are going to be put inside the Cargo.toml
 +        format!("{dep} = {{ path = \"{}/{dep}\" }}\n", rustc_source_dir.display())
 +    });
 +
 +    // format a new [dependencies]-block with the new deps we need to inject
 +    let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n");
 +    new_deps.for_each(|dep_line| {
 +        all_deps.push_str(&dep_line);
 +    });
 +    all_deps.push_str("\n[dependencies]\n");
 +
 +    // replace "[dependencies]" with
 +    // [dependencies]
 +    // dep1 = { path = ... }
 +    // dep2 = { path = ... }
 +    // etc
 +    let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1);
 +
 +    // println!("{new_manifest}");
 +    let mut file = File::create(manifest_path)?;
 +    file.write_all(new_manifest.as_bytes())?;
 +
 +    println!("info: successfully setup dependencies inside {manifest_path}");
 +
 +    Ok(())
 +}
 +
 +pub fn remove_rustc_src() {
 +    for project in CLIPPY_PROJECTS {
 +        remove_rustc_src_from_project(project);
 +    }
 +}
 +
 +fn remove_rustc_src_from_project(project: &ClippyProjectInfo) -> bool {
-     let section_start = if let Some(section_start) = cargo_content.find(RUSTC_PATH_SECTION) {
-         section_start
-     } else {
++    let Ok(mut cargo_content) = read_project_file(project.cargo_file) else {
 +        return false;
 +    };
-     let end_point = if let Some(end_point) = cargo_content.find(DEPENDENCIES_SECTION) {
-         end_point
-     } else {
++    let Some(section_start) = cargo_content.find(RUSTC_PATH_SECTION) else {
 +        println!(
 +            "info: dependencies could not be found in `{}` for {}, skipping file",
 +            project.cargo_file, project.name
 +        );
 +        return true;
 +    };
 +
++    let Some(end_point) = cargo_content.find(DEPENDENCIES_SECTION) else {
 +        eprintln!(
 +            "error: the end of the rustc dependencies section could not be found in `{}`",
 +            project.cargo_file
 +        );
 +        return false;
 +    };
 +
 +    cargo_content.replace_range(section_start..end_point, "");
 +
 +    match File::create(project.cargo_file) {
 +        Ok(mut file) => {
 +            file.write_all(cargo_content.as_bytes()).unwrap();
 +            println!("info: successfully removed dependencies inside {}", project.cargo_file);
 +            true
 +        },
 +        Err(err) => {
 +            eprintln!(
 +                "error: unable to open file `{}` to remove rustc dependencies for {} ({err})",
 +                project.cargo_file, project.name
 +            );
 +            false
 +        },
 +    }
 +}
index 0eb443167ecf3ffcb4f26d8f1f42007cffe55b43,0000000000000000000000000000000000000000..e690bc369cd4337203eef2e1c2e6d7ba25834751
mode 100644,000000..100644
--- /dev/null
@@@ -1,1406 -1,0 +1,1404 @@@
-             &format!("clippy_lints/src/lib.register_{lint_group}.rs"),
 +use crate::clippy_project_root;
 +use aho_corasick::AhoCorasickBuilder;
 +use indoc::writedoc;
 +use itertools::Itertools;
 +use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
 +use std::collections::{BTreeSet, HashMap, HashSet};
 +use std::ffi::OsStr;
 +use std::fmt::Write;
 +use std::fs::{self, OpenOptions};
 +use std::io::{self, Read, Seek, SeekFrom, Write as _};
 +use std::ops::Range;
 +use std::path::{Path, PathBuf};
 +use walkdir::{DirEntry, WalkDir};
 +
 +const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\
 +     // Use that command to update this file and do not edit by hand.\n\
 +     // Manual edits will be overwritten.\n\n";
 +
 +const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
 +
 +#[derive(Clone, Copy, PartialEq, Eq)]
 +pub enum UpdateMode {
 +    Check,
 +    Change,
 +}
 +
 +/// Runs the `update_lints` command.
 +///
 +/// This updates various generated values from the lint source code.
 +///
 +/// `update_mode` indicates if the files should be updated or if updates should be checked for.
 +///
 +/// # Panics
 +///
 +/// Panics if a file path could not read from or then written to
 +pub fn update(update_mode: UpdateMode) {
 +    let (lints, deprecated_lints, renamed_lints) = gather_all();
 +    generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints);
 +}
 +
 +fn generate_lint_files(
 +    update_mode: UpdateMode,
 +    lints: &[Lint],
 +    deprecated_lints: &[DeprecatedLint],
 +    renamed_lints: &[RenamedLint],
 +) {
 +    let internal_lints = Lint::internal_lints(lints);
 +    let mut usable_lints = Lint::usable_lints(lints);
 +    usable_lints.sort_by_key(|lint| lint.name.clone());
 +
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("README.md"),
 +        "[There are over ",
 +        " lints included in this crate!]",
 +        |res| {
 +            write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
 +        },
 +    );
 +
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("book/src/README.md"),
 +        "[There are over ",
 +        " lints included in this crate!]",
 +        |res| {
 +            write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
 +        },
 +    );
 +
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("CHANGELOG.md"),
 +        "<!-- begin autogenerated links to lint list -->\n",
 +        "<!-- end autogenerated links to lint list -->",
 +        |res| {
 +            for lint in usable_lints
 +                .iter()
 +                .map(|l| &*l.name)
 +                .chain(deprecated_lints.iter().map(|l| &*l.name))
 +                .chain(
 +                    renamed_lints
 +                        .iter()
 +                        .map(|l| l.old_name.strip_prefix("clippy::").unwrap_or(&l.old_name)),
 +                )
 +                .sorted()
 +            {
 +                writeln!(res, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap();
 +            }
 +        },
 +    );
 +
 +    // This has to be in lib.rs, otherwise rustfmt doesn't work
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("clippy_lints/src/lib.rs"),
 +        "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n",
 +        "// end lints modules, do not remove this comment, it’s used in `update_lints`",
 +        |res| {
 +            for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() {
 +                writeln!(res, "mod {lint_mod};").unwrap();
 +            }
 +        },
 +    );
 +
 +    process_file(
 +        "clippy_lints/src/lib.register_lints.rs",
 +        update_mode,
 +        &gen_register_lint_list(internal_lints.iter(), usable_lints.iter()),
 +    );
 +    process_file(
 +        "clippy_lints/src/lib.deprecated.rs",
 +        update_mode,
 +        &gen_deprecated(deprecated_lints),
 +    );
 +
 +    let all_group_lints = usable_lints.iter().filter(|l| {
 +        matches!(
 +            &*l.group,
 +            "correctness" | "suspicious" | "style" | "complexity" | "perf"
 +        )
 +    });
 +    let content = gen_lint_group_list("all", all_group_lints);
 +    process_file("clippy_lints/src/lib.register_all.rs", update_mode, &content);
 +
 +    update_docs(update_mode, &usable_lints);
 +
 +    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(
-             $($(let $capture =)? if let Some(LintDeclSearchResult {
++            format!("clippy_lints/src/lib.register_{lint_group}.rs"),
 +            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);
 +}
 +
 +fn update_docs(update_mode: UpdateMode, usable_lints: &[Lint]) {
 +    replace_region_in_file(update_mode, Path::new("src/docs.rs"), "docs! {\n", "\n}\n", |res| {
 +        for name in usable_lints.iter().map(|lint| lint.name.clone()).sorted() {
 +            writeln!(res, r#"    "{name}","#).unwrap();
 +        }
 +    });
 +
 +    if update_mode == UpdateMode::Check {
 +        let mut extra = BTreeSet::new();
 +        let mut lint_names = usable_lints
 +            .iter()
 +            .map(|lint| lint.name.clone())
 +            .collect::<BTreeSet<_>>();
 +        for file in std::fs::read_dir("src/docs").unwrap() {
 +            let filename = file.unwrap().file_name().into_string().unwrap();
 +            if let Some(name) = filename.strip_suffix(".txt") {
 +                if !lint_names.remove(name) {
 +                    extra.insert(name.to_string());
 +                }
 +            }
 +        }
 +
 +        let failed = print_lint_names("extra lint docs:", &extra) | print_lint_names("missing lint docs:", &lint_names);
 +
 +        if failed {
 +            exit_with_failure();
 +        }
 +    } else {
 +        if std::fs::remove_dir_all("src/docs").is_err() {
 +            eprintln!("could not remove src/docs directory");
 +        }
 +        if std::fs::create_dir("src/docs").is_err() {
 +            eprintln!("could not recreate src/docs directory");
 +        }
 +    }
 +    for lint in usable_lints {
 +        process_file(
 +            Path::new("src/docs").join(lint.name.clone() + ".txt"),
 +            update_mode,
 +            &lint.documentation,
 +        );
 +    }
 +}
 +
 +fn print_lint_names(header: &str, lints: &BTreeSet<String>) -> bool {
 +    if lints.is_empty() {
 +        return false;
 +    }
 +    println!("{header}");
 +    for lint in lints.iter().sorted() {
 +        println!("    {lint}");
 +    }
 +    println!();
 +    true
 +}
 +
 +pub fn print_lints() {
 +    let (lint_list, _, _) = gather_all();
 +    let usable_lints = Lint::usable_lints(&lint_list);
 +    let usable_lint_count = usable_lints.len();
 +    let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
 +
 +    for (lint_group, mut lints) in grouped_by_lint_group {
 +        println!("\n## {lint_group}");
 +
 +        lints.sort_by_key(|l| l.name.clone());
 +
 +        for lint in lints {
 +            println!("* [{}]({DOCS_LINK}#{}) ({})", lint.name, lint.name, lint.desc);
 +        }
 +    }
 +
 +    println!("there are {usable_lint_count} lints");
 +}
 +
 +/// 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!("`{old_name}` should not contain the `{prefix}` prefix");
 +    }
 +    if let Some((prefix, _)) = new_name.split_once("::") {
 +        panic!("`{new_name}` should not contain the `{prefix}` 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),
 +        "`{old_name}` has already been renamed"
 +    );
 +    assert!(
 +        !deprecated_lints.iter().any(|l| lint.old_name == l.name),
 +        "`{old_name}` has already been deprecated"
 +    );
 +
 +    // 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!(
 +            "`{old_name}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually."
 +        );
 +    } else if found_new_name {
 +        write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
 +        println!(
 +            "`{new_name}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually."
 +        );
 +    } 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/{old_name}.rs")),
 +            Path::new(&format!("tests/ui/{new_name}.rs")),
 +        ) {
 +            try_rename_file(
 +                Path::new(&format!("tests/ui/{old_name}.stderr")),
 +                Path::new(&format!("tests/ui/{new_name}.stderr")),
 +            );
 +            try_rename_file(
 +                Path::new(&format!("tests/ui/{old_name}.fixed")),
 +                Path::new(&format!("tests/ui/{new_name}.fixed")),
 +            );
 +        }
 +
 +        // 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/{old_name}.rs")),
 +                Path::new(&format!("clippy_lints/src/{new_name}.rs")),
 +            ) {
 +            // 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/{}/{old_name}.rs", lint.module)),
 +                Path::new(&format!("clippy_lints/src/{}/{new_name}.rs", lint.module)),
 +            )
 +        {
 +            // Edit the module name in the lint list. Note there could be multiple lints, or none.
 +            let renamed_mod = format!("{}::{old_name}", lint.module);
 +            for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) {
 +                lint.module = format!("{}::{new_name}", lint.module);
 +            }
 +            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!("{old_name} has been successfully renamed");
 +    }
 +
 +    println!("note: `cargo uitest` still needs to be run to update the test results");
 +}
 +
 +const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note";
 +/// Runs the `deprecate` command
 +///
 +/// This does the following:
 +/// * Adds an entry to `deprecated_lints.rs`.
 +/// * Removes the lint declaration (and the entire file if applicable)
 +///
 +/// # Panics
 +///
 +/// If a file path could not read from or written to
 +pub fn deprecate(name: &str, reason: Option<&String>) {
 +    fn finish(
 +        (lints, mut deprecated_lints, renamed_lints): (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>),
 +        name: &str,
 +        reason: &str,
 +    ) {
 +        deprecated_lints.push(DeprecatedLint {
 +            name: name.to_string(),
 +            reason: reason.to_string(),
 +            declaration_range: Range::default(),
 +        });
 +
 +        generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
 +        println!("info: `{name}` has successfully been deprecated");
 +
 +        if reason == DEFAULT_DEPRECATION_REASON {
 +            println!("note: the deprecation reason must be updated in `clippy_lints/src/deprecated_lints.rs`");
 +        }
 +        println!("note: you must run `cargo uitest` to update the test results");
 +    }
 +
 +    let reason = reason.map_or(DEFAULT_DEPRECATION_REASON, String::as_str);
 +    let name_lower = name.to_lowercase();
 +    let name_upper = name.to_uppercase();
 +
 +    let (mut lints, deprecated_lints, renamed_lints) = gather_all();
 +    let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { eprintln!("error: failed to find lint `{name}`"); return; };
 +
 +    let mod_path = {
 +        let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module));
 +        if mod_path.is_dir() {
 +            mod_path = mod_path.join("mod");
 +        }
 +
 +        mod_path.set_extension("rs");
 +        mod_path
 +    };
 +
 +    let deprecated_lints_path = &*clippy_project_root().join("clippy_lints/src/deprecated_lints.rs");
 +
 +    if remove_lint_declaration(&name_lower, &mod_path, &mut lints).unwrap_or(false) {
 +        declare_deprecated(&name_upper, deprecated_lints_path, reason).unwrap();
 +        finish((lints, deprecated_lints, renamed_lints), name, reason);
 +        return;
 +    }
 +
 +    eprintln!("error: lint not found");
 +}
 +
 +fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io::Result<bool> {
 +    fn remove_lint(name: &str, lints: &mut Vec<Lint>) {
 +        lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos));
 +    }
 +
 +    fn remove_test_assets(name: &str) {
 +        let test_file_stem = format!("tests/ui/{name}");
 +        let path = Path::new(&test_file_stem);
 +
 +        // Some lints have their own directories, delete them
 +        if path.is_dir() {
 +            fs::remove_dir_all(path).ok();
 +            return;
 +        }
 +
 +        // Remove all related test files
 +        fs::remove_file(path.with_extension("rs")).ok();
 +        fs::remove_file(path.with_extension("stderr")).ok();
 +        fs::remove_file(path.with_extension("fixed")).ok();
 +    }
 +
 +    fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) {
 +        let impl_lint_pass_start = content.find("impl_lint_pass!").unwrap_or_else(|| {
 +            content
 +                .find("declare_lint_pass!")
 +                .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`"))
 +        });
 +        let mut impl_lint_pass_end = content[impl_lint_pass_start..]
 +            .find(']')
 +            .expect("failed to find `impl_lint_pass` terminator");
 +
 +        impl_lint_pass_end += impl_lint_pass_start;
 +        if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) {
 +            let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len());
 +            for c in content[lint_name_end..impl_lint_pass_end].chars() {
 +                // Remove trailing whitespace
 +                if c == ',' || c.is_whitespace() {
 +                    lint_name_end += 1;
 +                } else {
 +                    break;
 +                }
 +            }
 +
 +            content.replace_range(impl_lint_pass_start + lint_name_pos..lint_name_end, "");
 +        }
 +    }
 +
 +    if path.exists() {
 +        if let Some(lint) = lints.iter().find(|l| l.name == name) {
 +            if lint.module == name {
 +                // The lint name is the same as the file, we can just delete the entire file
 +                fs::remove_file(path)?;
 +            } else {
 +                // We can't delete the entire file, just remove the declaration
 +
 +                if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) {
 +                    // Remove clippy_lints/src/some_mod/some_lint.rs
 +                    let mut lint_mod_path = path.to_path_buf();
 +                    lint_mod_path.set_file_name(name);
 +                    lint_mod_path.set_extension("rs");
 +
 +                    fs::remove_file(lint_mod_path).ok();
 +                }
 +
 +                let mut content =
 +                    fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
 +
 +                eprintln!(
 +                    "warn: you will have to manually remove any code related to `{name}` from `{}`",
 +                    path.display()
 +                );
 +
 +                assert!(
 +                    content[lint.declaration_range.clone()].contains(&name.to_uppercase()),
 +                    "error: `{}` does not contain lint `{}`'s declaration",
 +                    path.display(),
 +                    lint.name
 +                );
 +
 +                // Remove lint declaration (declare_clippy_lint!)
 +                content.replace_range(lint.declaration_range.clone(), "");
 +
 +                // Remove the module declaration (mod xyz;)
 +                let mod_decl = format!("\nmod {name};");
 +                content = content.replacen(&mod_decl, "", 1);
 +
 +                remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content);
 +                fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy()));
 +            }
 +
 +            remove_test_assets(name);
 +            remove_lint(name, lints);
 +            return Ok(true);
 +        }
 +    }
 +
 +    Ok(false)
 +}
 +
 +fn declare_deprecated(name: &str, path: &Path, reason: &str) -> io::Result<()> {
 +    let mut file = OpenOptions::new().write(true).open(path)?;
 +
 +    file.seek(SeekFrom::End(0))?;
 +
 +    let version = crate::new_lint::get_stabilization_version();
 +    let deprecation_reason = if reason == DEFAULT_DEPRECATION_REASON {
 +        "TODO"
 +    } else {
 +        reason
 +    };
 +
 +    writedoc!(
 +        file,
 +        "
 +
 +        declare_deprecated_lint! {{
 +            /// ### What it does
 +            /// Nothing. This lint has been deprecated.
 +            ///
 +            /// ### Deprecation reason
 +            /// {}
 +            #[clippy::version = \"{}\"]
 +            pub {},
 +            \"{}\"
 +        }}
 +
 +        ",
 +        deprecation_reason,
 +        version,
 +        name,
 +        reason,
 +    )
 +}
 +
 +/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there
 +/// were no replacements.
 +fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option<String> {
 +    fn is_ident_char(c: u8) -> bool {
 +        matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_')
 +    }
 +
 +    let searcher = AhoCorasickBuilder::new()
 +        .dfa(true)
 +        .match_kind(aho_corasick::MatchKind::LeftmostLongest)
 +        .build_with_size::<u16, _, _>(replacements.iter().map(|&(x, _)| x.as_bytes()))
 +        .unwrap();
 +
 +    let mut result = String::with_capacity(contents.len() + 1024);
 +    let mut pos = 0;
 +    let mut edited = false;
 +    for m in searcher.find_iter(contents) {
 +        let (old, new) = replacements[m.pattern()];
 +        result.push_str(&contents[pos..m.start()]);
 +        result.push_str(
 +            if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0))
 +                && !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0))
 +            {
 +                edited = true;
 +                new
 +            } else {
 +                old
 +            },
 +        );
 +        pos = m.end();
 +    }
 +    result.push_str(&contents[pos..]);
 +    edited.then_some(result)
 +}
 +
 +fn round_to_fifty(count: usize) -> usize {
 +    count / 50 * 50
 +}
 +
 +fn process_file(path: impl AsRef<Path>, update_mode: UpdateMode, content: &str) {
 +    if update_mode == UpdateMode::Check {
 +        let old_content =
 +            fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {e}", path.as_ref().display()));
 +        if content != old_content {
 +            exit_with_failure();
 +        }
 +    } else {
 +        fs::write(&path, content.as_bytes())
 +            .unwrap_or_else(|e| panic!("Cannot write to {}: {e}", path.as_ref().display()));
 +    }
 +}
 +
 +fn exit_with_failure() {
 +    println!(
 +        "Not all lints defined properly. \
 +                 Please run `cargo dev update_lints` to make sure all lints are defined properly."
 +    );
 +    std::process::exit(1);
 +}
 +
 +/// Lint data parsed from the Clippy source code.
 +#[derive(Clone, PartialEq, Eq, Debug)]
 +struct Lint {
 +    name: String,
 +    group: String,
 +    desc: String,
 +    module: String,
 +    declaration_range: Range<usize>,
 +    documentation: String,
 +}
 +
 +impl Lint {
 +    #[must_use]
 +    fn new(
 +        name: &str,
 +        group: &str,
 +        desc: &str,
 +        module: &str,
 +        declaration_range: Range<usize>,
 +        documentation: String,
 +    ) -> Self {
 +        Self {
 +            name: name.to_lowercase(),
 +            group: group.into(),
 +            desc: remove_line_splices(desc),
 +            module: module.into(),
 +            declaration_range,
 +            documentation,
 +        }
 +    }
 +
 +    /// Returns all non-deprecated lints and non-internal lints
 +    #[must_use]
 +    fn usable_lints(lints: &[Self]) -> Vec<Self> {
 +        lints
 +            .iter()
 +            .filter(|l| !l.group.starts_with("internal"))
 +            .cloned()
 +            .collect()
 +    }
 +
 +    /// Returns all internal lints (not `internal_warn` lints)
 +    #[must_use]
 +    fn internal_lints(lints: &[Self]) -> Vec<Self> {
 +        lints.iter().filter(|l| l.group == "internal").cloned().collect()
 +    }
 +
 +    /// Returns the lints in a `HashMap`, grouped by the different lint groups
 +    #[must_use]
 +    fn by_lint_group(lints: impl Iterator<Item = Self>) -> HashMap<String, Vec<Self>> {
 +        lints.map(|lint| (lint.group.to_string(), lint)).into_group_map()
 +    }
 +}
 +
 +#[derive(Clone, PartialEq, Eq, Debug)]
 +struct DeprecatedLint {
 +    name: String,
 +    reason: String,
 +    declaration_range: Range<usize>,
 +}
 +impl DeprecatedLint {
 +    fn new(name: &str, reason: &str, declaration_range: Range<usize>) -> Self {
 +        Self {
 +            name: name.to_lowercase(),
 +            reason: remove_line_splices(reason),
 +            declaration_range,
 +        }
 +    }
 +}
 +
 +struct RenamedLint {
 +    old_name: String,
 +    new_name: String,
 +}
 +impl RenamedLint {
 +    fn new(old_name: &str, new_name: &str) -> Self {
 +        Self {
 +            old_name: remove_line_splices(old_name),
 +            new_name: remove_line_splices(new_name),
 +        }
 +    }
 +}
 +
 +/// Generates the code for registering a group
 +fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lint>) -> String {
 +    let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect();
 +    details.sort_unstable();
 +
 +    let mut output = GENERATED_FILE_COMMENT.to_string();
 +
 +    let _ = writeln!(
 +        output,
 +        "store.register_group(true, \"clippy::{group_name}\", Some(\"clippy_{group_name}\"), vec![",
 +    );
 +    for (module, name) in details {
 +        let _ = writeln!(output, "    LintId::of({module}::{name}),");
 +    }
 +    output.push_str("])\n");
 +
 +    output
 +}
 +
 +/// Generates the `register_removed` code
 +#[must_use]
 +fn gen_deprecated(lints: &[DeprecatedLint]) -> String {
 +    let mut output = GENERATED_FILE_COMMENT.to_string();
 +    output.push_str("{\n");
 +    for lint in lints {
 +        let _ = write!(
 +            output,
 +            concat!(
 +                "    store.register_removed(\n",
 +                "        \"clippy::{}\",\n",
 +                "        \"{}\",\n",
 +                "    );\n"
 +            ),
 +            lint.name, lint.reason,
 +        );
 +    }
 +    output.push_str("}\n");
 +
 +    output
 +}
 +
 +/// Generates the code for registering lints
 +#[must_use]
 +fn gen_register_lint_list<'a>(
 +    internal_lints: impl Iterator<Item = &'a Lint>,
 +    usable_lints: impl Iterator<Item = &'a Lint>,
 +) -> String {
 +    let mut details: Vec<_> = internal_lints
 +        .map(|l| (false, &l.module, l.name.to_uppercase()))
 +        .chain(usable_lints.map(|l| (true, &l.module, l.name.to_uppercase())))
 +        .collect();
 +    details.sort_unstable();
 +
 +    let mut output = GENERATED_FILE_COMMENT.to_string();
 +    output.push_str("store.register_lints(&[\n");
 +
 +    for (is_public, module_name, lint_name) in details {
 +        if !is_public {
 +            output.push_str("    #[cfg(feature = \"internal\")]\n");
 +        }
 +        let _ = writeln!(output, "    {module_name}::{lint_name},");
 +    }
 +    output.push_str("])\n");
 +
 +    output
 +}
 +
 +fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String {
 +    let mut res: String = GENERATED_FILE_COMMENT.into();
 +    for lint in lints {
 +        writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap();
 +    }
 +    res.push_str("\nfn main() {}\n");
 +    res
 +}
 +
 +fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String {
 +    let mut seen_lints = HashSet::new();
 +    let mut res: String = GENERATED_FILE_COMMENT.into();
 +    res.push_str("// run-rustfix\n\n");
 +    for lint in lints {
 +        if seen_lints.insert(&lint.new_name) {
 +            writeln!(res, "#![allow({})]", lint.new_name).unwrap();
 +        }
 +    }
 +    seen_lints.clear();
 +    for lint in lints {
 +        if seen_lints.insert(&lint.old_name) {
 +            writeln!(res, "#![warn({})]", lint.old_name).unwrap();
 +        }
 +    }
 +    res.push_str("\nfn main() {}\n");
 +    res
 +}
 +
 +fn gen_renamed_lints_list(lints: &[RenamedLint]) -> String {
 +    const HEADER: &str = "\
 +        // This file is managed by `cargo dev rename_lint`. Prefer using that when possible.\n\n\
 +        #[rustfmt::skip]\n\
 +        pub static RENAMED_LINTS: &[(&str, &str)] = &[\n";
 +
 +    let mut res = String::from(HEADER);
 +    for lint in lints {
 +        writeln!(res, "    (\"{}\", \"{}\"),", lint.old_name, lint.new_name).unwrap();
 +    }
 +    res.push_str("];\n");
 +    res
 +}
 +
 +/// Gathers all lints defined in `clippy_lints/src`
 +fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) {
 +    let mut lints = Vec::with_capacity(1000);
 +    let mut deprecated_lints = Vec::with_capacity(50);
 +    let mut renamed_lints = Vec::with_capacity(50);
 +
 +    for (rel_path, file) in clippy_lints_src_files() {
 +        let path = file.path();
 +        let contents =
 +            fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display()));
 +        let module = rel_path
 +            .components()
 +            .map(|c| c.as_os_str().to_str().unwrap())
 +            .collect::<Vec<_>>()
 +            .join("::");
 +
 +        // If the lints are stored in mod.rs, we get the module name from
 +        // the containing directory:
 +        let module = if let Some(module) = module.strip_suffix("::mod.rs") {
 +            module
 +        } else {
 +            module.strip_suffix(".rs").unwrap_or(&module)
 +        };
 +
 +        match module {
 +            "deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints),
 +            "renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints),
 +            _ => parse_contents(&contents, module, &mut lints),
 +        }
 +    }
 +    (lints, deprecated_lints, renamed_lints)
 +}
 +
 +fn clippy_lints_src_files() -> impl Iterator<Item = (PathBuf, DirEntry)> {
 +    let root_path = clippy_project_root().join("clippy_lints/src");
 +    let iter = WalkDir::new(&root_path).into_iter();
 +    iter.map(Result::unwrap)
 +        .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
 +        .map(move |f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f))
 +}
 +
 +macro_rules! match_tokens {
 +    ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => {
 +         {
-                     content: _x,
++            $(#[allow(clippy::redundant_pattern)] let Some(LintDeclSearchResult {
 +                    token_kind: TokenKind::$token $({$($fields)*})?,
-             }) = $iter.next() {
-                 _x
-             } else {
++                    content: $($capture @)? _,
 +                    ..
++            }) = $iter.next() else {
 +                continue;
 +            };)*
 +            #[allow(clippy::unused_unit)]
 +            { ($($($capture,)?)*) }
 +        }
 +    }
 +}
 +
 +pub(crate) use match_tokens;
 +
 +pub(crate) struct LintDeclSearchResult<'a> {
 +    pub token_kind: TokenKind,
 +    pub content: &'a str,
 +    pub range: Range<usize>,
 +}
 +
 +/// Parse a source file looking for `declare_clippy_lint` macro invocations.
 +fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
 +    let mut offset = 0usize;
 +    let mut iter = tokenize(contents).map(|t| {
 +        let range = offset..offset + t.len as usize;
 +        offset = range.end;
 +
 +        LintDeclSearchResult {
 +            token_kind: t.kind,
 +            content: &contents[range.clone()],
 +            range,
 +        }
 +    });
 +
 +    while let Some(LintDeclSearchResult { range, .. }) = iter.find(
 +        |LintDeclSearchResult {
 +             token_kind, content, ..
 +         }| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint",
 +    ) {
 +        let start = range.start;
 +        let mut docs = String::with_capacity(128);
 +        let mut iter = iter.by_ref().filter(|t| !matches!(t.token_kind, TokenKind::Whitespace));
 +        // matches `!{`
 +        match_tokens!(iter, Bang OpenBrace);
 +        let mut in_code = false;
 +        while let Some(t) = iter.next() {
 +            match t.token_kind {
 +                TokenKind::LineComment { .. } => {
 +                    if let Some(line) = t.content.strip_prefix("/// ").or_else(|| t.content.strip_prefix("///")) {
 +                        if line.starts_with("```") {
 +                            docs += "```\n";
 +                            in_code = !in_code;
 +                        } else if !(in_code && line.starts_with("# ")) {
 +                            docs += line;
 +                            docs.push('\n');
 +                        }
 +                    }
 +                },
 +                TokenKind::Pound => {
 +                    match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
 +                    break;
 +                },
 +                TokenKind::Ident => {
 +                    break;
 +                },
 +                _ => {},
 +            }
 +        }
 +        docs.pop(); // remove final newline
 +
 +        let (name, group, desc) = match_tokens!(
 +            iter,
 +            // LINT_NAME
 +            Ident(name) Comma
 +            // group,
 +            Ident(group) Comma
 +            // "description"
 +            Literal{..}(desc)
 +        );
 +
 +        if let Some(LintDeclSearchResult {
 +            token_kind: TokenKind::CloseBrace,
 +            range,
 +            ..
 +        }) = iter.next()
 +        {
 +            lints.push(Lint::new(name, group, desc, module, start..range.end, docs));
 +        }
 +    }
 +}
 +
 +/// Parse a source file looking for `declare_deprecated_lint` macro invocations.
 +fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) {
 +    let mut offset = 0usize;
 +    let mut iter = tokenize(contents).map(|t| {
 +        let range = offset..offset + t.len as usize;
 +        offset = range.end;
 +
 +        LintDeclSearchResult {
 +            token_kind: t.kind,
 +            content: &contents[range.clone()],
 +            range,
 +        }
 +    });
 +
 +    while let Some(LintDeclSearchResult { range, .. }) = iter.find(
 +        |LintDeclSearchResult {
 +             token_kind, content, ..
 +         }| token_kind == &TokenKind::Ident && *content == "declare_deprecated_lint",
 +    ) {
 +        let start = range.start;
 +
 +        let mut iter = iter.by_ref().filter(|LintDeclSearchResult { ref token_kind, .. }| {
 +            !matches!(token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })
 +        });
 +        let (name, reason) = match_tokens!(
 +            iter,
 +            // !{
 +            Bang OpenBrace
 +            // #[clippy::version = "version"]
 +            Pound OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket
 +            // pub LINT_NAME,
 +            Ident Ident(name) Comma
 +            // "description"
 +            Literal{kind: LiteralKind::Str{..},..}(reason)
 +        );
 +
 +        if let Some(LintDeclSearchResult {
 +            token_kind: TokenKind::CloseBrace,
 +            range,
 +            ..
 +        }) = iter.next()
 +        {
 +            lints.push(DeprecatedLint::new(name, reason, start..range.end));
 +        }
 +    }
 +}
 +
 +fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) {
 +    for line in contents.lines() {
 +        let mut offset = 0usize;
 +        let mut iter = tokenize(line).map(|t| {
 +            let range = offset..offset + t.len as usize;
 +            offset = range.end;
 +
 +            LintDeclSearchResult {
 +                token_kind: t.kind,
 +                content: &line[range.clone()],
 +                range,
 +            }
 +        });
 +
 +        let (old_name, new_name) = match_tokens!(
 +            iter,
 +            // ("old_name",
 +            Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(old_name) Comma
 +            // "new_name"),
 +            Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma
 +        );
 +        lints.push(RenamedLint::new(old_name, new_name));
 +    }
 +}
 +
 +/// Removes the line splices and surrounding quotes from a string literal
 +fn remove_line_splices(s: &str) -> String {
 +    let s = s
 +        .strip_prefix('r')
 +        .unwrap_or(s)
 +        .trim_matches('#')
 +        .strip_prefix('"')
 +        .and_then(|s| s.strip_suffix('"'))
 +        .unwrap_or_else(|| panic!("expected quoted string, found `{s}`"));
 +    let mut res = String::with_capacity(s.len());
 +    unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, ch| {
 +        if ch.is_ok() {
 +            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 `{}`: {e}", path.display()));
 +    let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) {
 +        Ok(x) => x,
 +        Err(delim) => panic!("Couldn't find `{delim}` in file `{}`", 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 `{}`: {e}", path.display());
 +            }
 +        },
 +    }
 +}
 +
 +/// 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 {action} file `{}`: {error}", name.display())
 +}
 +
 +fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option<String>) {
 +    let mut file = fs::OpenOptions::new()
 +        .write(true)
 +        .read(true)
 +        .open(path)
 +        .unwrap_or_else(|e| panic_file(e, path, "open"));
 +    let mut buf = String::new();
 +    file.read_to_string(&mut buf)
 +        .unwrap_or_else(|e| panic_file(e, path, "read"));
 +    if let Some(new_contents) = f(&buf) {
 +        file.rewind().unwrap_or_else(|e| panic_file(e, path, "write"));
 +        file.write_all(new_contents.as_bytes())
 +            .unwrap_or_else(|e| panic_file(e, path, "write"));
 +        file.set_len(new_contents.len() as u64)
 +            .unwrap_or_else(|e| panic_file(e, path, "write"));
 +    }
 +}
 +
 +fn write_file(path: &Path, contents: &str) {
 +    fs::write(path, contents).unwrap_or_else(|e| panic_file(e, path, "write"));
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +
 +    #[test]
 +    fn test_parse_contents() {
 +        static CONTENTS: &str = r#"
 +            declare_clippy_lint! {
 +                #[clippy::version = "Hello Clippy!"]
 +                pub PTR_ARG,
 +                style,
 +                "really long \
 +                text"
 +            }
 +
 +            declare_clippy_lint!{
 +                #[clippy::version = "Test version"]
 +                pub DOC_MARKDOWN,
 +                pedantic,
 +                "single line"
 +            }
 +        "#;
 +        let mut result = Vec::new();
 +        parse_contents(CONTENTS, "module_name", &mut result);
 +        for r in &mut result {
 +            r.declaration_range = Range::default();
 +        }
 +
 +        let expected = vec![
 +            Lint::new(
 +                "ptr_arg",
 +                "style",
 +                "\"really long text\"",
 +                "module_name",
 +                Range::default(),
 +                String::new(),
 +            ),
 +            Lint::new(
 +                "doc_markdown",
 +                "pedantic",
 +                "\"single line\"",
 +                "module_name",
 +                Range::default(),
 +                String::new(),
 +            ),
 +        ];
 +        assert_eq!(expected, result);
 +    }
 +
 +    #[test]
 +    fn test_parse_deprecated_contents() {
 +        static DEPRECATED_CONTENTS: &str = r#"
 +            /// some doc comment
 +            declare_deprecated_lint! {
 +                #[clippy::version = "I'm a version"]
 +                pub SHOULD_ASSERT_EQ,
 +                "`assert!()` will be more flexible with RFC 2011"
 +            }
 +        "#;
 +
 +        let mut result = Vec::new();
 +        parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result);
 +        for r in &mut result {
 +            r.declaration_range = Range::default();
 +        }
 +
 +        let expected = vec![DeprecatedLint::new(
 +            "should_assert_eq",
 +            "\"`assert!()` will be more flexible with RFC 2011\"",
 +            Range::default(),
 +        )];
 +        assert_eq!(expected, result);
 +    }
 +
 +    #[test]
 +    fn test_usable_lints() {
 +        let lints = vec![
 +            Lint::new(
 +                "should_assert_eq2",
 +                "Not Deprecated",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +                String::new(),
 +            ),
 +            Lint::new(
 +                "should_assert_eq2",
 +                "internal",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +                String::new(),
 +            ),
 +            Lint::new(
 +                "should_assert_eq2",
 +                "internal_style",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +                String::new(),
 +            ),
 +        ];
 +        let expected = vec![Lint::new(
 +            "should_assert_eq2",
 +            "Not Deprecated",
 +            "\"abc\"",
 +            "module_name",
 +            Range::default(),
 +            String::new(),
 +        )];
 +        assert_eq!(expected, Lint::usable_lints(&lints));
 +    }
 +
 +    #[test]
 +    fn test_by_lint_group() {
 +        let lints = vec![
 +            Lint::new(
 +                "should_assert_eq",
 +                "group1",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +                String::new(),
 +            ),
 +            Lint::new(
 +                "should_assert_eq2",
 +                "group2",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +                String::new(),
 +            ),
 +            Lint::new(
 +                "incorrect_match",
 +                "group1",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +                String::new(),
 +            ),
 +        ];
 +        let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
 +        expected.insert(
 +            "group1".to_string(),
 +            vec![
 +                Lint::new(
 +                    "should_assert_eq",
 +                    "group1",
 +                    "\"abc\"",
 +                    "module_name",
 +                    Range::default(),
 +                    String::new(),
 +                ),
 +                Lint::new(
 +                    "incorrect_match",
 +                    "group1",
 +                    "\"abc\"",
 +                    "module_name",
 +                    Range::default(),
 +                    String::new(),
 +                ),
 +            ],
 +        );
 +        expected.insert(
 +            "group2".to_string(),
 +            vec![Lint::new(
 +                "should_assert_eq2",
 +                "group2",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +                String::new(),
 +            )],
 +        );
 +        assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
 +    }
 +
 +    #[test]
 +    fn test_gen_deprecated() {
 +        let lints = vec![
 +            DeprecatedLint::new(
 +                "should_assert_eq",
 +                "\"has been superseded by should_assert_eq2\"",
 +                Range::default(),
 +            ),
 +            DeprecatedLint::new("another_deprecated", "\"will be removed\"", Range::default()),
 +        ];
 +
 +        let expected = GENERATED_FILE_COMMENT.to_string()
 +            + &[
 +                "{",
 +                "    store.register_removed(",
 +                "        \"clippy::should_assert_eq\",",
 +                "        \"has been superseded by should_assert_eq2\",",
 +                "    );",
 +                "    store.register_removed(",
 +                "        \"clippy::another_deprecated\",",
 +                "        \"will be removed\",",
 +                "    );",
 +                "}",
 +            ]
 +            .join("\n")
 +            + "\n";
 +
 +        assert_eq!(expected, gen_deprecated(&lints));
 +    }
 +
 +    #[test]
 +    fn test_gen_lint_group_list() {
 +        let lints = vec![
 +            Lint::new(
 +                "abc",
 +                "group1",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +                String::new(),
 +            ),
 +            Lint::new(
 +                "should_assert_eq",
 +                "group1",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +                String::new(),
 +            ),
 +            Lint::new(
 +                "internal",
 +                "internal_style",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +                String::new(),
 +            ),
 +        ];
 +        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 2a15cbc7a3c3b0a981eb29511b505bd36f063000,0000000000000000000000000000000000000000..08164c0b654e2a0daa18737dfc44e0134603dca0
mode 100644,000000..100644
--- /dev/null
@@@ -1,511 -1,0 +1,513 @@@
- use clippy_utils::{eq_expr_value, get_trait_def_id, paths};
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
++use clippy_utils::eq_expr_value;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
-     get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[]))
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 +use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for boolean expressions that can be written more
 +    /// concisely.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability of boolean expressions suffers from
 +    /// unnecessary duplication.
 +    ///
 +    /// ### Known problems
 +    /// Ignores short circuiting behavior of `||` and
 +    /// `&&`. Ignores `|`, `&` and `^`.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// if a && true {}
 +    /// if !(a == b) {}
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// if a {}
 +    /// if a != b {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NONMINIMAL_BOOL,
 +    complexity,
 +    "boolean expressions that can be written more concisely"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for boolean expressions that contain terminals that
 +    /// can be eliminated.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is most likely a logic bug.
 +    ///
 +    /// ### Known problems
 +    /// Ignores short circuiting behavior.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // The `b` is unnecessary, the expression is equivalent to `if a`.
 +    /// if a && b || a { ... }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// if a {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OVERLY_COMPLEX_BOOL_EXPR,
 +    correctness,
 +    "boolean expressions that contain terminals which can be eliminated"
 +}
 +
 +// For each pairs, both orders are considered.
 +const METHODS_WITH_NEGATION: [(&str, &str); 2] = [("is_some", "is_none"), ("is_err", "is_ok")];
 +
 +declare_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]);
 +
 +impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        _: FnKind<'tcx>,
 +        _: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        _: Span,
 +        _: HirId,
 +    ) {
 +        NonminimalBoolVisitor { cx }.visit_body(body);
 +    }
 +}
 +
 +struct NonminimalBoolVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +use quine_mc_cluskey::Bool;
 +struct Hir2Qmm<'a, 'tcx, 'v> {
 +    terminals: Vec<&'v Expr<'v>>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
 +    fn extract(&mut self, op: BinOpKind, a: &[&'v Expr<'_>], mut v: Vec<Bool>) -> Result<Vec<Bool>, String> {
 +        for a in a {
 +            if let ExprKind::Binary(binop, lhs, rhs) = &a.kind {
 +                if binop.node == op {
 +                    v = self.extract(op, &[lhs, rhs], v)?;
 +                    continue;
 +                }
 +            }
 +            v.push(self.run(a)?);
 +        }
 +        Ok(v)
 +    }
 +
 +    fn run(&mut self, e: &'v Expr<'_>) -> Result<Bool, String> {
 +        fn negate(bin_op_kind: BinOpKind) -> Option<BinOpKind> {
 +            match bin_op_kind {
 +                BinOpKind::Eq => Some(BinOpKind::Ne),
 +                BinOpKind::Ne => Some(BinOpKind::Eq),
 +                BinOpKind::Gt => Some(BinOpKind::Le),
 +                BinOpKind::Ge => Some(BinOpKind::Lt),
 +                BinOpKind::Lt => Some(BinOpKind::Ge),
 +                BinOpKind::Le => Some(BinOpKind::Gt),
 +                _ => None,
 +            }
 +        }
 +
 +        // prevent folding of `cfg!` macros and the like
 +        if !e.span.from_expansion() {
 +            match &e.kind {
 +                ExprKind::Unary(UnOp::Not, inner) => return Ok(Bool::Not(Box::new(self.run(inner)?))),
 +                ExprKind::Binary(binop, lhs, rhs) => match &binop.node {
 +                    BinOpKind::Or => {
 +                        return Ok(Bool::Or(self.extract(BinOpKind::Or, &[lhs, rhs], Vec::new())?));
 +                    },
 +                    BinOpKind::And => {
 +                        return Ok(Bool::And(self.extract(BinOpKind::And, &[lhs, rhs], Vec::new())?));
 +                    },
 +                    _ => (),
 +                },
 +                ExprKind::Lit(lit) => match lit.node {
 +                    LitKind::Bool(true) => return Ok(Bool::True),
 +                    LitKind::Bool(false) => return Ok(Bool::False),
 +                    _ => (),
 +                },
 +                _ => (),
 +            }
 +        }
 +        for (n, expr) in self.terminals.iter().enumerate() {
 +            if eq_expr_value(self.cx, e, expr) {
 +                #[expect(clippy::cast_possible_truncation)]
 +                return Ok(Bool::Term(n as u8));
 +            }
 +
 +            if_chain! {
 +                if let ExprKind::Binary(e_binop, e_lhs, e_rhs) = &e.kind;
 +                if implements_ord(self.cx, e_lhs);
 +                if let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind;
 +                if negate(e_binop.node) == Some(expr_binop.node);
 +                if eq_expr_value(self.cx, e_lhs, expr_lhs);
 +                if eq_expr_value(self.cx, e_rhs, expr_rhs);
 +                then {
 +                    #[expect(clippy::cast_possible_truncation)]
 +                    return Ok(Bool::Not(Box::new(Bool::Term(n as u8))));
 +                }
 +            }
 +        }
 +        let n = self.terminals.len();
 +        self.terminals.push(e);
 +        if n < 32 {
 +            #[expect(clippy::cast_possible_truncation)]
 +            Ok(Bool::Term(n as u8))
 +        } else {
 +            Err("too many literals".to_owned())
 +        }
 +    }
 +}
 +
 +struct SuggestContext<'a, 'tcx, 'v> {
 +    terminals: &'v [&'v Expr<'v>],
 +    cx: &'a LateContext<'tcx>,
 +    output: String,
 +}
 +
 +impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
 +    fn recurse(&mut self, suggestion: &Bool) -> Option<()> {
 +        use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True};
 +        match suggestion {
 +            True => {
 +                self.output.push_str("true");
 +            },
 +            False => {
 +                self.output.push_str("false");
 +            },
 +            Not(inner) => match **inner {
 +                And(_) | Or(_) => {
 +                    self.output.push('!');
 +                    self.output.push('(');
 +                    self.recurse(inner);
 +                    self.output.push(')');
 +                },
 +                Term(n) => {
 +                    let terminal = self.terminals[n as usize];
 +                    if let Some(str) = simplify_not(self.cx, terminal) {
 +                        self.output.push_str(&str);
 +                    } else {
 +                        self.output.push('!');
 +                        let snip = snippet_opt(self.cx, terminal.span)?;
 +                        self.output.push_str(&snip);
 +                    }
 +                },
 +                True | False | Not(_) => {
 +                    self.output.push('!');
 +                    self.recurse(inner)?;
 +                },
 +            },
 +            And(v) => {
 +                for (index, inner) in v.iter().enumerate() {
 +                    if index > 0 {
 +                        self.output.push_str(" && ");
 +                    }
 +                    if let Or(_) = *inner {
 +                        self.output.push('(');
 +                        self.recurse(inner);
 +                        self.output.push(')');
 +                    } else {
 +                        self.recurse(inner);
 +                    }
 +                }
 +            },
 +            Or(v) => {
 +                for (index, inner) in v.iter().rev().enumerate() {
 +                    if index > 0 {
 +                        self.output.push_str(" || ");
 +                    }
 +                    self.recurse(inner);
 +                }
 +            },
 +            &Term(n) => {
 +                let snip = snippet_opt(self.cx, self.terminals[n as usize].span.source_callsite())?;
 +                self.output.push_str(&snip);
 +            },
 +        }
 +        Some(())
 +    }
 +}
 +
 +fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
 +    match &expr.kind {
 +        ExprKind::Binary(binop, lhs, rhs) => {
 +            if !implements_ord(cx, lhs) {
 +                return None;
 +            }
 +
 +            match binop.node {
 +                BinOpKind::Eq => Some(" != "),
 +                BinOpKind::Ne => Some(" == "),
 +                BinOpKind::Lt => Some(" >= "),
 +                BinOpKind::Gt => Some(" <= "),
 +                BinOpKind::Le => Some(" > "),
 +                BinOpKind::Ge => Some(" < "),
 +                _ => None,
 +            }
 +            .and_then(|op| {
 +                Some(format!(
 +                    "{}{op}{}",
 +                    snippet_opt(cx, lhs.span)?,
 +                    snippet_opt(cx, rhs.span)?
 +                ))
 +            })
 +        },
 +        ExprKind::MethodCall(path, receiver, [], _) => {
 +            let type_of_receiver = cx.typeck_results().expr_ty(receiver);
 +            if !is_type_diagnostic_item(cx, type_of_receiver, sym::Option)
 +                && !is_type_diagnostic_item(cx, type_of_receiver, sym::Result)
 +            {
 +                return None;
 +            }
 +            METHODS_WITH_NEGATION
 +                .iter()
 +                .copied()
 +                .flat_map(|(a, b)| vec![(a, b), (b, a)])
 +                .find(|&(a, _)| {
 +                    let path: &str = path.ident.name.as_str();
 +                    a == path
 +                })
 +                .and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", snippet_opt(cx, receiver.span)?)))
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn suggest(cx: &LateContext<'_>, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String {
 +    let mut suggest_context = SuggestContext {
 +        terminals,
 +        cx,
 +        output: String::new(),
 +    };
 +    suggest_context.recurse(suggestion);
 +    suggest_context.output
 +}
 +
 +fn simple_negate(b: Bool) -> Bool {
 +    use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True};
 +    match b {
 +        True => False,
 +        False => True,
 +        t @ Term(_) => Not(Box::new(t)),
 +        And(mut v) => {
 +            for el in &mut v {
 +                *el = simple_negate(::std::mem::replace(el, True));
 +            }
 +            Or(v)
 +        },
 +        Or(mut v) => {
 +            for el in &mut v {
 +                *el = simple_negate(::std::mem::replace(el, True));
 +            }
 +            And(v)
 +        },
 +        Not(inner) => *inner,
 +    }
 +}
 +
 +#[derive(Default)]
 +struct Stats {
 +    terminals: [usize; 32],
 +    negations: usize,
 +    ops: usize,
 +}
 +
 +fn terminal_stats(b: &Bool) -> Stats {
 +    fn recurse(b: &Bool, stats: &mut Stats) {
 +        match b {
 +            True | False => stats.ops += 1,
 +            Not(inner) => {
 +                match **inner {
 +                    And(_) | Or(_) => stats.ops += 1, // brackets are also operations
 +                    _ => stats.negations += 1,
 +                }
 +                recurse(inner, stats);
 +            },
 +            And(v) | Or(v) => {
 +                stats.ops += v.len() - 1;
 +                for inner in v {
 +                    recurse(inner, stats);
 +                }
 +            },
 +            &Term(n) => stats.terminals[n as usize] += 1,
 +        }
 +    }
 +    use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True};
 +    let mut stats = Stats::default();
 +    recurse(b, &mut stats);
 +    stats
 +}
 +
 +impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
 +    fn bool_expr(&self, e: &'tcx Expr<'_>) {
 +        let mut h2q = Hir2Qmm {
 +            terminals: Vec::new(),
 +            cx: self.cx,
 +        };
 +        if let Ok(expr) = h2q.run(e) {
 +            if h2q.terminals.len() > 8 {
 +                // QMC has exponentially slow behavior as the number of terminals increases
 +                // 8 is reasonable, it takes approximately 0.2 seconds.
 +                // See #825
 +                return;
 +            }
 +
 +            let stats = terminal_stats(&expr);
 +            let mut simplified = expr.simplify();
 +            for simple in Bool::Not(Box::new(expr)).simplify() {
 +                match simple {
 +                    Bool::Not(_) | Bool::True | Bool::False => {},
 +                    _ => simplified.push(Bool::Not(Box::new(simple.clone()))),
 +                }
 +                let simple_negated = simple_negate(simple);
 +                if simplified.iter().any(|s| *s == simple_negated) {
 +                    continue;
 +                }
 +                simplified.push(simple_negated);
 +            }
 +            let mut improvements = Vec::with_capacity(simplified.len());
 +            'simplified: for suggestion in &simplified {
 +                let simplified_stats = terminal_stats(suggestion);
 +                let mut improvement = false;
 +                for i in 0..32 {
 +                    // ignore any "simplifications" that end up requiring a terminal more often
 +                    // than in the original expression
 +                    if stats.terminals[i] < simplified_stats.terminals[i] {
 +                        continue 'simplified;
 +                    }
 +                    if stats.terminals[i] != 0 && simplified_stats.terminals[i] == 0 {
 +                        span_lint_hir_and_then(
 +                            self.cx,
 +                            OVERLY_COMPLEX_BOOL_EXPR,
 +                            e.hir_id,
 +                            e.span,
 +                            "this boolean expression contains a logic bug",
 +                            |diag| {
 +                                diag.span_help(
 +                                    h2q.terminals[i].span,
 +                                    "this expression can be optimized out by applying boolean operations to the \
 +                                     outer expression",
 +                                );
 +                                diag.span_suggestion(
 +                                    e.span,
 +                                    "it would look like the following",
 +                                    suggest(self.cx, suggestion, &h2q.terminals),
 +                                    // nonminimal_bool can produce minimal but
 +                                    // not human readable expressions (#3141)
 +                                    Applicability::Unspecified,
 +                                );
 +                            },
 +                        );
 +                        // don't also lint `NONMINIMAL_BOOL`
 +                        return;
 +                    }
 +                    // if the number of occurrences of a terminal decreases or any of the stats
 +                    // decreases while none increases
 +                    improvement |= (stats.terminals[i] > simplified_stats.terminals[i])
 +                        || (stats.negations > simplified_stats.negations && stats.ops == simplified_stats.ops)
 +                        || (stats.ops > simplified_stats.ops && stats.negations == simplified_stats.negations);
 +                }
 +                if improvement {
 +                    improvements.push(suggestion);
 +                }
 +            }
 +            let nonminimal_bool_lint = |suggestions: Vec<_>| {
 +                span_lint_hir_and_then(
 +                    self.cx,
 +                    NONMINIMAL_BOOL,
 +                    e.hir_id,
 +                    e.span,
 +                    "this boolean expression can be simplified",
 +                    |diag| {
 +                        diag.span_suggestions(
 +                            e.span,
 +                            "try",
 +                            suggestions.into_iter(),
 +                            // nonminimal_bool can produce minimal but
 +                            // not human readable expressions (#3141)
 +                            Applicability::Unspecified,
 +                        );
 +                    },
 +                );
 +            };
 +            if improvements.is_empty() {
 +                let mut visitor = NotSimplificationVisitor { cx: self.cx };
 +                visitor.visit_expr(e);
 +            } else {
 +                nonminimal_bool_lint(
 +                    improvements
 +                        .into_iter()
 +                        .map(|suggestion| suggest(self.cx, suggestion, &h2q.terminals))
 +                        .collect(),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +        if !e.span.from_expansion() {
 +            match &e.kind {
 +                ExprKind::Binary(binop, _, _) if binop.node == BinOpKind::Or || binop.node == BinOpKind::And => {
 +                    self.bool_expr(e);
 +                },
 +                ExprKind::Unary(UnOp::Not, inner) => {
 +                    if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() {
 +                        self.bool_expr(e);
 +                    }
 +                },
 +                _ => {},
 +            }
 +        }
 +        walk_expr(self, e);
 +    }
 +}
 +
 +fn implements_ord<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool {
 +    let ty = cx.typeck_results().expr_ty(expr);
++    cx.tcx
++        .get_diagnostic_item(sym::Ord)
++        .map_or(false, |id| implements_trait(cx, ty, id, &[]))
 +}
 +
 +struct NotSimplificationVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind {
 +            if let Some(suggestion) = simplify_not(self.cx, inner) {
 +                span_lint_and_sugg(
 +                    self.cx,
 +                    NONMINIMAL_BOOL,
 +                    expr.span,
 +                    "this boolean expression can be simplified",
 +                    "try",
 +                    suggestion,
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +}
index 792183ac40814ef90355cd13e94a3bd72ce3cfbf,0000000000000000000000000000000000000000..36daceabe0bea61a034aeac4dd894cbcff834eb0
mode 100644,000000..100644
--- /dev/null
@@@ -1,61 -1,0 +1,129 @@@
- use clippy_utils::{diagnostics::span_lint_and_help, is_default_equivalent, path_def_id};
- use rustc_hir::{Expr, ExprKind, QPath};
++use clippy_utils::{
++    diagnostics::span_lint_and_sugg, get_parent_node, is_default_equivalent, macros::macro_backtrace, match_path,
++    path_def_id, paths, ty::expr_sig,
++};
++use rustc_errors::Applicability;
++use rustc_hir::{
++    intravisit::{walk_ty, Visitor},
++    Block, Expr, ExprKind, Local, Node, QPath, TyKind,
++};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// checks for `Box::new(T::default())`, which is better written as
 +    /// `Box::<T>::default()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// First, it's more complex, involving two calls instead of one.
 +    /// Second, `Box::default()` can be faster
 +    /// [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box).
 +    ///
-     /// ### Known problems
-     /// The lint may miss some cases (e.g. Box::new(String::from(""))).
-     /// On the other hand, it will trigger on cases where the `default`
-     /// code comes from a macro that does something different based on
-     /// e.g. target operating system.
-     ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: Box<String> = Box::new(Default::default());
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x: Box<String> = Box::default();
 +    /// ```
 +    #[clippy::version = "1.65.0"]
 +    pub BOX_DEFAULT,
 +    perf,
 +    "Using Box::new(T::default()) instead of Box::default()"
 +}
 +
 +declare_lint_pass!(BoxDefault => [BOX_DEFAULT]);
 +
 +impl LateLintPass<'_> for BoxDefault {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        if let ExprKind::Call(box_new, [arg]) = expr.kind
 +            && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind
-             && let ExprKind::Call(..) = arg.kind
++            && let ExprKind::Call(arg_path, ..) = arg.kind
 +            && !in_external_macro(cx.sess(), expr.span)
-             && expr.span.eq_ctxt(arg.span)
++            && (expr.span.eq_ctxt(arg.span) || is_vec_expn(cx, arg))
 +            && seg.ident.name == sym::new
-             && path_def_id(cx, ty) == cx.tcx.lang_items().owned_box()
++            && path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box())
 +            && is_default_equivalent(cx, arg)
 +        {
-             span_lint_and_help(
++            let arg_ty = cx.typeck_results().expr_ty(arg);
++            span_lint_and_sugg(
 +                cx,
 +                BOX_DEFAULT,
 +                expr.span,
 +                "`Box::new(_)` of default value",
-                 None,
-                 "use `Box::default()` instead",
++                "try",
++                if is_plain_default(arg_path) || given_type(cx, expr) {
++                    "Box::default()".into()
++                } else {
++                    format!("Box::<{arg_ty}>::default()")
++                },
++                Applicability::MachineApplicable
 +            );
 +        }
 +    }
 +}
++
++fn is_plain_default(arg_path: &Expr<'_>) -> bool {
++    // we need to match the actual path so we don't match e.g. "u8::default"
++    if let ExprKind::Path(QPath::Resolved(None, path)) = &arg_path.kind {
++        // avoid generic parameters
++        match_path(path, &paths::DEFAULT_TRAIT_METHOD) && path.segments.iter().all(|seg| seg.args.is_none())
++    } else {
++        false
++    }
++}
++
++fn is_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
++    macro_backtrace(expr.span)
++        .next()
++        .map_or(false, |call| cx.tcx.is_diagnostic_item(sym::vec_macro, call.def_id))
++}
++
++#[derive(Default)]
++struct InferVisitor(bool);
++
++impl<'tcx> Visitor<'tcx> for InferVisitor {
++    fn visit_ty(&mut self, t: &rustc_hir::Ty<'_>) {
++        self.0 |= matches!(t.kind, TyKind::Infer | TyKind::OpaqueDef(..) | TyKind::TraitObject(..));
++        if !self.0 {
++            walk_ty(self, t);
++        }
++    }
++}
++
++fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
++    match get_parent_node(cx.tcx, expr.hir_id) {
++        Some(Node::Local(Local { ty: Some(ty), .. })) => {
++            let mut v = InferVisitor::default();
++            v.visit_ty(ty);
++            !v.0
++        },
++        Some(
++            Node::Expr(Expr {
++                kind: ExprKind::Call(path, args),
++                ..
++            }) | Node::Block(Block {
++                expr:
++                    Some(Expr {
++                        kind: ExprKind::Call(path, args),
++                        ..
++                    }),
++                ..
++            }),
++        ) => {
++            if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id) &&
++                let Some(sig) = expr_sig(cx, path) &&
++                let Some(input) = sig.input(index)
++            {
++                input.no_bound_vars().is_some()
++            } else {
++                false
++            }
++        },
++        _ => false,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9409f4844f54b4140217260c97d1f9f09e32f8e4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet_opt;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::LateContext;
++use rustc_middle::{
++    mir::Mutability,
++    ty::{self, Ty, TypeAndMut},
++};
++
++use super::AS_PTR_CAST_MUT;
++
++pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>) {
++    if let ty::RawPtr(ptrty @ TypeAndMut { mutbl: Mutability::Mut, .. }) = cast_to.kind()
++        && let ty::RawPtr(TypeAndMut { mutbl: Mutability::Not, .. }) =
++            cx.typeck_results().node_type(cast_expr.hir_id).kind()
++        && let ExprKind::MethodCall(method_name, receiver, [], _) = cast_expr.peel_blocks().kind
++        && method_name.ident.name == rustc_span::sym::as_ptr
++        && let Some(as_ptr_did) = cx.typeck_results().type_dependent_def_id(cast_expr.peel_blocks().hir_id)
++        && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did)
++        && let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next()
++        && let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind()
++        && let Some(recv) = snippet_opt(cx, receiver.span)
++    {
++        // `as_mut_ptr` might not exist
++        let applicability = Applicability::MaybeIncorrect;
++
++        span_lint_and_sugg(
++            cx,
++            AS_PTR_CAST_MUT,
++            expr.span,
++            &format!("casting the result of `as_ptr` to *{ptrty}"),
++            "replace with",
++            format!("{recv}.as_mut_ptr()"),
++            applicability
++        );
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..322dc41b3a197dd4301a45c355f193f466d9be61
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++use super::CAST_NAN_TO_INT;
++
++use clippy_utils::consts::{constant, Constant};
++use clippy_utils::diagnostics::span_lint_and_note;
++use rustc_hir::Expr;
++use rustc_lint::LateContext;
++use rustc_middle::ty::Ty;
++
++pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, from_ty: Ty<'_>, to_ty: Ty<'_>) {
++    if from_ty.is_floating_point() && to_ty.is_integral() && is_known_nan(cx, cast_expr) {
++        span_lint_and_note(
++            cx,
++            CAST_NAN_TO_INT,
++            expr.span,
++            &format!("casting a known NaN to {to_ty}"),
++            None,
++            "this always evaluates to 0",
++        );
++    }
++}
++
++fn is_known_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
++    match constant(cx, cx.typeck_results(), e) {
++        Some((Constant::F64(n), _)) => n.is_nan(),
++        Some((Constant::F32(n), _)) => n.is_nan(),
++        _ => false,
++    }
++}
index cc5d346b954e3d393e71fa6090c4bcc0839bafa7,0000000000000000000000000000000000000000..b72c4c772f1ce24d655a3d55ddf8d188d28e24f8
mode 100644,000000..100644
--- /dev/null
@@@ -1,687 -1,0 +1,742 @@@
-     CAST_SLICE_FROM_RAW_PARTS
++mod as_ptr_cast_mut;
 +mod as_underscore;
 +mod borrow_as_ptr;
 +mod cast_abs_to_unsigned;
 +mod cast_enum_constructor;
 +mod cast_lossless;
++mod cast_nan_to_int;
 +mod cast_possible_truncation;
 +mod cast_possible_wrap;
 +mod cast_precision_loss;
 +mod cast_ptr_alignment;
 +mod cast_ref_to_mut;
 +mod cast_sign_loss;
 +mod cast_slice_different_sizes;
 +mod cast_slice_from_raw_parts;
 +mod char_lit_as_u8;
 +mod fn_to_numeric_cast;
 +mod fn_to_numeric_cast_any;
 +mod fn_to_numeric_cast_with_truncation;
 +mod ptr_as_ptr;
 +mod unnecessary_cast;
 +mod utils;
 +
 +use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs};
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from any numerical to a float type where
 +    /// the receiving type cannot store all values from the original type without
 +    /// rounding errors. This possible rounding is to be expected, so this lint is
 +    /// `Allow` by default.
 +    ///
 +    /// Basically, this warns on casting any integer with 32 or more bits to `f32`
 +    /// or any 64-bit integer to `f64`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's not bad at all. But in some applications it can be
 +    /// helpful to know where precision loss can take place. This lint can help find
 +    /// those places in the code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = u64::MAX;
 +    /// x as f64;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_PRECISION_LOSS,
 +    pedantic,
 +    "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from a signed to an unsigned numerical
 +    /// type. In this case, negative values wrap around to large positive values,
 +    /// which can be quite surprising in practice. However, as the cast works as
 +    /// defined, this lint is `Allow` by default.
 +    ///
 +    /// ### Why is this bad?
 +    /// Possibly surprising results. You can activate this lint
 +    /// as a one-time check to see where numerical wrapping can arise.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let y: i8 = -1;
 +    /// y as u128; // will return 18446744073709551615
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_SIGN_LOSS,
 +    pedantic,
 +    "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts between numerical types that may
 +    /// truncate large values. This is expected behavior, so the cast is `Allow` by
 +    /// default.
 +    ///
 +    /// ### Why is this bad?
 +    /// In some problem domains, it is good practice to avoid
 +    /// truncation. This lint can be activated to help assess where additional
 +    /// checks could be beneficial.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn as_u8(x: u64) -> u8 {
 +    ///     x as u8
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_POSSIBLE_TRUNCATION,
 +    pedantic,
 +    "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from an unsigned type to a signed type of
 +    /// the same size. Performing such a cast is a 'no-op' for the compiler,
 +    /// i.e., nothing is changed at the bit level, and the binary representation of
 +    /// the value is reinterpreted. This can cause wrapping if the value is too big
 +    /// for the target signed type. However, the cast works as defined, so this lint
 +    /// is `Allow` by default.
 +    ///
 +    /// ### Why is this bad?
 +    /// While such a cast is not bad in itself, the results can
 +    /// be surprising when this is not the intended behavior, as demonstrated by the
 +    /// example below.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// u32::MAX as i32; // will yield a value of `-1`
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_POSSIBLE_WRAP,
 +    pedantic,
 +    "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts between numerical types that may
 +    /// be replaced by safe conversion functions.
 +    ///
 +    /// ### Why is this bad?
 +    /// Rust's `as` keyword will perform many kinds of
 +    /// conversions, including silently lossy conversions. Conversion functions such
 +    /// as `i32::from` will only perform lossless conversions. Using the conversion
 +    /// functions prevents conversions from turning into silent lossy conversions if
 +    /// the types of the input expressions ever change, and make it easier for
 +    /// people reading the code to know that the conversion is lossless.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn as_u64(x: u8) -> u64 {
 +    ///     x as u64
 +    /// }
 +    /// ```
 +    ///
 +    /// Using `::from` would look like this:
 +    ///
 +    /// ```rust
 +    /// fn as_u64(x: u8) -> u64 {
 +    ///     u64::from(x)
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_LOSSLESS,
 +    pedantic,
 +    "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts to the same type, casts of int literals to integer types
 +    /// and casts of float literals to float types.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's just unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = 2i32 as i32;
 +    /// let _ = 0.5 as f32;
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust
 +    /// let _ = 2_i32;
 +    /// let _ = 0.5_f32;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNNECESSARY_CAST,
 +    complexity,
 +    "cast to the same type, e.g., `x as i32` where `x: i32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts, using `as` or `pointer::cast`,
 +    /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer
 +    ///
 +    /// ### Why is this bad?
 +    /// Dereferencing the resulting pointer may be undefined
 +    /// behavior.
 +    ///
 +    /// ### Known problems
 +    /// Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar
 +    /// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like
 +    /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (&1u8 as *const u8) as *const u16;
 +    /// let _ = (&mut 1u8 as *mut u8) as *mut u16;
 +    ///
 +    /// (&1u8 as *const u8).cast::<u16>();
 +    /// (&mut 1u8 as *mut u8).cast::<u16>();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_PTR_ALIGNMENT,
 +    pedantic,
 +    "cast from a pointer to a more-strictly-aligned pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of function pointers to something other than usize
 +    ///
 +    /// ### Why is this bad?
 +    /// Casting a function pointer to anything other than usize/isize is not portable across
 +    /// architectures, because you end up losing bits if the target type is too small or end up with a
 +    /// bunch of extra bits that waste space and add more instructions to the final binary than
 +    /// strictly necessary for the problem
 +    ///
 +    /// Casting to isize also doesn't make sense since there are no signed addresses.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn fun() -> i32 { 1 }
 +    /// let _ = fun as i64;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # fn fun() -> i32 { 1 }
 +    /// let _ = fun as usize;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FN_TO_NUMERIC_CAST,
 +    style,
 +    "casting a function pointer to a numeric type other than usize"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of a function pointer to a numeric type not wide enough to
 +    /// store address.
 +    ///
 +    /// ### Why is this bad?
 +    /// Such a cast discards some bits of the function's address. If this is intended, it would be more
 +    /// clearly expressed by casting to usize first, then casting the usize to the intended type (with
 +    /// a comment) to perform the truncation.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn fn1() -> i16 {
 +    ///     1
 +    /// };
 +    /// let _ = fn1 as i32;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// // Cast to usize first, then comment with the reason for the truncation
 +    /// fn fn1() -> i16 {
 +    ///     1
 +    /// };
 +    /// let fn_ptr = fn1 as usize;
 +    /// let fn_ptr_truncated = fn_ptr as i32;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
 +    style,
 +    "casting a function pointer to a numeric type not wide enough to store the address"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of a function pointer to any integer type.
 +    ///
 +    /// ### Why is this bad?
 +    /// Casting a function pointer to an integer can have surprising results and can occur
 +    /// accidentally if parentheses are omitted from a function call. If you aren't doing anything
 +    /// low-level with function pointers then you can opt-out of casting functions to integers in
 +    /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
 +    /// pointer casts in your code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // fn1 is cast as `usize`
 +    /// fn fn1() -> u16 {
 +    ///     1
 +    /// };
 +    /// let _ = fn1 as usize;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// // maybe you intended to call the function?
 +    /// fn fn2() -> u16 {
 +    ///     1
 +    /// };
 +    /// let _ = fn2() as usize;
 +    ///
 +    /// // or
 +    ///
 +    /// // maybe you intended to cast it to a function type?
 +    /// fn fn3() -> u16 {
 +    ///     1
 +    /// }
 +    /// let _ = fn3 as fn() -> u16;
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub FN_TO_NUMERIC_CAST_ANY,
 +    restriction,
 +    "casting a function pointer to any integer type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of `&T` to `&mut T` anywhere in the code.
 +    ///
 +    /// ### Why is this bad?
 +    /// It’s basically guaranteed to be undefined behavior.
 +    /// `UnsafeCell` is the only way to obtain aliasable data that is considered
 +    /// mutable.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// fn x(r: &i32) {
 +    ///     unsafe {
 +    ///         *(r as *const _ as *mut _) += 1;
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Instead consider using interior mutability types.
 +    ///
 +    /// ```rust
 +    /// use std::cell::UnsafeCell;
 +    ///
 +    /// fn x(r: &UnsafeCell<i32>) {
 +    ///     unsafe {
 +    ///         *r.get() += 1;
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.33.0"]
 +    pub CAST_REF_TO_MUT,
 +    correctness,
 +    "a cast of reference to a mutable pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expressions where a character literal is cast
 +    /// to `u8` and suggests using a byte literal instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// In general, casting values to smaller types is
 +    /// error-prone and should be avoided where possible. In the particular case of
 +    /// converting a character literal to u8, it is easy to avoid by just using a
 +    /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter
 +    /// than `'a' as u8`.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// 'x' as u8
 +    /// ```
 +    ///
 +    /// A better version, using the byte literal:
 +    ///
 +    /// ```rust,ignore
 +    /// b'x'
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CHAR_LIT_AS_U8,
 +    complexity,
 +    "casting a character literal to `u8` truncates"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `as` casts between raw pointers without changing its mutability,
 +    /// namely `*const T` to `*const U` and `*mut T` to `*mut U`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because
 +    /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let ptr: *const u32 = &42_u32;
 +    /// let mut_ptr: *mut u32 = &mut 42_u32;
 +    /// let _ = ptr as *const i32;
 +    /// let _ = mut_ptr as *mut i32;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let ptr: *const u32 = &42_u32;
 +    /// let mut_ptr: *mut u32 = &mut 42_u32;
 +    /// let _ = ptr.cast::<i32>();
 +    /// let _ = mut_ptr.cast::<i32>();
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub PTR_AS_PTR,
 +    pedantic,
 +    "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from an enum type to an integral type which will definitely truncate the
 +    /// value.
 +    ///
 +    /// ### Why is this bad?
 +    /// The resulting integral value will not match the value of the variant it came from.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// enum E { X = 256 };
 +    /// let _ = E::X as u8;
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub CAST_ENUM_TRUNCATION,
 +    suspicious,
 +    "casts from an enum type to an integral type which will truncate the value"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `as` casts between raw pointers to slices with differently sized elements.
 +    ///
 +    /// ### Why is this bad?
 +    /// The produced raw pointer to a slice does not update its length metadata. The produced
 +    /// pointer will point to a different number of bytes than the original pointer because the
 +    /// length metadata of a raw slice pointer is in elements rather than bytes.
 +    /// Producing a slice reference from the raw pointer will either create a slice with
 +    /// less data (which can be surprising) or create a slice with more data and cause Undefined Behavior.
 +    ///
 +    /// ### Example
 +    /// // Missing data
 +    /// ```rust
 +    /// let a = [1_i32, 2, 3, 4];
 +    /// let p = &a as *const [i32] as *const [u8];
 +    /// unsafe {
 +    ///     println!("{:?}", &*p);
 +    /// }
 +    /// ```
 +    /// // Undefined Behavior (note: also potential alignment issues)
 +    /// ```rust
 +    /// let a = [1_u8, 2, 3, 4];
 +    /// let p = &a as *const [u8] as *const [u32];
 +    /// unsafe {
 +    ///     println!("{:?}", &*p);
 +    /// }
 +    /// ```
 +    /// Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length
 +    /// ```rust
 +    /// let a = [1_i32, 2, 3, 4];
 +    /// let old_ptr = &a as *const [i32];
 +    /// // The data pointer is cast to a pointer to the target `u8` not `[u8]`
 +    /// // The length comes from the known length of 4 i32s times the 4 bytes per i32
 +    /// let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16);
 +    /// unsafe {
 +    ///     println!("{:?}", &*new_ptr);
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub CAST_SLICE_DIFFERENT_SIZES,
 +    correctness,
 +    "casting using `as` between raw pointers to slices of types with different sizes"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from an enum tuple constructor to an integer.
 +    ///
 +    /// ### Why is this bad?
 +    /// The cast is easily confused with casting a c-like enum value to an integer.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// enum E { X(i32) };
 +    /// let _ = E::X as usize;
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub CAST_ENUM_CONSTRUCTOR,
 +    suspicious,
 +    "casts from an enum tuple constructor to an integer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for uses of the `abs()` method that cast the result to unsigned.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `unsigned_abs()` method avoids panic when called on the MIN value.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: i32 = -42;
 +    /// let y: u32 = x.abs() as u32;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x: i32 = -42;
 +    /// let y: u32 = x.unsigned_abs();
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub CAST_ABS_TO_UNSIGNED,
 +    suspicious,
 +    "casting the result of `abs()` to an unsigned integer can panic"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Check for the usage of `as _` conversion using inferred type.
 +    ///
 +    /// ### Why is this bad?
 +    /// The conversion might include lossy conversion and dangerous cast that might go
 +    /// undetected due to the type being inferred.
 +    ///
 +    /// The lint is allowed by default as using `_` is less wordy than always specifying the type.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo(n: usize) {}
 +    /// let n: u16 = 256;
 +    /// foo(n as _);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn foo(n: usize) {}
 +    /// let n: u16 = 256;
 +    /// foo(n as usize);
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub AS_UNDERSCORE,
 +    restriction,
 +    "detects `as _` conversion"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the usage of `&expr as *const T` or
 +    /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
 +    /// `ptr::addr_of_mut` instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// This would improve readability and avoid creating a reference
 +    /// that points to an uninitialized value or unaligned place.
 +    /// Read the `ptr::addr_of` docs for more information.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let val = 1;
 +    /// let p = &val as *const i32;
 +    ///
 +    /// let mut val_mut = 1;
 +    /// let p_mut = &mut val_mut as *mut i32;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let val = 1;
 +    /// let p = std::ptr::addr_of!(val);
 +    ///
 +    /// let mut val_mut = 1;
 +    /// let p_mut = std::ptr::addr_of_mut!(val_mut);
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub BORROW_AS_PTR,
 +    pedantic,
 +    "borrowing just to cast to a raw pointer"
 +}
++
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for a raw slice being cast to a slice pointer
 +    ///
 +    /// ### Why is this bad?
 +    /// This can result in multiple `&mut` references to the same location when only a pointer is
 +    /// required.
 +    /// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require
 +    /// the same [safety requirements] to be upheld.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _;
 +    /// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len);
 +    /// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len);
 +    /// ```
 +    /// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety
 +    #[clippy::version = "1.64.0"]
 +    pub CAST_SLICE_FROM_RAW_PARTS,
 +    suspicious,
 +    "casting a slice created from a pointer and length to a slice pointer"
 +}
 +
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer
++    ///
++    /// ### Why is this bad?
++    /// Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior
++    /// mutability is used, making it unlikely that having it as a mutable pointer is correct.
++    ///
++    /// ### Example
++    /// ```rust
++    /// let string = String::with_capacity(1);
++    /// let ptr = string.as_ptr() as *mut u8;
++    /// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// let mut string = String::with_capacity(1);
++    /// let ptr = string.as_mut_ptr();
++    /// unsafe { ptr.write(4) };
++    /// ```
++    #[clippy::version = "1.66.0"]
++    pub AS_PTR_CAST_MUT,
++    nursery,
++    "casting the result of the `&self`-taking `as_ptr` to a mutabe pointer"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for a known NaN float being cast to an integer
++    ///
++    /// ### Why is this bad?
++    /// NaNs are cast into zero, so one could simply use this and make the
++    /// code more readable. The lint could also hint at a programmer error.
++    ///
++    /// ### Example
++    /// ```rust,ignore
++    /// let _: (0.0_f32 / 0.0) as u64;
++    /// ```
++    /// Use instead:
++    /// ```rust,ignore
++    /// let _: = 0_u64;
++    /// ```
++    #[clippy::version = "1.64.0"]
++    pub CAST_NAN_TO_INT,
++    suspicious,
++    "casting a known floating-point NaN into an integer"
++}
++
 +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,
 +    AS_UNDERSCORE,
 +    BORROW_AS_PTR,
++    CAST_SLICE_FROM_RAW_PARTS,
++    AS_PTR_CAST_MUT,
++    CAST_NAN_TO_INT,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Casts {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if !in_external_macro(cx.sess(), expr.span) {
 +            ptr_as_ptr::check(cx, expr, self.msrv);
 +        }
 +
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind {
 +            if is_hir_ty_cfg_dependant(cx, cast_to_hir) {
 +                return;
 +            }
 +            let (cast_from, cast_to) = (
 +                cx.typeck_results().expr_ty(cast_expr),
 +                cx.typeck_results().expr_ty(expr),
 +            );
 +
 +            if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
 +                return;
 +            }
 +            cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv);
++            as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to);
 +            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_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to);
 +                }
 +                cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
 +                cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
 +            }
 +
 +            as_underscore::check(cx, expr, cast_to_hir);
 +
 +            if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
 +                borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
 +            }
 +        }
 +
 +        cast_ref_to_mut::check(cx, expr);
 +        cast_ptr_alignment::check(cx, expr);
 +        char_lit_as_u8::check(cx, expr);
 +        ptr_as_ptr::check(cx, expr, self.msrv);
 +        cast_slice_different_sizes::check(cx, expr, self.msrv);
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
index 21ed7f4844cc593948bbb21bc6a69c1c37ca126c,0000000000000000000000000000000000000000..c8596987e4d719b158aa879d7f1bc2bf452d4c16
mode 100644,000000..100644
--- /dev/null
@@@ -1,154 -1,0 +1,151 @@@
-             LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {
-                 return false;
-             },
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::get_parent_expr;
 +use clippy_utils::numeric_literal::NumericLiteral;
 +use clippy_utils::source::snippet_opt;
 +use if_chain::if_chain;
 +use rustc_ast::{LitFloatType, LitIntType, LitKind};
 +use rustc_errors::Applicability;
 +use rustc_hir::def::Res;
 +use rustc_hir::{Expr, ExprKind, Lit, QPath, TyKind, UnOp};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{self, FloatTy, InferTy, Ty};
 +
 +use super::UNNECESSARY_CAST;
 +
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &Expr<'tcx>,
 +    cast_expr: &Expr<'tcx>,
 +    cast_from: Ty<'tcx>,
 +    cast_to: Ty<'tcx>,
 +) -> bool {
 +    // skip non-primitive type cast
 +    if_chain! {
 +        if let ExprKind::Cast(_, cast_to) = expr.kind;
 +        if let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind;
 +        if let Res::PrimTy(_) = path.res;
 +        then {}
 +        else {
 +            return false
 +        }
 +    }
 +
 +    let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default();
 +
 +    if let Some(lit) = get_numeric_literal(cast_expr) {
 +        let literal_str = &cast_str;
 +
 +        if_chain! {
 +            if let LitKind::Int(n, _) = lit.node;
 +            if let Some(src) = snippet_opt(cx, cast_expr.span);
 +            if cast_to.is_floating_point();
 +            if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node);
 +            let from_nbits = 128 - n.leading_zeros();
 +            let to_nbits = fp_ty_mantissa_nbits(cast_to);
 +            if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal();
 +            then {
 +                lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
 +                return true
 +            }
 +        }
 +
 +        match lit.node {
 +            LitKind::Int(_, LitIntType::Unsuffixed) if cast_to.is_integral() => {
 +                lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to);
 +                return false;
 +            },
 +            LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => {
 +                lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to);
 +                return false;
 +            },
 +            LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_))
 +            | LitKind::Float(_, LitFloatType::Suffixed(_))
 +                if cast_from.kind() == cast_to.kind() =>
 +            {
 +                if let Some(src) = snippet_opt(cx, cast_expr.span) {
 +                    if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) {
 +                        lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
 +                        return true;
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +
 +    if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) {
 +        span_lint_and_sugg(
 +            cx,
 +            UNNECESSARY_CAST,
 +            expr.span,
 +            &format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"),
 +            "try",
 +            cast_str,
 +            Applicability::MachineApplicable,
 +        );
 +        return true;
 +    }
 +
 +    false
 +}
 +
 +fn lint_unnecessary_cast(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    raw_literal_str: &str,
 +    cast_from: Ty<'_>,
 +    cast_to: Ty<'_>,
 +) {
 +    let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" };
 +    // first we remove all matches so `-(1)` become `-1`, and remove trailing dots, so `1.` become `1`
 +    let literal_str = raw_literal_str
 +        .replace(['(', ')'], "")
 +        .trim_end_matches('.')
 +        .to_string();
 +    // we know need to check if the parent is a method call, to add parenthesis accordingly (eg:
 +    // (-1).foo() instead of -1.foo())
 +    let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr)
 +        && let ExprKind::MethodCall(..) = parent_expr.kind
 +        && literal_str.starts_with('-')
 +        {
 +            format!("({literal_str}_{cast_to})")
 +
 +        } else {
 +            format!("{literal_str}_{cast_to}")
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        UNNECESSARY_CAST,
 +        expr.span,
 +        &format!("casting {literal_kind_name} literal to `{cast_to}` is unnecessary"),
 +        "try",
 +        sugg,
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> {
 +    match expr.kind {
 +        ExprKind::Lit(ref lit) => Some(lit),
 +        ExprKind::Unary(UnOp::Neg, e) => {
 +            if let ExprKind::Lit(ref lit) = e.kind {
 +                Some(lit)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +/// Returns the mantissa bits wide of a fp type.
 +/// Will return 0 if the type is not a fp
 +fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 {
 +    match typ.kind() {
 +        ty::Float(FloatTy::F32) => 23,
 +        ty::Float(FloatTy::F64) | ty::Infer(InferTy::FloatVar(_)) => 52,
 +        _ => 0,
 +    }
 +}
index a05b41eb3ab52f72441b53a75dcadfdfa167b1e6,0000000000000000000000000000000000000000..0fe973b49a3550235520ab0f87a55139f46fd18f
mode 100644,000000..100644
--- /dev/null
@@@ -1,132 -1,0 +1,136 @@@
- use clippy_utils::{get_trait_def_id, if_sequence, in_constant, is_else_clause, paths, SpanlessEq};
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::ty::implements_trait;
-                 let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[]));
++use clippy_utils::{if_sequence, in_constant, is_else_clause, SpanlessEq};
 +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
 +    /// Checks comparison chains written with `if` that can be
 +    /// rewritten with `match` and `cmp`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `if` is not guaranteed to be exhaustive and conditionals can get
 +    /// repetitive
 +    ///
 +    /// ### Known problems
 +    /// The match statement may be slower due to the compiler
 +    /// not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354)
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// # fn a() {}
 +    /// # fn b() {}
 +    /// # fn c() {}
 +    /// fn f(x: u8, y: u8) {
 +    ///     if x > y {
 +    ///         a()
 +    ///     } else if x < y {
 +    ///         b()
 +    ///     } else {
 +    ///         c()
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// use std::cmp::Ordering;
 +    /// # fn a() {}
 +    /// # fn b() {}
 +    /// # fn c() {}
 +    /// fn f(x: u8, y: u8) {
 +    ///      match x.cmp(&y) {
 +    ///          Ordering::Greater => a(),
 +    ///          Ordering::Less => b(),
 +    ///          Ordering::Equal => c()
 +    ///      }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub COMPARISON_CHAIN,
 +    style,
 +    "`if`s that can be rewritten with `match` and `cmp`"
 +}
 +
 +declare_lint_pass!(ComparisonChain => [COMPARISON_CHAIN]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        // We only care about the top-most `if` in the chain
 +        if is_else_clause(cx.tcx, expr) {
 +            return;
 +        }
 +
 +        if in_constant(cx, expr.hir_id) {
 +            return;
 +        }
 +
 +        // Check that there exists at least one explicit else condition
 +        let (conds, _) = if_sequence(expr);
 +        if conds.len() < 2 {
 +            return;
 +        }
 +
 +        for cond in conds.windows(2) {
 +            if let (&ExprKind::Binary(ref kind1, lhs1, rhs1), &ExprKind::Binary(ref kind2, lhs2, rhs2)) =
 +                (&cond[0].kind, &cond[1].kind)
 +            {
 +                if !kind_is_cmp(kind1.node) || !kind_is_cmp(kind2.node) {
 +                    return;
 +                }
 +
 +                // Check that both sets of operands are equal
 +                let mut spanless_eq = SpanlessEq::new(cx);
 +                let same_fixed_operands = spanless_eq.eq_expr(lhs1, lhs2) && spanless_eq.eq_expr(rhs1, rhs2);
 +                let same_transposed_operands = spanless_eq.eq_expr(lhs1, rhs2) && spanless_eq.eq_expr(rhs1, lhs2);
 +
 +                if !same_fixed_operands && !same_transposed_operands {
 +                    return;
 +                }
 +
 +                // Check that if the operation is the same, either it's not `==` or the operands are transposed
 +                if kind1.node == kind2.node {
 +                    if kind1.node == BinOpKind::Eq {
 +                        return;
 +                    }
 +                    if !same_transposed_operands {
 +                        return;
 +                    }
 +                }
 +
 +                // Check that the type being compared implements `core::cmp::Ord`
 +                let ty = cx.typeck_results().expr_ty(lhs1);
++                let is_ord = cx
++                    .tcx
++                    .get_diagnostic_item(sym::Ord)
++                    .map_or(false, |id| implements_trait(cx, ty, id, &[]));
 +
 +                if !is_ord {
 +                    return;
 +                }
 +            } else {
 +                // We only care about comparison chains
 +                return;
 +            }
 +        }
 +        span_lint_and_help(
 +            cx,
 +            COMPARISON_CHAIN,
 +            expr.span,
 +            "`if` chain can be rewritten with `match`",
 +            None,
 +            "consider rewriting the `if` chain to use `cmp` and `match`",
 +        );
 +    }
 +}
 +
 +fn kind_is_cmp(kind: BinOpKind) -> bool {
 +    matches!(kind, BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq)
 +}
index 3ed9cd36a2292a171e0ff63e7e2574ef5c8e7aea,0000000000000000000000000000000000000000..03460689e19ad7a5bdac313b6c2f6eade3fb7b77
mode 100644,000000..100644
--- /dev/null
@@@ -1,245 -1,0 +1,238 @@@
- use clippy_utils::numeric_literal;
 +use clippy_utils::diagnostics::span_lint_hir_and_then;
-     Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind,
 +use clippy_utils::source::snippet_opt;
++use clippy_utils::{get_parent_node, numeric_literal};
 +use if_chain::if_chain;
 +use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    intravisit::{walk_expr, walk_stmt, Visitor},
-         let mut visitor = NumericFallbackVisitor::new(cx);
++    Body, Expr, ExprKind, HirId, ItemKind, Lit, Node, Stmt, StmtKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::{
 +    lint::in_external_macro,
 +    ty::{self, FloatTy, IntTy, PolyFnSig, Ty},
 +};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use std::iter;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type
 +    /// inference.
 +    ///
 +    /// Default numeric fallback means that if numeric types have not yet been bound to concrete
 +    /// types at the end of type inference, then integer type is bound to `i32`, and similarly
 +    /// floating type is bound to `f64`.
 +    ///
 +    /// See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback.
 +    ///
 +    /// ### Why is this bad?
 +    /// For those who are very careful about types, default numeric fallback
 +    /// can be a pitfall that cause unexpected runtime behavior.
 +    ///
 +    /// ### Known problems
 +    /// This lint can only be allowed at the function level or above.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let i = 10;
 +    /// let f = 1.23;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let i = 10i32;
 +    /// let f = 1.23f64;
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub DEFAULT_NUMERIC_FALLBACK,
 +    restriction,
 +    "usage of unconstrained numeric literals which may cause default numeric fallback."
 +}
 +
 +declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]);
 +
 +impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback {
 +    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
-     ty_bounds: Vec<TyBound<'tcx>>,
++        let is_parent_const = if let Some(Node::Item(item)) = get_parent_node(cx.tcx, body.id().hir_id) {
++            matches!(item.kind, ItemKind::Const(..))
++        } else {
++            false
++        };
++        let mut visitor = NumericFallbackVisitor::new(cx, is_parent_const);
 +        visitor.visit_body(body);
 +    }
 +}
 +
 +struct NumericFallbackVisitor<'a, 'tcx> {
 +    /// Stack manages type bound of exprs. The top element holds current expr type.
-     fn new(cx: &'a LateContext<'tcx>) -> Self {
++    ty_bounds: Vec<ExplicitTyBound>,
 +
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
-             ty_bounds: vec![TyBound::Nothing],
++    fn new(cx: &'a LateContext<'tcx>, is_parent_const: bool) -> Self {
 +        Self {
-                 if let Some(ty_bound) = self.ty_bounds.last();
++            ty_bounds: vec![if is_parent_const {
++                ExplicitTyBound(true)
++            } else {
++                ExplicitTyBound(false)
++            }],
 +            cx,
 +        }
 +    }
 +
 +    /// Check whether a passed literal has potential to cause fallback or not.
 +    fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) {
 +        if_chain! {
 +                if !in_external_macro(self.cx.sess(), lit.span);
-                 if !ty_bound.is_numeric();
++                if matches!(self.ty_bounds.last(), Some(ExplicitTyBound(false)));
 +                if matches!(lit.node,
 +                            LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
-                         self.ty_bounds.push(TyBound::Ty(*bound));
 +                then {
 +                    let (suffix, is_float) = match lit_ty.kind() {
 +                        ty::Int(IntTy::I32) => ("i32", false),
 +                        ty::Float(FloatTy::F64) => ("f64", true),
 +                        // Default numeric fallback never results in other types.
 +                        _ => return,
 +                    };
 +
 +                    let src = if let Some(src) = snippet_opt(self.cx, lit.span) {
 +                        src
 +                    } else {
 +                        match lit.node {
 +                            LitKind::Int(src, _) => format!("{src}"),
 +                            LitKind::Float(src, _) => format!("{src}"),
 +                            _ => return,
 +                        }
 +                    };
 +                    let sugg = numeric_literal::format(&src, Some(suffix), is_float);
 +                    span_lint_hir_and_then(
 +                        self.cx,
 +                        DEFAULT_NUMERIC_FALLBACK,
 +                        emit_hir_id,
 +                        lit.span,
 +                        "default numeric fallback might occur",
 +                        |diag| {
 +                            diag.span_suggestion(lit.span, "consider adding suffix", sugg, Applicability::MaybeIncorrect);
 +                        }
 +                    );
 +                }
 +        }
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        match &expr.kind {
 +            ExprKind::Call(func, args) => {
 +                if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) {
 +                    for (expr, bound) in iter::zip(*args, fn_sig.skip_binder().inputs()) {
 +                        // Push found arg type, then visit arg.
-                         self.ty_bounds.push(TyBound::Ty(*bound));
++                        self.ty_bounds.push((*bound).into());
 +                        self.visit_expr(expr);
 +                        self.ty_bounds.pop();
 +                    }
 +                    return;
 +                }
 +            },
 +
 +            ExprKind::MethodCall(_, receiver, args, _) => {
 +                if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
 +                    let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder();
 +                    for (expr, bound) in iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) {
-                             self.ty_bounds.push(TyBound::Nothing);
++                        self.ty_bounds.push((*bound).into());
 +                        self.visit_expr(expr);
 +                        self.ty_bounds.pop();
 +                    }
 +                    return;
 +                }
 +            },
 +
 +            ExprKind::Struct(_, fields, base) => {
 +                let ty = self.cx.typeck_results().expr_ty(expr);
 +                if_chain! {
 +                    if let Some(adt_def) = ty.ty_adt_def();
 +                    if adt_def.is_struct();
 +                    if let Some(variant) = adt_def.variants().iter().next();
 +                    then {
 +                        let fields_def = &variant.fields;
 +
 +                        // Push field type then visit each field expr.
 +                        for field in fields.iter() {
 +                            let bound =
 +                                fields_def
 +                                    .iter()
 +                                    .find_map(|f_def| {
 +                                        if f_def.ident(self.cx.tcx) == field.ident
 +                                            { Some(self.cx.tcx.type_of(f_def.did)) }
 +                                        else { None }
 +                                    });
 +                            self.ty_bounds.push(bound.into());
 +                            self.visit_expr(field.expr);
 +                            self.ty_bounds.pop();
 +                        }
 +
 +                        // Visit base with no bound.
 +                        if let Some(base) = base {
-             StmtKind::Local(local) => {
-                 if local.ty.is_some() {
-                     self.ty_bounds.push(TyBound::Any);
-                 } else {
-                     self.ty_bounds.push(TyBound::Nothing);
-                 }
-             },
++                            self.ty_bounds.push(ExplicitTyBound(false));
 +                            self.visit_expr(base);
 +                            self.ty_bounds.pop();
 +                        }
 +                        return;
 +                    }
 +                }
 +            },
 +
 +            ExprKind::Lit(lit) => {
 +                let ty = self.cx.typeck_results().expr_ty(expr);
 +                self.check_lit(lit, ty, expr.hir_id);
 +                return;
 +            },
 +
 +            _ => {},
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +
 +    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
 +        match stmt.kind {
-             _ => self.ty_bounds.push(TyBound::Nothing),
++            // we cannot check the exact type since it's a hir::Ty which does not implement `is_numeric`
++            StmtKind::Local(local) => self.ty_bounds.push(ExplicitTyBound(local.ty.is_some())),
 +
- enum TyBound<'tcx> {
-     Any,
-     Ty(Ty<'tcx>),
-     Nothing,
- }
++            _ => self.ty_bounds.push(ExplicitTyBound(false)),
 +        }
 +
 +        walk_stmt(self, stmt);
 +        self.ty_bounds.pop();
 +    }
 +}
 +
 +fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<PolyFnSig<'tcx>> {
 +    let node_ty = cx.typeck_results().node_type_opt(hir_id)?;
 +    // We can't use `Ty::fn_sig` because it automatically performs substs, this may result in FNs.
 +    match node_ty.kind() {
 +        ty::FnDef(def_id, _) => Some(cx.tcx.fn_sig(*def_id)),
 +        ty::FnPtr(fn_sig) => Some(*fn_sig),
 +        _ => None,
 +    }
 +}
 +
++/// Wrapper around a `bool` to make the meaning of the value clearer
 +#[derive(Debug, Clone, Copy)]
- impl<'tcx> TyBound<'tcx> {
-     fn is_numeric(self) -> bool {
-         match self {
-             TyBound::Any => true,
-             TyBound::Ty(t) => t.is_numeric(),
-             TyBound::Nothing => false,
-         }
++struct ExplicitTyBound(pub bool);
 +
- impl<'tcx> From<Option<Ty<'tcx>>> for TyBound<'tcx> {
++impl<'tcx> From<Ty<'tcx>> for ExplicitTyBound {
++    fn from(v: Ty<'tcx>) -> Self {
++        Self(v.is_numeric())
 +    }
 +}
 +
-         match v {
-             Some(t) => TyBound::Ty(t),
-             None => TyBound::Nothing,
-         }
++impl<'tcx> From<Option<Ty<'tcx>>> for ExplicitTyBound {
 +    fn from(v: Option<Ty<'tcx>>) -> Self {
++        Self(v.map_or(false, Ty::is_numeric))
 +    }
 +}
index 02a16f765b7324117fc9c9fc380b661caccc7bf9,0000000000000000000000000000000000000000..a95d9f5390de3a858420ebcef41243cc39aadc70
mode 100644,000000..100644
--- /dev/null
@@@ -1,1500 -1,0 +1,1569 @@@
-     self as hir, def_id::DefId, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy,
-     GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind,
-     Path, QPath, TraitItem, TraitItemKind, TyKind, UnOp,
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
++use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
 +use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 +use clippy_utils::sugg::has_enclosing_paren;
 +use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
 +use clippy_utils::{
 +    fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local,
 +    walk_to_expr_usage,
 +};
 +use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
 +use rustc_data_structures::fx::FxIndexMap;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_ty, Visitor};
 +use rustc_hir::{
- impl_lint_pass!(Dereferencing => [
++    self as hir,
++    def_id::{DefId, LocalDefId},
++    BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId, ImplItem,
++    ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
++    TraitItemKind, TyKind, UnOp,
 +};
 +use rustc_index::bit_set::BitSet;
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::mir::{Rvalue, StatementKind};
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
 +use rustc_middle::ty::{
 +    self, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
 +    ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
 +};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
 +use rustc_trait_selection::infer::InferCtxtExt as _;
 +use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
 +use std::collections::VecDeque;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for explicit `deref()` or `deref_mut()` method calls.
 +    ///
 +    /// ### Why is this bad?
 +    /// Dereferencing by `&*x` or `&mut *x` is clearer and more concise,
 +    /// when not part of a method chain.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::ops::Deref;
 +    /// let a: &mut String = &mut String::from("foo");
 +    /// let b: &str = a.deref();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let a: &mut String = &mut String::from("foo");
 +    /// let b = &*a;
 +    /// ```
 +    ///
 +    /// This lint excludes:
 +    /// ```rust,ignore
 +    /// let _ = d.unwrap().deref();
 +    /// ```
 +    #[clippy::version = "1.44.0"]
 +    pub EXPLICIT_DEREF_METHODS,
 +    pedantic,
 +    "Explicit use of deref or deref_mut method while not in a method chain."
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for address of operations (`&`) that are going to
 +    /// be dereferenced immediately by the compiler.
 +    ///
 +    /// ### Why is this bad?
 +    /// Suggests that the receiver of the expression borrows
 +    /// the expression.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn fun(_a: &i32) {}
 +    ///
 +    /// let x: &i32 = &&&&&&5;
 +    /// fun(&x);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # fn fun(_a: &i32) {}
 +    /// let x: &i32 = &5;
 +    /// fun(x);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEEDLESS_BORROW,
 +    style,
 +    "taking a reference that is going to be automatically dereferenced"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `ref` bindings which create a reference to a reference.
 +    ///
 +    /// ### Why is this bad?
 +    /// The address-of operator at the use site is clearer about the need for a reference.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = Some("");
 +    /// if let Some(ref x) = x {
 +    ///     // use `x` here
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = Some("");
 +    /// if let Some(x) = x {
 +    ///     // use `&x` here
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.54.0"]
 +    pub REF_BINDING_TO_REFERENCE,
 +    pedantic,
 +    "`ref` binding to a reference"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for dereferencing expressions which would be covered by auto-deref.
 +    ///
 +    /// ### Why is this bad?
 +    /// This unnecessarily complicates the code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = String::new();
 +    /// let y: &str = &*x;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = String::new();
 +    /// let y: &str = &x;
 +    /// ```
 +    #[clippy::version = "1.64.0"]
 +    pub EXPLICIT_AUTO_DEREF,
 +    complexity,
 +    "dereferencing when the compiler would automatically dereference"
 +}
 +
- pub struct Dereferencing {
++impl_lint_pass!(Dereferencing<'_> => [
 +    EXPLICIT_DEREF_METHODS,
 +    NEEDLESS_BORROW,
 +    REF_BINDING_TO_REFERENCE,
 +    EXPLICIT_AUTO_DEREF,
 +]);
 +
 +#[derive(Default)]
- impl Dereferencing {
++pub struct Dereferencing<'tcx> {
 +    state: Option<(State, StateData)>,
 +
 +    // While parsing a `deref` method call in ufcs form, the path to the function is itself an
 +    // expression. This is to store the id of that expression so it can be skipped when
 +    // `check_expr` is called for it.
 +    skip_expr: Option<HirId>,
 +
 +    /// The body the first local was found in. Used to emit lints when the traversal of the body has
 +    /// been finished. Note we can't lint at the end of every body as they can be nested within each
 +    /// other.
 +    current_body: Option<BodyId>,
 +
 +    /// The list of locals currently being checked by the lint.
 +    /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
 +    /// This is needed for or patterns where one of the branches can be linted, but another can not
 +    /// be.
 +    ///
 +    /// e.g. `m!(x) | Foo::Bar(ref x)`
 +    ref_locals: FxIndexMap<HirId, Option<RefPat>>,
 +
++    /// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by
++    /// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead
++    /// be moved.
++    possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
++
 +    // `IntoIterator` for arrays requires Rust 1.53.
 +    msrv: Option<RustcVersion>,
 +}
 +
- impl<'tcx> LateLintPass<'tcx> for Dereferencing {
++impl<'tcx> Dereferencing<'tcx> {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self {
 +            msrv,
 +            ..Dereferencing::default()
 +        }
 +    }
 +}
 +
 +#[derive(Debug)]
 +struct StateData {
 +    /// Span of the top level expression
 +    span: Span,
 +    hir_id: HirId,
 +    position: Position,
 +}
 +
 +#[derive(Debug)]
 +struct DerefedBorrow {
 +    count: usize,
 +    msg: &'static str,
 +    snip_expr: Option<HirId>,
 +}
 +
 +#[derive(Debug)]
 +enum State {
 +    // Any number of deref method calls.
 +    DerefMethod {
 +        // The number of calls in a sequence which changed the referenced type
 +        ty_changed_count: usize,
 +        is_final_ufcs: bool,
 +        /// The required mutability
 +        target_mut: Mutability,
 +    },
 +    DerefedBorrow(DerefedBorrow),
 +    ExplicitDeref {
 +        mutability: Option<Mutability>,
 +    },
 +    ExplicitDerefField {
 +        name: Symbol,
 +    },
 +    Reborrow {
 +        mutability: Mutability,
 +    },
 +    Borrow {
 +        mutability: Mutability,
 +    },
 +}
 +
 +// A reference operation considered by this lint pass
 +enum RefOp {
 +    Method(Mutability),
 +    Deref,
 +    AddrOf(Mutability),
 +}
 +
 +struct RefPat {
 +    /// Whether every usage of the binding is dereferenced.
 +    always_deref: bool,
 +    /// The spans of all the ref bindings for this local.
 +    spans: Vec<Span>,
 +    /// The applicability of this suggestion.
 +    app: Applicability,
 +    /// All the replacements which need to be made.
 +    replacements: Vec<(Span, String)>,
 +    /// The [`HirId`] that the lint should be emitted at.
 +    hir_id: HirId,
 +}
 +
-                 let (position, adjustments) = walk_parents(cx, expr, self.msrv);
++impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
 +    #[expect(clippy::too_many_lines)]
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        // Skip path expressions from deref calls. e.g. `Deref::deref(e)`
 +        if Some(expr.hir_id) == self.skip_expr.take() {
 +            return;
 +        }
 +
 +        if let Some(local) = path_to_local(expr) {
 +            self.check_local_usage(cx, expr, local);
 +        }
 +
 +        // Stop processing sub expressions when a macro call is seen
 +        if expr.span.from_expansion() {
 +            if let Some((state, data)) = self.state.take() {
 +                report(cx, expr, state, data);
 +            }
 +            return;
 +        }
 +
 +        let typeck = cx.typeck_results();
 +        let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) {
 +            x
 +        } else {
 +            // The whole chain of reference operations has been seen
 +            if let Some((state, data)) = self.state.take() {
 +                report(cx, expr, state, data);
 +            }
 +            return;
 +        };
 +
 +        match (self.state.take(), kind) {
 +            (None, kind) => {
 +                let expr_ty = typeck.expr_ty(expr);
-                                     needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
++                let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, self.msrv);
 +                match kind {
 +                    RefOp::Deref => {
 +                        if let Position::FieldAccess {
 +                            name,
 +                            of_union: false,
 +                        } = position
 +                            && !ty_contains_field(typeck.expr_ty(sub_expr), name)
 +                        {
 +                            self.state = Some((
 +                                State::ExplicitDerefField { name },
 +                                StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                            ));
 +                        } else if position.is_deref_stable() {
 +                            self.state = Some((
 +                                State::ExplicitDeref { mutability: None },
 +                                StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                            ));
 +                        }
 +                    }
 +                    RefOp::Method(target_mut)
 +                        if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
 +                            && position.lint_explicit_deref() =>
 +                    {
 +                        let ty_changed_count = usize::from(!deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)));
 +                        self.state = Some((
 +                            State::DerefMethod {
 +                                ty_changed_count,
 +                                is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
 +                                target_mut,
 +                            },
 +                            StateData {
 +                                span: expr.span,
 +                                hir_id: expr.hir_id,
 +                                position
 +                            },
 +                        ));
 +                    },
 +                    RefOp::AddrOf(mutability) => {
 +                        // Find the number of times the borrow is auto-derefed.
 +                        let mut iter = adjustments.iter();
 +                        let mut deref_count = 0usize;
 +                        let next_adjust = loop {
 +                            match iter.next() {
 +                                Some(adjust) => {
 +                                    if !matches!(adjust.kind, Adjust::Deref(_)) {
 +                                        break Some(adjust);
 +                                    } else if !adjust.target.is_ref() {
 +                                        deref_count += 1;
 +                                        break iter.next();
 +                                    }
 +                                    deref_count += 1;
 +                                },
 +                                None => break None,
 +                            };
 +                        };
 +
 +                        // Determine the required number of references before any can be removed. In all cases the
 +                        // reference made by the current expression will be removed. After that there are four cases to
 +                        // handle.
 +                        //
 +                        // 1. Auto-borrow will trigger in the current position, so no further references are required.
 +                        // 2. Auto-deref ends at a reference, or the underlying type, so one extra needs to be left to
 +                        //    handle the automatically inserted re-borrow.
 +                        // 3. Auto-deref hits a user-defined `Deref` impl, so at least one reference needs to exist to
 +                        //    start auto-deref.
 +                        // 4. If the chain of non-user-defined derefs ends with a mutable re-borrow, and re-borrow
 +                        //    adjustments will not be inserted automatically, then leave one further reference to avoid
 +                        //    moving a mutable borrow.
 +                        //    e.g.
 +                        //        fn foo<T>(x: &mut Option<&mut T>, y: &mut T) {
 +                        //            let x = match x {
 +                        //                // Removing the borrow will cause `x` to be moved
 +                        //                Some(x) => &mut *x,
 +                        //                None => y
 +                        //            };
 +                        //        }
 +                        let deref_msg =
 +                            "this expression creates a reference which is immediately dereferenced by the compiler";
 +                        let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
 +                        let impl_msg = "the borrowed expression implements the required traits";
 +
 +                        let (required_refs, msg, snip_expr) = if position.can_auto_borrow() {
 +                            (1, if deref_count == 1 { borrow_msg } else { deref_msg }, None)
 +                        } else if let Position::ImplArg(hir_id) = position {
 +                            (0, impl_msg, Some(hir_id))
 +                        } else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
 +                            next_adjust.map(|a| &a.kind)
 +                        {
 +                            if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
 +                            {
 +                                (3, deref_msg, None)
 +                            } else {
 +                                (2, deref_msg, None)
 +                            }
 +                        } else {
 +                            (2, deref_msg, None)
 +                        };
 +
 +                        if deref_count >= required_refs {
 +                            self.state = Some((
 +                                State::DerefedBorrow(DerefedBorrow {
 +                                    // One of the required refs is for the current borrow expression, the remaining ones
 +                                    // can't be removed without breaking the code. See earlier comment.
 +                                    count: deref_count - required_refs,
 +                                    msg,
 +                                    snip_expr,
 +                                }),
 +                                StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                            ));
 +                        } else if position.is_deref_stable()
 +                            // Auto-deref doesn't combine with other adjustments
 +                            && next_adjust.map_or(true, |a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
 +                            && iter.all(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
 +                        {
 +                            self.state = Some((
 +                                State::Borrow { mutability },
 +                                StateData {
 +                                    span: expr.span,
 +                                    hir_id: expr.hir_id,
 +                                    position
 +                                },
 +                            ));
 +                        }
 +                    },
 +                    RefOp::Method(..) => (),
 +                }
 +            },
 +            (
 +                Some((
 +                    State::DerefMethod {
 +                        target_mut,
 +                        ty_changed_count,
 +                        ..
 +                    },
 +                    data,
 +                )),
 +                RefOp::Method(_),
 +            ) => {
 +                self.state = Some((
 +                    State::DerefMethod {
 +                        ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) {
 +                            ty_changed_count
 +                        } else {
 +                            ty_changed_count + 1
 +                        },
 +                        is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
 +                        target_mut,
 +                    },
 +                    data,
 +                ));
 +            },
 +            (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(_)) if state.count != 0 => {
 +                self.state = Some((
 +                    State::DerefedBorrow(DerefedBorrow {
 +                        count: state.count - 1,
 +                        ..state
 +                    }),
 +                    data,
 +                ));
 +            },
 +            (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(mutability)) => {
 +                let position = data.position;
 +                report(cx, expr, State::DerefedBorrow(state), data);
 +                if position.is_deref_stable() {
 +                    self.state = Some((
 +                        State::Borrow { mutability },
 +                        StateData {
 +                            span: expr.span,
 +                            hir_id: expr.hir_id,
 +                            position,
 +                        },
 +                    ));
 +                }
 +            },
 +            (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => {
 +                let position = data.position;
 +                report(cx, expr, State::DerefedBorrow(state), data);
 +                if let Position::FieldAccess{name, ..} = position
 +                    && !ty_contains_field(typeck.expr_ty(sub_expr), name)
 +                {
 +                    self.state = Some((
 +                        State::ExplicitDerefField { name },
 +                        StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                    ));
 +                } else if position.is_deref_stable() {
 +                    self.state = Some((
 +                        State::ExplicitDeref { mutability: None },
 +                        StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                    ));
 +                }
 +            },
 +
 +            (Some((State::Borrow { mutability }, data)), RefOp::Deref) => {
 +                if typeck.expr_ty(sub_expr).is_ref() {
 +                    self.state = Some((State::Reborrow { mutability }, data));
 +                } else {
 +                    self.state = Some((
 +                        State::ExplicitDeref {
 +                            mutability: Some(mutability),
 +                        },
 +                        data,
 +                    ));
 +                }
 +            },
 +            (Some((State::Reborrow { mutability }, data)), RefOp::Deref) => {
 +                self.state = Some((
 +                    State::ExplicitDeref {
 +                        mutability: Some(mutability),
 +                    },
 +                    data,
 +                ));
 +            },
 +            (state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => {
 +                self.state = state;
 +            },
 +            (Some((State::ExplicitDerefField { name }, data)), RefOp::Deref)
 +                if !ty_contains_field(typeck.expr_ty(sub_expr), name) =>
 +            {
 +                self.state = Some((State::ExplicitDerefField { name }, data));
 +            },
 +
 +            (Some((state, data)), _) => report(cx, expr, state, data),
 +        }
 +    }
 +
 +    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
 +        if let PatKind::Binding(BindingAnnotation::REF, id, name, _) = pat.kind {
 +            if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) {
 +                // This binding id has been seen before. Add this pattern to the list of changes.
 +                if let Some(prev_pat) = opt_prev_pat {
 +                    if pat.span.from_expansion() {
 +                        // Doesn't match the context of the previous pattern. Can't lint here.
 +                        *opt_prev_pat = None;
 +                    } else {
 +                        prev_pat.spans.push(pat.span);
 +                        prev_pat.replacements.push((
 +                            pat.span,
 +                            snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
 +                                .0
 +                                .into(),
 +                        ));
 +                    }
 +                }
 +                return;
 +            }
 +
 +            if_chain! {
 +                if !pat.span.from_expansion();
 +                if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
 +                // only lint immutable refs, because borrowed `&mut T` cannot be moved out
 +                if let ty::Ref(_, _, Mutability::Not) = *tam.kind();
 +                then {
 +                    let mut app = Applicability::MachineApplicable;
 +                    let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
 +                    self.current_body = self.current_body.or(cx.enclosing_body);
 +                    self.ref_locals.insert(
 +                        id,
 +                        Some(RefPat {
 +                            always_deref: true,
 +                            spans: vec![pat.span],
 +                            app,
 +                            replacements: vec![(pat.span, snip.into())],
 +                            hir_id: pat.hir_id,
 +                        }),
 +                    );
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
++        if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| {
++            local_def_id == cx.tcx.hir().body_owner_def_id(body.id())
++        }) {
++            self.possible_borrowers.pop();
++        }
++
 +        if Some(body.id()) == self.current_body {
 +            for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
 +                let replacements = pat.replacements;
 +                let app = pat.app;
 +                let lint = if pat.always_deref {
 +                    NEEDLESS_BORROW
 +                } else {
 +                    REF_BINDING_TO_REFERENCE
 +                };
 +                span_lint_hir_and_then(
 +                    cx,
 +                    lint,
 +                    pat.hir_id,
 +                    pat.spans,
 +                    "this pattern creates a reference to a reference",
 +                    |diag| {
 +                        diag.multipart_suggestion("try this", replacements, app);
 +                    },
 +                );
 +            }
 +            self.current_body = None;
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn try_parse_ref_op<'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    typeck: &'tcx TypeckResults<'_>,
 +    expr: &'tcx Expr<'_>,
 +) -> Option<(RefOp, &'tcx Expr<'tcx>)> {
 +    let (def_id, arg) = match expr.kind {
 +        ExprKind::MethodCall(_, arg, [], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg),
 +        ExprKind::Call(
 +            Expr {
 +                kind: ExprKind::Path(path),
 +                hir_id,
 +                ..
 +            },
 +            [arg],
 +        ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg),
 +        ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => {
 +            return Some((RefOp::Deref, sub_expr));
 +        },
 +        ExprKind::AddrOf(BorrowKind::Ref, mutability, sub_expr) => return Some((RefOp::AddrOf(mutability), sub_expr)),
 +        _ => return None,
 +    };
 +    if tcx.is_diagnostic_item(sym::deref_method, def_id) {
 +        Some((RefOp::Method(Mutability::Not), arg))
 +    } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? {
 +        Some((RefOp::Method(Mutability::Mut), arg))
 +    } else {
 +        None
 +    }
 +}
 +
 +// Checks whether the type for a deref call actually changed the type, not just the mutability of
 +// the reference.
 +fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
 +    match (result_ty.kind(), arg_ty.kind()) {
 +        (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => result_ty == arg_ty,
 +
 +        // The result type for a deref method is always a reference
 +        // Not matching the previous pattern means the argument type is not a reference
 +        // This means that the type did change
 +        _ => false,
 +    }
 +}
 +
 +/// The position of an expression relative to it's parent.
 +#[derive(Clone, Copy, Debug)]
 +enum Position {
 +    MethodReceiver,
 +    /// The method is defined on a reference type. e.g. `impl Foo for &T`
 +    MethodReceiverRefImpl,
 +    Callee,
 +    ImplArg(HirId),
 +    FieldAccess {
 +        name: Symbol,
 +        of_union: bool,
 +    }, // union fields cannot be auto borrowed
 +    Postfix,
 +    Deref,
 +    /// Any other location which will trigger auto-deref to a specific time.
 +    /// Contains the precedence of the parent expression and whether the target type is sized.
 +    DerefStable(i8, bool),
 +    /// Any other location which will trigger auto-reborrowing.
 +    /// Contains the precedence of the parent expression.
 +    ReborrowStable(i8),
 +    /// Contains the precedence of the parent expression.
 +    Other(i8),
 +}
 +impl Position {
 +    fn is_deref_stable(self) -> bool {
 +        matches!(self, Self::DerefStable(..))
 +    }
 +
 +    fn is_reborrow_stable(self) -> bool {
 +        matches!(self, Self::DerefStable(..) | Self::ReborrowStable(_))
 +    }
 +
 +    fn can_auto_borrow(self) -> bool {
 +        matches!(
 +            self,
 +            Self::MethodReceiver | Self::FieldAccess { of_union: false, .. } | Self::Callee
 +        )
 +    }
 +
 +    fn lint_explicit_deref(self) -> bool {
 +        matches!(self, Self::Other(_) | Self::DerefStable(..) | Self::ReborrowStable(_))
 +    }
 +
 +    fn precedence(self) -> i8 {
 +        match self {
 +            Self::MethodReceiver
 +            | Self::MethodReceiverRefImpl
 +            | Self::Callee
 +            | Self::FieldAccess { .. }
 +            | Self::Postfix => PREC_POSTFIX,
 +            Self::ImplArg(_) | Self::Deref => PREC_PREFIX,
 +            Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p,
 +        }
 +    }
 +}
 +
 +/// Walks up the parent expressions attempting to determine both how stable the auto-deref result
 +/// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
 +/// locations as those follow different rules.
 +#[expect(clippy::too_many_lines)]
 +fn walk_parents<'tcx>(
 +    cx: &LateContext<'tcx>,
++    possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
 +    e: &'tcx Expr<'_>,
 +    msrv: Option<RustcVersion>,
 +) -> (Position, &'tcx [Adjustment<'tcx>]) {
 +    let mut adjustments = [].as_slice();
 +    let mut precedence = 0i8;
 +    let ctxt = e.span.ctxt();
 +    let position = walk_to_expr_usage(cx, e, &mut |parent, child_id| {
 +        // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead.
 +        if adjustments.is_empty() && let Node::Expr(e) = cx.tcx.hir().get(child_id) {
 +            adjustments = cx.typeck_results().expr_adjustments(e);
 +        }
 +        match parent {
 +            Node::Local(Local { ty: Some(ty), span, .. }) if span.ctxt() == ctxt => {
 +                Some(binding_ty_auto_deref_stability(cx, ty, precedence, List::empty()))
 +            },
 +            Node::Item(&Item {
 +                kind: ItemKind::Static(..) | ItemKind::Const(..),
 +                def_id,
 +                span,
 +                ..
 +            })
 +            | Node::TraitItem(&TraitItem {
 +                kind: TraitItemKind::Const(..),
 +                def_id,
 +                span,
 +                ..
 +            })
 +            | Node::ImplItem(&ImplItem {
 +                kind: ImplItemKind::Const(..),
 +                def_id,
 +                span,
 +                ..
 +            }) if span.ctxt() == ctxt => {
 +                let ty = cx.tcx.type_of(def_id.def_id);
 +                Some(ty_auto_deref_stability(cx, ty, precedence).position_for_result(cx))
 +            },
 +
 +            Node::Item(&Item {
 +                kind: ItemKind::Fn(..),
 +                def_id,
 +                span,
 +                ..
 +            })
 +            | Node::TraitItem(&TraitItem {
 +                kind: TraitItemKind::Fn(..),
 +                def_id,
 +                span,
 +                ..
 +            })
 +            | Node::ImplItem(&ImplItem {
 +                kind: ImplItemKind::Fn(..),
 +                def_id,
 +                span,
 +                ..
 +            }) if span.ctxt() == ctxt => {
 +                let output = cx
 +                    .tcx
 +                    .erase_late_bound_regions(cx.tcx.fn_sig(def_id.to_def_id()).output());
 +                Some(ty_auto_deref_stability(cx, output, precedence).position_for_result(cx))
 +            },
 +
 +            Node::ExprField(field) if field.span.ctxt() == ctxt => match get_parent_expr_for_hir(cx, field.hir_id) {
 +                Some(Expr {
 +                    hir_id,
 +                    kind: ExprKind::Struct(path, ..),
 +                    ..
 +                }) => variant_of_res(cx, cx.qpath_res(path, *hir_id))
 +                    .and_then(|variant| variant.fields.iter().find(|f| f.name == field.ident.name))
 +                    .map(|field_def| {
 +                        ty_auto_deref_stability(cx, cx.tcx.type_of(field_def.did), precedence).position_for_arg()
 +                    }),
 +                _ => None,
 +            },
 +
 +            Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
 +                ExprKind::Ret(_) => {
 +                    let owner_id = cx.tcx.hir().body_owner(cx.enclosing_body.unwrap());
 +                    Some(
 +                        if let Node::Expr(
 +                            closure_expr @ Expr {
 +                                kind: ExprKind::Closure(closure),
 +                                ..
 +                            },
 +                        ) = cx.tcx.hir().get(owner_id)
 +                        {
 +                            closure_result_position(cx, closure, cx.typeck_results().expr_ty(closure_expr), precedence)
 +                        } else {
 +                            let output = cx
 +                                .tcx
 +                                .erase_late_bound_regions(cx.tcx.fn_sig(cx.tcx.hir().local_def_id(owner_id)).output());
 +                            ty_auto_deref_stability(cx, output, precedence).position_for_result(cx)
 +                        },
 +                    )
 +                },
 +                ExprKind::Closure(closure) => Some(closure_result_position(
 +                    cx,
 +                    closure,
 +                    cx.typeck_results().expr_ty(parent),
 +                    precedence,
 +                )),
 +                ExprKind::Call(func, _) if func.hir_id == child_id => {
 +                    (child_id == e.hir_id).then_some(Position::Callee)
 +                },
 +                ExprKind::Call(func, args) => args
 +                    .iter()
 +                    .position(|arg| arg.hir_id == child_id)
 +                    .zip(expr_sig(cx, func))
 +                    .and_then(|(i, sig)| {
 +                        sig.input_with_hir(i).map(|(hir_ty, ty)| match hir_ty {
 +                            // Type inference for closures can depend on how they're called. Only go by the explicit
 +                            // types here.
 +                            Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
 +                            None => {
 +                                if let ty::Param(param_ty) = ty.skip_binder().kind() {
-                             needless_borrow_impl_arg_position(cx, parent, i + 1, *param_ty, e, precedence, msrv)
++                                    needless_borrow_impl_arg_position(
++                                        cx,
++                                        possible_borrowers,
++                                        parent,
++                                        i,
++                                        *param_ty,
++                                        e,
++                                        precedence,
++                                        msrv,
++                                    )
 +                                } else {
 +                                    ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
 +                                        .position_for_arg()
 +                                }
 +                            },
 +                        })
 +                    }),
 +                ExprKind::MethodCall(_, receiver, args, _) => {
 +                    let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
 +                    if receiver.hir_id == child_id {
 +                        // Check for calls to trait methods where the trait is implemented on a reference.
 +                        // Two cases need to be handled:
 +                        // * `self` methods on `&T` will never have auto-borrow
 +                        // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
 +                        //   priority.
 +                        if e.hir_id != child_id {
 +                            return Some(Position::ReborrowStable(precedence))
 +                        } else if let Some(trait_id) = cx.tcx.trait_of_item(id)
 +                            && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
 +                            && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
 +                            && let subs = match cx
 +                                .typeck_results()
 +                                .node_substs_opt(parent.hir_id)
 +                                .and_then(|subs| subs.get(1..))
 +                            {
 +                                Some(subs) => cx.tcx.mk_substs(subs.iter().copied()),
 +                                None => cx.tcx.mk_substs(std::iter::empty::<ty::subst::GenericArg<'_>>()),
 +                            } && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
 +                                // Trait methods taking `&self`
 +                                sub_ty
 +                            } else {
 +                                // Trait methods taking `self`
 +                                arg_ty
 +                            } && impl_ty.is_ref()
 +                            && let infcx = cx.tcx.infer_ctxt().build()
 +                            && infcx
 +                                .type_implements_trait(trait_id, impl_ty, subs, cx.param_env)
 +                                .must_apply_modulo_regions()
 +                        {
 +                            return Some(Position::MethodReceiverRefImpl)
 +                        }
 +                        return Some(Position::MethodReceiver);
 +                    }
 +                    args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
 +                        let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1];
 +                        if let ty::Param(param_ty) = ty.kind() {
-     let mut check_referent = |referent| {
++                            needless_borrow_impl_arg_position(
++                                cx,
++                                possible_borrowers,
++                                parent,
++                                i + 1,
++                                *param_ty,
++                                e,
++                                precedence,
++                                msrv,
++                            )
 +                        } else {
 +                            ty_auto_deref_stability(
 +                                cx,
 +                                cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i + 1)),
 +                                precedence,
 +                            )
 +                            .position_for_arg()
 +                        }
 +                    })
 +                },
 +                ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess {
 +                    name: name.name,
 +                    of_union: is_union(cx.typeck_results(), child),
 +                }),
 +                ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),
 +                ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
 +                | ExprKind::Index(child, _)
 +                    if child.hir_id == e.hir_id =>
 +                {
 +                    Some(Position::Postfix)
 +                },
 +                _ if child_id == e.hir_id => {
 +                    precedence = parent.precedence().order();
 +                    None
 +                },
 +                _ => None,
 +            },
 +            _ => None,
 +        }
 +    })
 +    .unwrap_or(Position::Other(precedence));
 +    (position, adjustments)
 +}
 +
 +fn is_union<'tcx>(typeck: &'tcx TypeckResults<'_>, path_expr: &'tcx Expr<'_>) -> bool {
 +    typeck
 +        .expr_ty_adjusted(path_expr)
 +        .ty_adt_def()
 +        .map_or(false, rustc_middle::ty::AdtDef::is_union)
 +}
 +
 +fn closure_result_position<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    closure: &'tcx Closure<'_>,
 +    ty: Ty<'tcx>,
 +    precedence: i8,
 +) -> Position {
 +    match closure.fn_decl.output {
 +        FnRetTy::Return(hir_ty) => {
 +            if let Some(sig) = ty_sig(cx, ty)
 +                && let Some(output) = sig.output()
 +            {
 +                binding_ty_auto_deref_stability(cx, hir_ty, precedence, output.bound_vars())
 +            } else {
 +                Position::Other(precedence)
 +            }
 +        },
 +        FnRetTy::DefaultReturn(_) => Position::Other(precedence),
 +    }
 +}
 +
 +// Checks the stability of auto-deref when assigned to a binding with the given explicit type.
 +//
 +// e.g.
 +// let x = Box::new(Box::new(0u32));
 +// let y1: &Box<_> = x.deref();
 +// let y2: &Box<_> = &x;
 +//
 +// Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when
 +// switching to auto-dereferencing.
 +fn binding_ty_auto_deref_stability<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: &'tcx hir::Ty<'_>,
 +    precedence: i8,
 +    binder_args: &'tcx List<BoundVariableKind>,
 +) -> Position {
 +    let TyKind::Rptr(_, ty) = &ty.kind else {
 +        return Position::Other(precedence);
 +    };
 +    let mut ty = ty;
 +
 +    loop {
 +        break match ty.ty.kind {
 +            TyKind::Rptr(_, ref ref_ty) => {
 +                ty = ref_ty;
 +                continue;
 +            },
 +            TyKind::Path(
 +                QPath::TypeRelative(_, path)
 +                | QPath::Resolved(
 +                    _,
 +                    Path {
 +                        segments: [.., path], ..
 +                    },
 +                ),
 +            ) => {
 +                if let Some(args) = path.args
 +                    && args.args.iter().any(|arg| match arg {
 +                        GenericArg::Infer(_) => true,
 +                        GenericArg::Type(ty) => ty_contains_infer(ty),
 +                        _ => false,
 +                    })
 +                {
 +                    Position::ReborrowStable(precedence)
 +                } else {
 +                    Position::DerefStable(
 +                        precedence,
 +                        cx.tcx
 +                            .erase_late_bound_regions(Binder::bind_with_vars(
 +                                cx.typeck_results().node_type(ty.ty.hir_id),
 +                                binder_args,
 +                            ))
 +                            .is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
 +                    )
 +                }
 +            },
 +            TyKind::Slice(_) => Position::DerefStable(precedence, false),
 +            TyKind::Array(..) | TyKind::Ptr(_) | TyKind::BareFn(_) => Position::DerefStable(precedence, true),
 +            TyKind::Never
 +            | TyKind::Tup(_)
 +            | TyKind::Path(_) => Position::DerefStable(
 +                precedence,
 +                cx.tcx
 +                    .erase_late_bound_regions(Binder::bind_with_vars(
 +                        cx.typeck_results().node_type(ty.ty.hir_id),
 +                        binder_args,
 +                    ))
 +                    .is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
 +            ),
 +            TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(..) | TyKind::TraitObject(..) | TyKind::Err => {
 +                Position::ReborrowStable(precedence)
 +            },
 +        };
 +    }
 +}
 +
 +// Checks whether a type is inferred at some point.
 +// e.g. `_`, `Box<_>`, `[_]`
 +fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
 +    struct V(bool);
 +    impl Visitor<'_> for V {
 +        fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
 +            if self.0
 +                || matches!(
 +                    ty.kind,
 +                    TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(_) | TyKind::Err
 +                )
 +            {
 +                self.0 = true;
 +            } else {
 +                walk_ty(self, ty);
 +            }
 +        }
 +
 +        fn visit_generic_arg(&mut self, arg: &GenericArg<'_>) {
 +            if self.0 || matches!(arg, GenericArg::Infer(_)) {
 +                self.0 = true;
 +            } else if let GenericArg::Type(ty) = arg {
 +                self.visit_ty(ty);
 +            }
 +        }
 +    }
 +    let mut v = V(false);
 +    v.visit_ty(ty);
 +    v.0
 +}
 +
 +// Checks whether:
 +// * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
 +// * `e`'s type implements `Trait` and is copyable
 +// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
 +//   The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
 +// be moved, but it cannot be.
++#[expect(clippy::too_many_arguments)]
 +fn needless_borrow_impl_arg_position<'tcx>(
 +    cx: &LateContext<'tcx>,
++    possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
 +    parent: &Expr<'tcx>,
 +    arg_index: usize,
 +    param_ty: ParamTy,
 +    mut expr: &Expr<'tcx>,
 +    precedence: i8,
 +    msrv: Option<RustcVersion>,
 +) -> Position {
 +    let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
 +    let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
 +
 +    let Some(callee_def_id) = fn_def_id(cx, parent) else { return Position::Other(precedence) };
 +    let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
 +    let substs_with_expr_ty = cx
 +        .typeck_results()
 +        .node_substs(if let ExprKind::Call(callee, _) = parent.kind {
 +            callee.hir_id
 +        } else {
 +            parent.hir_id
 +        });
 +
 +    let predicates = cx.tcx.param_env(callee_def_id).caller_bounds();
 +    let projection_predicates = predicates
 +        .iter()
 +        .filter_map(|predicate| {
 +            if let PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
 +                Some(projection_predicate)
 +            } else {
 +                None
 +            }
 +        })
 +        .collect::<Vec<_>>();
 +
 +    let mut trait_with_ref_mut_self_method = false;
 +
 +    // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
 +    if predicates
 +        .iter()
 +        .filter_map(|predicate| {
 +            if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
 +                && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
 +            {
 +                Some(trait_predicate.trait_ref.def_id)
 +            } else {
 +                None
 +            }
 +        })
 +        .inspect(|trait_def_id| {
 +            trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id);
 +        })
 +        .all(|trait_def_id| {
 +            Some(trait_def_id) == destruct_trait_def_id
 +                || Some(trait_def_id) == sized_trait_def_id
 +                || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
 +        })
 +    {
 +        return Position::Other(precedence);
 +    }
 +
 +    // `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
 +    // elements are modified each time `check_referent` is called.
 +    let mut substs_with_referent_ty = substs_with_expr_ty.to_vec();
 +
-         if !is_copy(cx, referent_ty) {
++    let mut check_reference_and_referent = |reference, referent| {
 +        let referent_ty = cx.typeck_results().expr_ty(referent);
 +
-         if !check_referent(referent) {
++        if !is_copy(cx, referent_ty)
++            && (referent_ty.has_significant_drop(cx.tcx, cx.param_env)
++                || !referent_used_exactly_once(cx, possible_borrowers, reference))
++        {
 +            return false;
 +        }
 +
 +        // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
 +        if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
 +            return false;
 +        }
 +
 +        if !replace_types(
 +            cx,
 +            param_ty,
 +            referent_ty,
 +            fn_sig,
 +            arg_index,
 +            &projection_predicates,
 +            &mut substs_with_referent_ty,
 +        ) {
 +            return false;
 +        }
 +
 +        predicates.iter().all(|predicate| {
 +            if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
 +                && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
 +                && let ty::Param(param_ty) = trait_predicate.self_ty().kind()
 +                && let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack()
 +                && ty.is_array()
 +                && !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR)
 +            {
 +                return false;
 +            }
 +
 +            let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty);
 +            let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate);
 +            let infcx = cx.tcx.infer_ctxt().build();
 +            infcx.predicate_must_hold_modulo_regions(&obligation)
 +        })
 +    };
 +
 +    let mut needless_borrow = false;
 +    while let ExprKind::AddrOf(_, _, referent) = expr.kind {
- impl Dereferencing {
-     fn check_local_usage<'tcx>(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) {
++        if !check_reference_and_referent(expr, referent) {
 +            break;
 +        }
 +        expr = referent;
 +        needless_borrow = true;
 +    }
 +
 +    if needless_borrow {
 +        Position::ImplArg(expr.hir_id)
 +    } else {
 +        Position::Other(precedence)
 +    }
 +}
 +
 +fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
 +    cx.tcx
 +        .associated_items(trait_def_id)
 +        .in_definition_order()
 +        .any(|assoc_item| {
 +            if assoc_item.fn_has_self_parameter {
 +                let self_ty = cx.tcx.fn_sig(assoc_item.def_id).skip_binder().inputs()[0];
 +                matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
 +            } else {
 +                false
 +            }
 +        })
 +}
 +
++fn referent_used_exactly_once<'tcx>(
++    cx: &LateContext<'tcx>,
++    possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
++    reference: &Expr<'tcx>,
++) -> bool {
++    let mir = enclosing_mir(cx.tcx, reference.hir_id);
++    if let Some(local) = expr_local(cx.tcx, reference)
++        && let [location] = *local_assignments(mir, local).as_slice()
++        && let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index)
++        && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
++        && !place.has_deref()
++    {
++        let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id);
++        if possible_borrowers
++            .last()
++            .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id)
++        {
++            possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir)));
++        }
++        let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1;
++        // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
++        // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
++        // itself. See the comment in that method for an explanation as to why.
++        possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
++            && used_exactly_once(mir, place.local).unwrap_or(false)
++    } else {
++        false
++    }
++}
++
 +// Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting
 +// projected type that is a type parameter. Returns `false` if replacing the types would have an
 +// effect on the function signature beyond substituting `new_ty` for `param_ty`.
 +// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
 +fn replace_types<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    param_ty: ParamTy,
 +    new_ty: Ty<'tcx>,
 +    fn_sig: FnSig<'tcx>,
 +    arg_index: usize,
 +    projection_predicates: &[ProjectionPredicate<'tcx>],
 +    substs: &mut [ty::GenericArg<'tcx>],
 +) -> bool {
 +    let mut replaced = BitSet::new_empty(substs.len());
 +
 +    let mut deque = VecDeque::with_capacity(substs.len());
 +    deque.push_back((param_ty, new_ty));
 +
 +    while let Some((param_ty, new_ty)) = deque.pop_front() {
 +        // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
 +        if !fn_sig
 +            .inputs_and_output
 +            .iter()
 +            .enumerate()
 +            .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx)))
 +        {
 +            return false;
 +        }
 +
 +        substs[param_ty.index as usize] = ty::GenericArg::from(new_ty);
 +
 +        // The `replaced.insert(...)` check provides some protection against infinite loops.
 +        if replaced.insert(param_ty.index) {
 +            for projection_predicate in projection_predicates {
 +                if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx)
 +                    && let Some(term_ty) = projection_predicate.term.ty()
 +                    && let ty::Param(term_param_ty) = term_ty.kind()
 +                {
 +                    let item_def_id = projection_predicate.projection_ty.item_def_id;
 +                    let assoc_item = cx.tcx.associated_item(item_def_id);
 +                    let projection = cx.tcx
 +                        .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, &[]));
 +
 +                    if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
 +                        && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
 +                    {
 +                        deque.push_back((*term_param_ty, projected_ty));
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    true
 +}
 +
 +struct TyPosition<'tcx> {
 +    position: Position,
 +    ty: Option<Ty<'tcx>>,
 +}
 +impl From<Position> for TyPosition<'_> {
 +    fn from(position: Position) -> Self {
 +        Self { position, ty: None }
 +    }
 +}
 +impl<'tcx> TyPosition<'tcx> {
 +    fn new_deref_stable_for_result(precedence: i8, ty: Ty<'tcx>) -> Self {
 +        Self {
 +            position: Position::ReborrowStable(precedence),
 +            ty: Some(ty),
 +        }
 +    }
 +    fn position_for_result(self, cx: &LateContext<'tcx>) -> Position {
 +        match (self.position, self.ty) {
 +            (Position::ReborrowStable(precedence), Some(ty)) => {
 +                Position::DerefStable(precedence, ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env))
 +            },
 +            (position, _) => position,
 +        }
 +    }
 +    fn position_for_arg(self) -> Position {
 +        self.position
 +    }
 +}
 +
 +// Checks whether a type is stable when switching to auto dereferencing,
 +fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedence: i8) -> TyPosition<'tcx> {
 +    let ty::Ref(_, mut ty, _) = *ty.kind() else {
 +        return Position::Other(precedence).into();
 +    };
 +
 +    loop {
 +        break match *ty.kind() {
 +            ty::Ref(_, ref_ty, _) => {
 +                ty = ref_ty;
 +                continue;
 +            },
 +            ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty),
 +            ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Opaque(..) | ty::Placeholder(_) | ty::Dynamic(..) => {
 +                Position::ReborrowStable(precedence).into()
 +            },
 +            ty::Adt(..) if ty.has_placeholders() || ty.has_opaque_types() => {
 +                Position::ReborrowStable(precedence).into()
 +            },
 +            ty::Adt(_, substs) if substs.has_non_region_param() => {
 +                TyPosition::new_deref_stable_for_result(precedence, ty)
 +            },
 +            ty::Bool
 +            | ty::Char
 +            | ty::Int(_)
 +            | ty::Uint(_)
 +            | ty::Array(..)
 +            | ty::Float(_)
 +            | ty::RawPtr(..)
 +            | ty::FnPtr(_) => Position::DerefStable(precedence, true).into(),
 +            ty::Str | ty::Slice(..) => Position::DerefStable(precedence, false).into(),
 +            ty::Adt(..)
 +            | ty::Foreign(_)
 +            | ty::FnDef(..)
 +            | ty::Generator(..)
 +            | ty::GeneratorWitness(..)
 +            | ty::Closure(..)
 +            | ty::Never
 +            | ty::Tuple(_)
 +            | ty::Projection(_) => Position::DerefStable(
 +                precedence,
 +                ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
 +            )
 +            .into(),
 +        };
 +    }
 +}
 +
 +fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
 +    if let ty::Adt(adt, _) = *ty.kind() {
 +        adt.is_struct() && adt.all_fields().any(|f| f.name == name)
 +    } else {
 +        false
 +    }
 +}
 +
 +#[expect(clippy::needless_pass_by_value, clippy::too_many_lines)]
 +fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
 +    match state {
 +        State::DerefMethod {
 +            ty_changed_count,
 +            is_final_ufcs,
 +            target_mut,
 +        } => {
 +            let mut app = Applicability::MachineApplicable;
 +            let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
 +            let ty = cx.typeck_results().expr_ty(expr);
 +            let (_, ref_count) = peel_mid_ty_refs(ty);
 +            let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
 +                // a deref call changing &T -> &U requires two deref operators the first time
 +                // this occurs. One to remove the reference, a second to call the deref impl.
 +                "*".repeat(ty_changed_count + 1)
 +            } else {
 +                "*".repeat(ty_changed_count)
 +            };
 +            let addr_of_str = if ty_changed_count < ref_count {
 +                // Check if a reborrow from &mut T -> &T is required.
 +                if target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
 +                    "&*"
 +                } else {
 +                    ""
 +                }
 +            } else if target_mut == Mutability::Mut {
 +                "&mut "
 +            } else {
 +                "&"
 +            };
 +
 +            let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
 +                format!("({expr_str})")
 +            } else {
 +                expr_str.into_owned()
 +            };
 +
 +            span_lint_and_sugg(
 +                cx,
 +                EXPLICIT_DEREF_METHODS,
 +                data.span,
 +                match target_mut {
 +                    Mutability::Not => "explicit `deref` method call",
 +                    Mutability::Mut => "explicit `deref_mut` method call",
 +                },
 +                "try this",
 +                format!("{addr_of_str}{deref_str}{expr_str}"),
 +                app,
 +            );
 +        },
 +        State::DerefedBorrow(state) => {
 +            let mut app = Applicability::MachineApplicable;
 +            let snip_expr = state.snip_expr.map_or(expr, |hir_id| cx.tcx.hir().expect_expr(hir_id));
 +            let (snip, snip_is_macro) = snippet_with_context(cx, snip_expr.span, data.span.ctxt(), "..", &mut app);
 +            span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
 +                let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee);
 +                let sugg = if !snip_is_macro
 +                    && !has_enclosing_paren(&snip)
 +                    && (expr.precedence().order() < data.position.precedence() || calls_field)
 +                {
 +                    format!("({snip})")
 +                } else {
 +                    snip.into()
 +                };
 +                diag.span_suggestion(data.span, "change this to", sugg, app);
 +            });
 +        },
 +        State::ExplicitDeref { mutability } => {
 +            if matches!(
 +                expr.kind,
 +                ExprKind::Block(..)
 +                    | ExprKind::ConstBlock(_)
 +                    | ExprKind::If(..)
 +                    | ExprKind::Loop(..)
 +                    | ExprKind::Match(..)
 +            ) && matches!(data.position, Position::DerefStable(_, true))
 +            {
 +                // Rustc bug: auto deref doesn't work on block expression when targeting sized types.
 +                return;
 +            }
 +
 +            let (prefix, precedence) = if let Some(mutability) = mutability
 +                && !cx.typeck_results().expr_ty(expr).is_ref()
 +            {
 +                let prefix = match mutability {
 +                    Mutability::Not => "&",
 +                    Mutability::Mut => "&mut ",
 +                };
 +                (prefix, 0)
 +            } else {
 +                ("", data.position.precedence())
 +            };
 +            span_lint_hir_and_then(
 +                cx,
 +                EXPLICIT_AUTO_DEREF,
 +                data.hir_id,
 +                data.span,
 +                "deref which would be done by auto-deref",
 +                |diag| {
 +                    let mut app = Applicability::MachineApplicable;
 +                    let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
 +                    let sugg =
 +                        if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
 +                            format!("{prefix}({snip})")
 +                        } else {
 +                            format!("{prefix}{snip}")
 +                        };
 +                    diag.span_suggestion(data.span, "try this", sugg, app);
 +                },
 +            );
 +        },
 +        State::ExplicitDerefField { .. } => {
 +            if matches!(
 +                expr.kind,
 +                ExprKind::Block(..)
 +                    | ExprKind::ConstBlock(_)
 +                    | ExprKind::If(..)
 +                    | ExprKind::Loop(..)
 +                    | ExprKind::Match(..)
 +            ) && matches!(data.position, Position::DerefStable(_, true))
 +            {
 +                // Rustc bug: auto deref doesn't work on block expression when targeting sized types.
 +                return;
 +            }
 +
 +            span_lint_hir_and_then(
 +                cx,
 +                EXPLICIT_AUTO_DEREF,
 +                data.hir_id,
 +                data.span,
 +                "deref which would be done by auto-deref",
 +                |diag| {
 +                    let mut app = Applicability::MachineApplicable;
 +                    let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
 +                    diag.span_suggestion(data.span, "try this", snip.into_owned(), app);
 +                },
 +            );
 +        },
 +        State::Borrow { .. } | State::Reborrow { .. } => (),
 +    }
 +}
 +
++impl<'tcx> Dereferencing<'tcx> {
++    fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) {
 +        if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
 +            if let Some(pat) = outer_pat {
 +                // Check for auto-deref
 +                if !matches!(
 +                    cx.typeck_results().expr_adjustments(e),
 +                    [
 +                        Adjustment {
 +                            kind: Adjust::Deref(_),
 +                            ..
 +                        },
 +                        Adjustment {
 +                            kind: Adjust::Deref(_),
 +                            ..
 +                        },
 +                        ..
 +                    ]
 +                ) {
 +                    match get_parent_expr(cx, e) {
 +                        // Field accesses are the same no matter the number of references.
 +                        Some(Expr {
 +                            kind: ExprKind::Field(..),
 +                            ..
 +                        }) => (),
 +                        Some(&Expr {
 +                            span,
 +                            kind: ExprKind::Unary(UnOp::Deref, _),
 +                            ..
 +                        }) if !span.from_expansion() => {
 +                            // Remove explicit deref.
 +                            let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
 +                            pat.replacements.push((span, snip.into()));
 +                        },
 +                        Some(parent) if !parent.span.from_expansion() => {
 +                            // Double reference might be needed at this point.
 +                            if parent.precedence().order() == PREC_POSTFIX {
 +                                // Parentheses would be needed here, don't lint.
 +                                *outer_pat = None;
 +                            } else {
 +                                pat.always_deref = false;
 +                                let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
 +                                pat.replacements.push((e.span, format!("&{snip}")));
 +                            }
 +                        },
 +                        _ if !e.span.from_expansion() => {
 +                            // Double reference might be needed at this point.
 +                            pat.always_deref = false;
 +                            let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
 +                            pat.replacements.push((e.span, format!("&{snip}")));
 +                        },
 +                        // Edge case for macros. The span of the identifier will usually match the context of the
 +                        // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
 +                        // macros
 +                        _ => *outer_pat = None,
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
index 3fac93dcc90c504f2f26dc9df219a1688b4f9ac9,0000000000000000000000000000000000000000..fad984d05ca95fa04f20cd76446688db9322748c
mode 100644,000000..100644
--- /dev/null
@@@ -1,531 -1,0 +1,528 @@@
-     let copy_id = match cx.tcx.lang_items().copy_trait() {
-         Some(id) => id,
-         None => return,
-     };
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::paths;
 +use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
 +use clippy_utils::{is_lint_allowed, match_def_path};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
 +use rustc_hir::{
 +    self as hir, BlockCheckMode, BodyId, Constness, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource,
 +    Unsafety,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::traits::Reveal;
 +use rustc_middle::ty::{
 +    self, Binder, BoundConstness, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef,
 +    Ty, TyCtxt,
 +};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for deriving `Hash` but implementing `PartialEq`
 +    /// explicitly or vice versa.
 +    ///
 +    /// ### Why is this bad?
 +    /// The implementation of these traits must agree (for
 +    /// example for use with `HashMap`) so it’s probably a bad idea to use a
 +    /// default-generated `Hash` implementation with an explicitly defined
 +    /// `PartialEq`. In particular, the following must hold for any type:
 +    ///
 +    /// ```text
 +    /// k1 == k2 ⇒ hash(k1) == hash(k2)
 +    /// ```
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// #[derive(Hash)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialEq for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DERIVE_HASH_XOR_EQ,
 +    correctness,
 +    "deriving `Hash` but implementing `PartialEq` explicitly"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for deriving `Ord` but implementing `PartialOrd`
 +    /// explicitly or vice versa.
 +    ///
 +    /// ### Why is this bad?
 +    /// The implementation of these traits must agree (for
 +    /// example for use with `sort`) so it’s probably a bad idea to use a
 +    /// default-generated `Ord` implementation with an explicitly defined
 +    /// `PartialOrd`. In particular, the following must hold for any type
 +    /// implementing `Ord`:
 +    ///
 +    /// ```text
 +    /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()
 +    /// ```
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// #[derive(Ord, PartialEq, Eq)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialOrd for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// #[derive(PartialEq, Eq)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialOrd for Foo {
 +    ///     fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
 +    ///        Some(self.cmp(other))
 +    ///     }
 +    /// }
 +    ///
 +    /// impl Ord for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    /// or, if you don't need a custom ordering:
 +    /// ```rust,ignore
 +    /// #[derive(Ord, PartialOrd, PartialEq, Eq)]
 +    /// struct Foo;
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub DERIVE_ORD_XOR_PARTIAL_ORD,
 +    correctness,
 +    "deriving `Ord` but implementing `PartialOrd` explicitly"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for explicit `Clone` implementations for `Copy`
 +    /// types.
 +    ///
 +    /// ### Why is this bad?
 +    /// To avoid surprising behavior, these traits should
 +    /// agree and the behavior of `Copy` cannot be overridden. In almost all
 +    /// situations a `Copy` type should have a `Clone` implementation that does
 +    /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
 +    /// gets you.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// #[derive(Copy)]
 +    /// struct Foo;
 +    ///
 +    /// impl Clone for Foo {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXPL_IMPL_CLONE_ON_COPY,
 +    pedantic,
 +    "implementing `Clone` explicitly on `Copy` types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for deriving `serde::Deserialize` on a type that
 +    /// has methods using `unsafe`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Deriving `serde::Deserialize` will create a constructor
 +    /// that may violate invariants hold by another constructor.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// use serde::Deserialize;
 +    ///
 +    /// #[derive(Deserialize)]
 +    /// pub struct Foo {
 +    ///     // ..
 +    /// }
 +    ///
 +    /// impl Foo {
 +    ///     pub fn new() -> Self {
 +    ///         // setup here ..
 +    ///     }
 +    ///
 +    ///     pub unsafe fn parts() -> (&str, &str) {
 +    ///         // assumes invariants hold
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub UNSAFE_DERIVE_DESERIALIZE,
 +    pedantic,
 +    "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for types that derive `PartialEq` and could implement `Eq`.
 +    ///
 +    /// ### Why is this bad?
 +    /// If a type `T` derives `PartialEq` and all of its members implement `Eq`,
 +    /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used
 +    /// in APIs that require `Eq` types. It also allows structs containing `T` to derive
 +    /// `Eq` themselves.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #[derive(PartialEq)]
 +    /// struct Foo {
 +    ///     i_am_eq: i32,
 +    ///     i_am_eq_too: Vec<String>,
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// #[derive(PartialEq, Eq)]
 +    /// struct Foo {
 +    ///     i_am_eq: i32,
 +    ///     i_am_eq_too: Vec<String>,
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub DERIVE_PARTIAL_EQ_WITHOUT_EQ,
 +    nursery,
 +    "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
 +}
 +
 +declare_lint_pass!(Derive => [
 +    EXPL_IMPL_CLONE_ON_COPY,
 +    DERIVE_HASH_XOR_EQ,
 +    DERIVE_ORD_XOR_PARTIAL_ORD,
 +    UNSAFE_DERIVE_DESERIALIZE,
 +    DERIVE_PARTIAL_EQ_WITHOUT_EQ
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Derive {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if let ItemKind::Impl(Impl {
 +            of_trait: Some(ref trait_ref),
 +            ..
 +        }) = item.kind
 +        {
 +            let ty = cx.tcx.type_of(item.def_id);
 +            let is_automatically_derived = cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived);
 +
 +            check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
 +            check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
 +
 +            if is_automatically_derived {
 +                check_unsafe_derive_deserialize(cx, item, trait_ref, ty);
 +                check_partial_eq_without_eq(cx, item.span, trait_ref, ty);
 +            } else {
 +                check_copy_clone(cx, item, trait_ref, ty);
 +            }
 +        }
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_HASH_XOR_EQ` lint.
 +fn check_hash_peq<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
 +    trait_ref: &hir::TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +    hash_is_automatically_derived: bool,
 +) {
 +    if_chain! {
 +        if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait();
 +        if let Some(def_id) = trait_ref.trait_def_id();
 +        if cx.tcx.is_diagnostic_item(sym::Hash, def_id);
 +        then {
 +            // Look for the PartialEq implementations for `ty`
 +            cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
 +                let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
 +
 +                if peq_is_automatically_derived == hash_is_automatically_derived {
 +                    return;
 +                }
 +
 +                let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
 +
 +                // Only care about `impl PartialEq<Foo> for Foo`
 +                // For `impl PartialEq<B> for A, input_types is [A, B]
 +                if trait_ref.substs.type_at(1) == ty {
 +                    let mess = if peq_is_automatically_derived {
 +                        "you are implementing `Hash` explicitly but have derived `PartialEq`"
 +                    } else {
 +                        "you are deriving `Hash` but have implemented `PartialEq` explicitly"
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        DERIVE_HASH_XOR_EQ,
 +                        span,
 +                        mess,
 +                        |diag| {
 +                            if let Some(local_def_id) = impl_id.as_local() {
 +                                let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +                                diag.span_note(
 +                                    cx.tcx.hir().span(hir_id),
 +                                    "`PartialEq` implemented here"
 +                                );
 +                            }
 +                        }
 +                    );
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
 +fn check_ord_partial_ord<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
 +    trait_ref: &hir::TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +    ord_is_automatically_derived: bool,
 +) {
 +    if_chain! {
 +        if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord);
 +        if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait();
 +        if let Some(def_id) = &trait_ref.trait_def_id();
 +        if *def_id == ord_trait_def_id;
 +        then {
 +            // Look for the PartialOrd implementations for `ty`
 +            cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
 +                let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
 +
 +                if partial_ord_is_automatically_derived == ord_is_automatically_derived {
 +                    return;
 +                }
 +
 +                let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
 +
 +                // Only care about `impl PartialOrd<Foo> for Foo`
 +                // For `impl PartialOrd<B> for A, input_types is [A, B]
 +                if trait_ref.substs.type_at(1) == ty {
 +                    let mess = if partial_ord_is_automatically_derived {
 +                        "you are implementing `Ord` explicitly but have derived `PartialOrd`"
 +                    } else {
 +                        "you are deriving `Ord` but have implemented `PartialOrd` explicitly"
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        DERIVE_ORD_XOR_PARTIAL_ORD,
 +                        span,
 +                        mess,
 +                        |diag| {
 +                            if let Some(local_def_id) = impl_id.as_local() {
 +                                let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +                                diag.span_note(
 +                                    cx.tcx.hir().span(hir_id),
 +                                    "`PartialOrd` implemented here"
 +                                );
 +                            }
 +                        }
 +                    );
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
 +fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
 +    let clone_id = match cx.tcx.lang_items().clone_trait() {
 +        Some(id) if trait_ref.trait_def_id() == Some(id) => id,
 +        _ => return,
 +    };
++    let Some(copy_id) = cx.tcx.lang_items().copy_trait() else { return };
 +    let (ty_adt, ty_subs) = match *ty.kind() {
 +        // Unions can't derive clone.
 +        ty::Adt(adt, subs) if !adt.is_union() => (adt, subs),
 +        _ => return,
 +    };
 +    // If the current self type doesn't implement Copy (due to generic constraints), search to see if
 +    // there's a Copy impl for any instance of the adt.
 +    if !is_copy(cx, ty) {
 +        if ty_subs.non_erasable_generics().next().is_some() {
 +            let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(&copy_id).map_or(false, |impls| {
 +                impls
 +                    .iter()
 +                    .any(|&id| matches!(cx.tcx.type_of(id).kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did()))
 +            });
 +            if !has_copy_impl {
 +                return;
 +            }
 +        } else {
 +            return;
 +        }
 +    }
 +    // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for
 +    // this impl.
 +    if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) {
 +        return;
 +    }
 +
 +    span_lint_and_note(
 +        cx,
 +        EXPL_IMPL_CLONE_ON_COPY,
 +        item.span,
 +        "you are implementing `Clone` explicitly on a `Copy` type",
 +        Some(item.span),
 +        "consider deriving `Clone` or removing `Copy`",
 +    );
 +}
 +
 +/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.
 +fn check_unsafe_derive_deserialize<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    item: &Item<'_>,
 +    trait_ref: &hir::TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +) {
 +    fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
 +        let mut visitor = UnsafeVisitor { cx, has_unsafe: false };
 +        walk_item(&mut visitor, item);
 +        visitor.has_unsafe
 +    }
 +
 +    if_chain! {
 +        if let Some(trait_def_id) = trait_ref.trait_def_id();
 +        if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE);
 +        if let ty::Adt(def, _) = ty.kind();
 +        if let Some(local_def_id) = def.did().as_local();
 +        let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +        if !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
 +        if cx.tcx.inherent_impls(def.did())
 +            .iter()
 +            .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local()))
 +            .any(|imp| has_unsafe(cx, imp));
 +        then {
 +            span_lint_and_help(
 +                cx,
 +                UNSAFE_DERIVE_DESERIALIZE,
 +                item.span,
 +                "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
 +                None,
 +                "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html"
 +            );
 +        }
 +    }
 +}
 +
 +struct UnsafeVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    has_unsafe: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, _: Span, id: HirId) {
 +        if self.has_unsafe {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let Some(header) = kind.header();
 +            if header.unsafety == Unsafety::Unsafe;
 +            then {
 +                self.has_unsafe = true;
 +            }
 +        }
 +
 +        walk_fn(self, kind, decl, body_id, id);
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.has_unsafe {
 +            return;
 +        }
 +
 +        if let ExprKind::Block(block, _) = expr.kind {
 +            if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
 +                self.has_unsafe = true;
 +            }
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
 +fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
 +    if_chain! {
 +        if let ty::Adt(adt, substs) = ty.kind();
 +        if cx.tcx.visibility(adt.did()).is_public();
 +        if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq);
 +        if let Some(def_id) = trait_ref.trait_def_id();
 +        if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id);
 +        let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id);
 +        if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]);
 +        // If all of our fields implement `Eq`, we can implement `Eq` too
 +        if adt
 +            .all_fields()
 +            .map(|f| f.ty(cx.tcx, substs))
 +            .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]));
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                DERIVE_PARTIAL_EQ_WITHOUT_EQ,
 +                span.ctxt().outer_expn_data().call_site,
 +                "you are deriving `PartialEq` and can implement `Eq`",
 +                "consider deriving `Eq` as well",
 +                "PartialEq, Eq".to_string(),
 +                Applicability::MachineApplicable,
 +            )
 +        }
 +    }
 +}
 +
 +/// Creates the `ParamEnv` used for the give type's derived `Eq` impl.
 +fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ParamEnv<'_> {
 +    // Initial map from generic index to param def.
 +    // Vec<(param_def, needs_eq)>
 +    let mut params = tcx
 +        .generics_of(did)
 +        .params
 +        .iter()
 +        .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. })))
 +        .collect::<Vec<_>>();
 +
 +    let ty_predicates = tcx.predicates_of(did).predicates;
 +    for (p, _) in ty_predicates {
 +        if let PredicateKind::Trait(p) = p.kind().skip_binder()
 +            && p.trait_ref.def_id == eq_trait_id
 +            && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
 +            && p.constness == BoundConstness::NotConst
 +        {
 +            // Flag types which already have an `Eq` bound.
 +            params[self_ty.index as usize].1 = false;
 +        }
 +    }
 +
 +    ParamEnv::new(
 +        tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain(
 +            params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
 +                tcx.mk_predicate(Binder::dummy(PredicateKind::Trait(TraitPredicate {
 +                    trait_ref: TraitRef::new(
 +                        eq_trait_id,
 +                        tcx.mk_substs(std::iter::once(tcx.mk_param_from_def(param))),
 +                    ),
 +                    constness: BoundConstness::NotConst,
 +                    polarity: ImplPolarity::Positive,
 +                })))
 +            }),
 +        )),
 +        Reveal::UserFacing,
 +        Constness::NotConst,
 +    )
 +}
index 1a381f92c0314f7c06048682e984665c213f4aeb,0000000000000000000000000000000000000000..6ac85606d9c7cf86827b7f038701fe152887e74b
mode 100644,000000..100644
--- /dev/null
@@@ -1,112 -1,0 +1,111 @@@
-         let def_id = match uncalled_path.or_else(|| fn_def_id(cx, expr)) {
-             Some(def_id) => def_id,
-             None => return,
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::{fn_def_id, get_parent_expr, path_def_id};
 +
 +use rustc_hir::def::{Namespace, Res};
 +use rustc_hir::def_id::DefIdMap;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +use crate::utils::conf;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Denies the configured methods and functions in clippy.toml
 +    ///
 +    /// Note: Even though this lint is warn-by-default, it will only trigger if
 +    /// methods are defined in the clippy.toml file.
 +    ///
 +    /// ### Why is this bad?
 +    /// Some methods are undesirable in certain contexts, and it's beneficial to
 +    /// lint for them as needed.
 +    ///
 +    /// ### Example
 +    /// An example clippy.toml configuration:
 +    /// ```toml
 +    /// # clippy.toml
 +    /// disallowed-methods = [
 +    ///     # Can use a string as the path of the disallowed method.
 +    ///     "std::boxed::Box::new",
 +    ///     # Can also use an inline table with a `path` key.
 +    ///     { path = "std::time::Instant::now" },
 +    ///     # When using an inline table, can add a `reason` for why the method
 +    ///     # is disallowed.
 +    ///     { path = "std::vec::Vec::leak", reason = "no leaking memory" },
 +    /// ]
 +    /// ```
 +    ///
 +    /// ```rust,ignore
 +    /// // Example code where clippy issues a warning
 +    /// let xs = vec![1, 2, 3, 4];
 +    /// xs.leak(); // Vec::leak is disallowed in the config.
 +    /// // The diagnostic contains the message "no leaking memory".
 +    ///
 +    /// let _now = Instant::now(); // Instant::now is disallowed in the config.
 +    ///
 +    /// let _box = Box::new(3); // Box::new is disallowed in the config.
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// // Example code which does not raise clippy warning
 +    /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.
 +    /// xs.push(123); // Vec::push is _not_ disallowed in the config.
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub DISALLOWED_METHODS,
 +    style,
 +    "use of a disallowed method call"
 +}
 +
 +#[derive(Clone, Debug)]
 +pub struct DisallowedMethods {
 +    conf_disallowed: Vec<conf::DisallowedPath>,
 +    disallowed: DefIdMap<usize>,
 +}
 +
 +impl DisallowedMethods {
 +    pub fn new(conf_disallowed: Vec<conf::DisallowedPath>) -> Self {
 +        Self {
 +            conf_disallowed,
 +            disallowed: DefIdMap::default(),
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
 +    fn check_crate(&mut self, cx: &LateContext<'_>) {
 +        for (index, conf) in self.conf_disallowed.iter().enumerate() {
 +            let segs: Vec<_> = conf.path().split("::").collect();
 +            if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::ValueNS)) {
 +                self.disallowed.insert(id, index);
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        let uncalled_path = if let Some(parent) = get_parent_expr(cx, expr)
 +            && let ExprKind::Call(receiver, _) = parent.kind
 +            && receiver.hir_id == expr.hir_id
 +        {
 +            None
 +        } else {
 +            path_def_id(cx, expr)
 +        };
++        let Some(def_id) = uncalled_path.or_else(|| fn_def_id(cx, expr)) else {
++            return
 +        };
 +        let conf = match self.disallowed.get(&def_id) {
 +            Some(&index) => &self.conf_disallowed[index],
 +            None => return,
 +        };
 +        let msg = format!("use of a disallowed method `{}`", conf.path());
 +        span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| {
 +            if let Some(reason) = conf.reason() {
 +                diag.note(&format!("{reason} (from clippy.toml)"));
 +            }
 +        });
 +    }
 +}
index 9c834cf014485e824ee47e27593a91a19381ea49,0000000000000000000000000000000000000000..b44e62435881fdca6e8144f6fe788bacf8c745ee
mode 100644,000000..100644
--- /dev/null
@@@ -1,644 -1,0 +1,640 @@@
-         let (cond_expr, then_expr, else_expr) = match higher::If::hir(expr) {
-             Some(higher::If { cond, then, r#else }) => (cond, then, r#else),
-             _ => return,
 +use clippy_utils::higher;
 +use clippy_utils::{
 +    can_move_expr_to_closure_no_visit,
 +    diagnostics::span_lint_and_sugg,
 +    is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, paths, peel_hir_expr_while,
 +    source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context},
 +    SpanlessEq,
 +};
 +use core::fmt::Write;
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    hir_id::HirIdSet,
 +    intravisit::{walk_expr, Visitor},
 +    Block, Expr, ExprKind, Guard, HirId, Let, Pat, Stmt, StmtKind, UnOp,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{Span, SyntaxContext, DUMMY_SP};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for uses of `contains_key` + `insert` on `HashMap`
 +    /// or `BTreeMap`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `entry` is more efficient.
 +    ///
 +    /// ### Known problems
 +    /// The suggestion may have type inference errors in some cases. e.g.
 +    /// ```rust
 +    /// let mut map = std::collections::HashMap::new();
 +    /// let _ = if !map.contains_key(&0) {
 +    ///     map.insert(0, 0)
 +    /// } else {
 +    ///     None
 +    /// };
 +    /// ```
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashMap;
 +    /// # let mut map = HashMap::new();
 +    /// # let k = 1;
 +    /// # let v = 1;
 +    /// if !map.contains_key(&k) {
 +    ///     map.insert(k, v);
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::collections::HashMap;
 +    /// # let mut map = HashMap::new();
 +    /// # let k = 1;
 +    /// # let v = 1;
 +    /// map.entry(k).or_insert(v);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MAP_ENTRY,
 +    perf,
 +    "use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`"
 +}
 +
 +declare_lint_pass!(HashMapPass => [MAP_ENTRY]);
 +
 +impl<'tcx> LateLintPass<'tcx> for HashMapPass {
 +    #[expect(clippy::too_many_lines)]
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-         let (map_ty, contains_expr) = match try_parse_contains(cx, cond_expr) {
-             Some(x) => x,
-             None => return,
++        let Some(higher::If { cond: cond_expr, then: then_expr, r#else: else_expr }) = higher::If::hir(expr) else {
++            return
 +        };
 +
-         let then_search = match find_insert_calls(cx, &contains_expr, then_expr) {
-             Some(x) => x,
-             None => return,
++        let Some((map_ty, contains_expr)) = try_parse_contains(cx, cond_expr) else {
++            return
 +        };
 +
-             let else_search = match find_insert_calls(cx, &contains_expr, else_expr) {
-                 Some(search) => search,
-                 None => return,
++        let Some(then_search) = find_insert_calls(cx, &contains_expr, then_expr) else {
++            return
 +        };
 +
 +        let mut app = Applicability::MachineApplicable;
 +        let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0;
 +        let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0;
 +        let sugg = if let Some(else_expr) = else_expr {
++            let Some(else_search) = find_insert_calls(cx, &contains_expr, else_expr) else {
++                return;
 +            };
 +
 +            if then_search.edits.is_empty() && else_search.edits.is_empty() {
 +                // No insertions
 +                return;
 +            } else if then_search.edits.is_empty() || else_search.edits.is_empty() {
 +                // if .. { insert } else { .. } or if .. { .. } else { insert }
 +                let ((then_str, entry_kind), else_str) = match (else_search.edits.is_empty(), contains_expr.negated) {
 +                    (true, true) => (
 +                        then_search.snippet_vacant(cx, then_expr.span, &mut app),
 +                        snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app),
 +                    ),
 +                    (true, false) => (
 +                        then_search.snippet_occupied(cx, then_expr.span, &mut app),
 +                        snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app),
 +                    ),
 +                    (false, true) => (
 +                        else_search.snippet_occupied(cx, else_expr.span, &mut app),
 +                        snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
 +                    ),
 +                    (false, false) => (
 +                        else_search.snippet_vacant(cx, else_expr.span, &mut app),
 +                        snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
 +                    ),
 +                };
 +                format!(
 +                    "if let {}::{entry_kind} = {map_str}.entry({key_str}) {then_str} else {else_str}",
 +                    map_ty.entry_path(),
 +                )
 +            } else {
 +                // if .. { insert } else { insert }
 +                let ((then_str, then_entry), (else_str, else_entry)) = if contains_expr.negated {
 +                    (
 +                        then_search.snippet_vacant(cx, then_expr.span, &mut app),
 +                        else_search.snippet_occupied(cx, else_expr.span, &mut app),
 +                    )
 +                } else {
 +                    (
 +                        then_search.snippet_occupied(cx, then_expr.span, &mut app),
 +                        else_search.snippet_vacant(cx, else_expr.span, &mut app),
 +                    )
 +                };
 +                let indent_str = snippet_indent(cx, expr.span);
 +                let indent_str = indent_str.as_deref().unwrap_or("");
 +                format!(
 +                    "match {map_str}.entry({key_str}) {{\n{indent_str}    {entry}::{then_entry} => {}\n\
 +                        {indent_str}    {entry}::{else_entry} => {}\n{indent_str}}}",
 +                    reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())),
 +                    reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())),
 +                    entry = map_ty.entry_path(),
 +                )
 +            }
 +        } else {
 +            if then_search.edits.is_empty() {
 +                // no insertions
 +                return;
 +            }
 +
 +            // if .. { insert }
 +            if !then_search.allow_insert_closure {
 +                let (body_str, entry_kind) = if contains_expr.negated {
 +                    then_search.snippet_vacant(cx, then_expr.span, &mut app)
 +                } else {
 +                    then_search.snippet_occupied(cx, then_expr.span, &mut app)
 +                };
 +                format!(
 +                    "if let {}::{entry_kind} = {map_str}.entry({key_str}) {body_str}",
 +                    map_ty.entry_path(),
 +                )
 +            } else if let Some(insertion) = then_search.as_single_insertion() {
 +                let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0;
 +                if contains_expr.negated {
 +                    if insertion.value.can_have_side_effects() {
 +                        format!("{map_str}.entry({key_str}).or_insert_with(|| {value_str});")
 +                    } else {
 +                        format!("{map_str}.entry({key_str}).or_insert({value_str});")
 +                    }
 +                } else {
 +                    // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
 +                    // This would need to be a different lint.
 +                    return;
 +                }
 +            } else {
 +                let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app);
 +                if contains_expr.negated {
 +                    format!("{map_str}.entry({key_str}).or_insert_with(|| {block_str});")
 +                } else {
 +                    // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
 +                    // This would need to be a different lint.
 +                    return;
 +                }
 +            }
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            MAP_ENTRY,
 +            expr.span,
 +            &format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()),
 +            "try this",
 +            sugg,
 +            app,
 +        );
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +enum MapType {
 +    Hash,
 +    BTree,
 +}
 +impl MapType {
 +    fn name(self) -> &'static str {
 +        match self {
 +            Self::Hash => "HashMap",
 +            Self::BTree => "BTreeMap",
 +        }
 +    }
 +    fn entry_path(self) -> &'static str {
 +        match self {
 +            Self::Hash => "std::collections::hash_map::Entry",
 +            Self::BTree => "std::collections::btree_map::Entry",
 +        }
 +    }
 +}
 +
 +struct ContainsExpr<'tcx> {
 +    negated: bool,
 +    map: &'tcx Expr<'tcx>,
 +    key: &'tcx Expr<'tcx>,
 +    call_ctxt: SyntaxContext,
 +}
 +fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> {
 +    let mut negated = false;
 +    let expr = peel_hir_expr_while(expr, |e| match e.kind {
 +        ExprKind::Unary(UnOp::Not, e) => {
 +            negated = !negated;
 +            Some(e)
 +        },
 +        _ => None,
 +    });
 +    match expr.kind {
 +        ExprKind::MethodCall(
 +            _,
 +            map,
 +            [
 +                Expr {
 +                    kind: ExprKind::AddrOf(_, _, key),
 +                    span: key_span,
 +                    ..
 +                },
 +            ],
 +            _,
 +        ) if key_span.ctxt() == expr.span.ctxt() => {
 +            let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
 +            let expr = ContainsExpr {
 +                negated,
 +                map,
 +                key,
 +                call_ctxt: expr.span.ctxt(),
 +            };
 +            if match_def_path(cx, id, &paths::BTREEMAP_CONTAINS_KEY) {
 +                Some((MapType::BTree, expr))
 +            } else if match_def_path(cx, id, &paths::HASHMAP_CONTAINS_KEY) {
 +                Some((MapType::Hash, expr))
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +struct InsertExpr<'tcx> {
 +    map: &'tcx Expr<'tcx>,
 +    key: &'tcx Expr<'tcx>,
 +    value: &'tcx Expr<'tcx>,
 +}
 +fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
 +    if let ExprKind::MethodCall(_, map, [key, value], _) = expr.kind {
 +        let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
 +        if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) {
 +            Some(InsertExpr { map, key, value })
 +        } else {
 +            None
 +        }
 +    } else {
 +        None
 +    }
 +}
 +
 +/// An edit that will need to be made to move the expression to use the entry api
 +#[derive(Clone, Copy)]
 +enum Edit<'tcx> {
 +    /// A semicolon that needs to be removed. Used to create a closure for `insert_with`.
 +    RemoveSemi(Span),
 +    /// An insertion into the map.
 +    Insertion(Insertion<'tcx>),
 +}
 +impl<'tcx> Edit<'tcx> {
 +    fn as_insertion(self) -> Option<Insertion<'tcx>> {
 +        if let Self::Insertion(i) = self { Some(i) } else { None }
 +    }
 +}
 +#[derive(Clone, Copy)]
 +struct Insertion<'tcx> {
 +    call: &'tcx Expr<'tcx>,
 +    value: &'tcx Expr<'tcx>,
 +}
 +
 +/// This visitor needs to do a multiple things:
 +/// * Find all usages of the map. An insertion can only be made before any other usages of the map.
 +/// * Determine if there's an insertion using the same key. There's no need for the entry api
 +///   otherwise.
 +/// * Determine if the final statement executed is an insertion. This is needed to use
 +///   `or_insert_with`.
 +/// * Determine if there's any sub-expression that can't be placed in a closure.
 +/// * Determine if there's only a single insert statement. `or_insert` can be used in this case.
 +#[expect(clippy::struct_excessive_bools)]
 +struct InsertSearcher<'cx, 'tcx> {
 +    cx: &'cx LateContext<'tcx>,
 +    /// The map expression used in the contains call.
 +    map: &'tcx Expr<'tcx>,
 +    /// The key expression used in the contains call.
 +    key: &'tcx Expr<'tcx>,
 +    /// The context of the top level block. All insert calls must be in the same context.
 +    ctxt: SyntaxContext,
 +    /// Whether this expression can be safely moved into a closure.
 +    allow_insert_closure: bool,
 +    /// Whether this expression can use the entry api.
 +    can_use_entry: bool,
 +    /// Whether this expression is the final expression in this code path. This may be a statement.
 +    in_tail_pos: bool,
 +    // Is this expression a single insert. A slightly better suggestion can be made in this case.
 +    is_single_insert: bool,
 +    /// If the visitor has seen the map being used.
 +    is_map_used: bool,
 +    /// The locations where changes need to be made for the suggestion.
 +    edits: Vec<Edit<'tcx>>,
 +    /// A stack of loops the visitor is currently in.
 +    loops: Vec<HirId>,
 +    /// Local variables created in the expression. These don't need to be captured.
 +    locals: HirIdSet,
 +}
 +impl<'tcx> InsertSearcher<'_, 'tcx> {
 +    /// Visit the expression as a branch in control flow. Multiple insert calls can be used, but
 +    /// only if they are on separate code paths. This will return whether the map was used in the
 +    /// given expression.
 +    fn visit_cond_arm(&mut self, e: &'tcx Expr<'_>) -> bool {
 +        let is_map_used = self.is_map_used;
 +        let in_tail_pos = self.in_tail_pos;
 +        self.visit_expr(e);
 +        let res = self.is_map_used;
 +        self.is_map_used = is_map_used;
 +        self.in_tail_pos = in_tail_pos;
 +        res
 +    }
 +
 +    /// Visits an expression which is not itself in a tail position, but other sibling expressions
 +    /// may be. e.g. if conditions
 +    fn visit_non_tail_expr(&mut self, e: &'tcx Expr<'_>) {
 +        let in_tail_pos = self.in_tail_pos;
 +        self.in_tail_pos = false;
 +        self.visit_expr(e);
 +        self.in_tail_pos = in_tail_pos;
 +    }
 +}
 +impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
 +    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
 +        match stmt.kind {
 +            StmtKind::Semi(e) => {
 +                self.visit_expr(e);
 +
 +                if self.in_tail_pos && self.allow_insert_closure {
 +                    // The spans are used to slice the top level expression into multiple parts. This requires that
 +                    // they all come from the same part of the source code.
 +                    if stmt.span.ctxt() == self.ctxt && e.span.ctxt() == self.ctxt {
 +                        self.edits
 +                            .push(Edit::RemoveSemi(stmt.span.trim_start(e.span).unwrap_or(DUMMY_SP)));
 +                    } else {
 +                        self.allow_insert_closure = false;
 +                    }
 +                }
 +            },
 +            StmtKind::Expr(e) => self.visit_expr(e),
 +            StmtKind::Local(l) => {
 +                self.visit_pat(l.pat);
 +                if let Some(e) = l.init {
 +                    self.allow_insert_closure &= !self.in_tail_pos;
 +                    self.in_tail_pos = false;
 +                    self.is_single_insert = false;
 +                    self.visit_expr(e);
 +                }
 +            },
 +            StmtKind::Item(_) => {
 +                self.allow_insert_closure &= !self.in_tail_pos;
 +                self.is_single_insert = false;
 +            },
 +        }
 +    }
 +
 +    fn visit_block(&mut self, block: &'tcx Block<'_>) {
 +        // If the block is in a tail position, then the last expression (possibly a statement) is in the
 +        // tail position. The rest, however, are not.
 +        match (block.stmts, block.expr) {
 +            ([], None) => {
 +                self.allow_insert_closure &= !self.in_tail_pos;
 +            },
 +            ([], Some(expr)) => self.visit_expr(expr),
 +            (stmts, Some(expr)) => {
 +                let in_tail_pos = self.in_tail_pos;
 +                self.in_tail_pos = false;
 +                for stmt in stmts {
 +                    self.visit_stmt(stmt);
 +                }
 +                self.in_tail_pos = in_tail_pos;
 +                self.visit_expr(expr);
 +            },
 +            ([stmts @ .., stmt], None) => {
 +                let in_tail_pos = self.in_tail_pos;
 +                self.in_tail_pos = false;
 +                for stmt in stmts {
 +                    self.visit_stmt(stmt);
 +                }
 +                self.in_tail_pos = in_tail_pos;
 +                self.visit_stmt(stmt);
 +            },
 +        }
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if !self.can_use_entry {
 +            return;
 +        }
 +
 +        match try_parse_insert(self.cx, expr) {
 +            Some(insert_expr) if SpanlessEq::new(self.cx).eq_expr(self.map, insert_expr.map) => {
 +                // Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api.
 +                if self.is_map_used
 +                    || !SpanlessEq::new(self.cx).eq_expr(self.key, insert_expr.key)
 +                    || expr.span.ctxt() != self.ctxt
 +                {
 +                    self.can_use_entry = false;
 +                    return;
 +                }
 +
 +                self.edits.push(Edit::Insertion(Insertion {
 +                    call: expr,
 +                    value: insert_expr.value,
 +                }));
 +                self.is_map_used = true;
 +                self.allow_insert_closure &= self.in_tail_pos;
 +
 +                // The value doesn't affect whether there is only a single insert expression.
 +                let is_single_insert = self.is_single_insert;
 +                self.visit_non_tail_expr(insert_expr.value);
 +                self.is_single_insert = is_single_insert;
 +            },
 +            _ if SpanlessEq::new(self.cx).eq_expr(self.map, expr) => {
 +                self.is_map_used = true;
 +            },
 +            _ => match expr.kind {
 +                ExprKind::If(cond_expr, then_expr, Some(else_expr)) => {
 +                    self.is_single_insert = false;
 +                    self.visit_non_tail_expr(cond_expr);
 +                    // Each branch may contain it's own insert expression.
 +                    let mut is_map_used = self.visit_cond_arm(then_expr);
 +                    is_map_used |= self.visit_cond_arm(else_expr);
 +                    self.is_map_used = is_map_used;
 +                },
 +                ExprKind::Match(scrutinee_expr, arms, _) => {
 +                    self.is_single_insert = false;
 +                    self.visit_non_tail_expr(scrutinee_expr);
 +                    // Each branch may contain it's own insert expression.
 +                    let mut is_map_used = self.is_map_used;
 +                    for arm in arms {
 +                        self.visit_pat(arm.pat);
 +                        if let Some(Guard::If(guard) | Guard::IfLet(&Let { init: guard, .. })) = arm.guard {
 +                            self.visit_non_tail_expr(guard);
 +                        }
 +                        is_map_used |= self.visit_cond_arm(arm.body);
 +                    }
 +                    self.is_map_used = is_map_used;
 +                },
 +                ExprKind::Loop(block, ..) => {
 +                    self.loops.push(expr.hir_id);
 +                    self.is_single_insert = false;
 +                    self.allow_insert_closure &= !self.in_tail_pos;
 +                    // Don't allow insertions inside of a loop.
 +                    let edit_len = self.edits.len();
 +                    self.visit_block(block);
 +                    if self.edits.len() != edit_len {
 +                        self.can_use_entry = false;
 +                    }
 +                    self.loops.pop();
 +                },
 +                ExprKind::Block(block, _) => self.visit_block(block),
 +                ExprKind::InlineAsm(_) => {
 +                    self.can_use_entry = false;
 +                },
 +                _ => {
 +                    self.allow_insert_closure &= !self.in_tail_pos;
 +                    self.allow_insert_closure &=
 +                        can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops, &self.locals);
 +                    // Sub expressions are no longer in the tail position.
 +                    self.is_single_insert = false;
 +                    self.in_tail_pos = false;
 +                    walk_expr(self, expr);
 +                },
 +            },
 +        }
 +    }
 +
 +    fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
 +        p.each_binding_or_first(&mut |_, id, _, _| {
 +            self.locals.insert(id);
 +        });
 +    }
 +}
 +
 +struct InsertSearchResults<'tcx> {
 +    edits: Vec<Edit<'tcx>>,
 +    allow_insert_closure: bool,
 +    is_single_insert: bool,
 +}
 +impl<'tcx> InsertSearchResults<'tcx> {
 +    fn as_single_insertion(&self) -> Option<Insertion<'tcx>> {
 +        self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap())
 +    }
 +
 +    fn snippet(
 +        &self,
 +        cx: &LateContext<'_>,
 +        mut span: Span,
 +        app: &mut Applicability,
 +        write_wrapped: impl Fn(&mut String, Insertion<'_>, SyntaxContext, &mut Applicability),
 +    ) -> String {
 +        let ctxt = span.ctxt();
 +        let mut res = String::new();
 +        for insertion in self.edits.iter().filter_map(|e| e.as_insertion()) {
 +            res.push_str(&snippet_with_applicability(
 +                cx,
 +                span.until(insertion.call.span),
 +                "..",
 +                app,
 +            ));
 +            if is_expr_used_or_unified(cx.tcx, insertion.call) {
 +                write_wrapped(&mut res, insertion, ctxt, app);
 +            } else {
 +                let _ = write!(
 +                    res,
 +                    "e.insert({})",
 +                    snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0
 +                );
 +            }
 +            span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP);
 +        }
 +        res.push_str(&snippet_with_applicability(cx, span, "..", app));
 +        res
 +    }
 +
 +    fn snippet_occupied(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) {
 +        (
 +            self.snippet(cx, span, app, |res, insertion, ctxt, app| {
 +                // Insertion into a map would return `Some(&mut value)`, but the entry returns `&mut value`
 +                let _ = write!(
 +                    res,
 +                    "Some(e.insert({}))",
 +                    snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0
 +                );
 +            }),
 +            "Occupied(mut e)",
 +        )
 +    }
 +
 +    fn snippet_vacant(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) {
 +        (
 +            self.snippet(cx, span, app, |res, insertion, ctxt, app| {
 +                // Insertion into a map would return `None`, but the entry returns a mutable reference.
 +                let _ = if is_expr_final_block_expr(cx.tcx, insertion.call) {
 +                    write!(
 +                        res,
 +                        "e.insert({});\n{}None",
 +                        snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0,
 +                        snippet_indent(cx, insertion.call.span).as_deref().unwrap_or(""),
 +                    )
 +                } else {
 +                    write!(
 +                        res,
 +                        "{{ e.insert({}); None }}",
 +                        snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0,
 +                    )
 +                };
 +            }),
 +            "Vacant(e)",
 +        )
 +    }
 +
 +    fn snippet_closure(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String {
 +        let ctxt = span.ctxt();
 +        let mut res = String::new();
 +        for edit in &self.edits {
 +            match *edit {
 +                Edit::Insertion(insertion) => {
 +                    // Cut out the value from `map.insert(key, value)`
 +                    res.push_str(&snippet_with_applicability(
 +                        cx,
 +                        span.until(insertion.call.span),
 +                        "..",
 +                        app,
 +                    ));
 +                    res.push_str(&snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0);
 +                    span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP);
 +                },
 +                Edit::RemoveSemi(semi_span) => {
 +                    // Cut out the semicolon. This allows the value to be returned from the closure.
 +                    res.push_str(&snippet_with_applicability(cx, span.until(semi_span), "..", app));
 +                    span = span.trim_start(semi_span).unwrap_or(DUMMY_SP);
 +                },
 +            }
 +        }
 +        res.push_str(&snippet_with_applicability(cx, span, "..", app));
 +        res
 +    }
 +}
 +
 +fn find_insert_calls<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    contains_expr: &ContainsExpr<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +) -> Option<InsertSearchResults<'tcx>> {
 +    let mut s = InsertSearcher {
 +        cx,
 +        map: contains_expr.map,
 +        key: contains_expr.key,
 +        ctxt: expr.span.ctxt(),
 +        edits: Vec::new(),
 +        is_map_used: false,
 +        allow_insert_closure: true,
 +        can_use_entry: true,
 +        in_tail_pos: true,
 +        is_single_insert: true,
 +        loops: Vec::new(),
 +        locals: HirIdSet::default(),
 +    };
 +    s.visit_expr(expr);
 +    let allow_insert_closure = s.allow_insert_closure;
 +    let is_single_insert = s.is_single_insert;
 +    let edits = s.edits;
 +    s.can_use_entry.then_some(InsertSearchResults {
 +        edits,
 +        allow_insert_closure,
 +        is_single_insert,
 +    })
 +}
index 3732410e71e57a9338ef71b6ab3a9e3f562c3789,0000000000000000000000000000000000000000..7b9786d7e570f91b398f2d7ea9e4fa46cb098fdf
mode 100644,000000..100644
--- /dev/null
@@@ -1,237 -1,0 +1,236 @@@
-     let substs = match closure_ty.kind() {
-         ty::Closure(_, substs) => substs,
-         _ => return false,
 +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::{implements_trait, is_type_diagnostic_item};
 +use clippy_utils::usage::local_used_after_expr;
 +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::{Closure, 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::{self, Ty, TypeVisitable};
 +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
 +    /// xs.map(|x| foo(x))
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// // where `foo(_)` is a plain function that takes the exact argument type of `x`.
 +    /// xs.map(foo)
 +    /// ```
 +    #[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(&Closure { body, .. }) => cx.tcx.hir().body(body),
 +            _ => 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, None, 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 let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait()
 +                            && implements_trait(cx, callee_ty.peel_refs(), fn_mut_id, &[])
 +                            && path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr))
 +                        {
 +                                // 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, receiver, args, _) = body.value.kind;
 +            if check_inputs(cx, body.params, Some(receiver), 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.bound_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<'_>],
 +    receiver: Option<&Expr<'_>>,
 +    call_args: &[Expr<'_>],
 +) -> bool {
 +    if receiver.map_or(params.len() != call_args.len(), |_| params.len() != call_args.len() + 1) {
 +        return false;
 +    }
 +    let binding_modes = cx.typeck_results().pat_binding_modes();
 +    let check_inputs = |param: &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,
 +        }
 +    };
 +    std::iter::zip(params, receiver.into_iter().chain(call_args.iter())).all(|(param, arg)| check_inputs(param, arg))
 +}
 +
 +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 ty::Closure(_, substs) = closure_ty.kind() else {
++        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 {
 +    let assoc_item = cx.tcx.associated_item(method_def_id);
 +    let def_id = assoc_item.container_id(cx.tcx);
 +    match assoc_item.container {
 +        ty::TraitContainer => cx.tcx.def_path_str(def_id),
 +        ty::ImplContainer => {
 +            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 99bef62f81436d48343eee7a921585b0664270de,0000000000000000000000000000000000000000..32073536b45bbd9036c86efc7112f7acaa963b50
mode 100644,000000..100644
--- /dev/null
@@@ -1,318 -1,0 +1,431 @@@
- use clippy_utils::macros::{is_format_macro, FormatArgsExpn, FormatParam, FormatParamUsage};
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred};
- use clippy_utils::ty::implements_trait;
++use clippy_utils::macros::{
++    is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage,
++};
 +use clippy_utils::source::snippet_opt;
-     pedantic,
++use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 +use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs};
 +use if_chain::if_chain;
 +use itertools::Itertools;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, HirId, QPath};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment};
 +use rustc_middle::ty::Ty;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::def_id::DefId;
++use rustc_span::edition::Edition::Edition2021;
 +use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects `format!` within the arguments of another macro that does
 +    /// formatting such as `format!` itself, `write!` or `println!`. Suggests
 +    /// inlining the `format!` call.
 +    ///
 +    /// ### Why is this bad?
 +    /// The recommended code is both shorter and avoids a temporary allocation.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::panic::Location;
 +    /// println!("error: {}", format!("something failed at {}", Location::caller()));
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::panic::Location;
 +    /// println!("error: something failed at {}", Location::caller());
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub FORMAT_IN_FORMAT_ARGS,
 +    perf,
 +    "`format!` used in a macro that does formatting"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for [`ToString::to_string`](https://doc.rust-lang.org/std/string/trait.ToString.html#tymethod.to_string)
 +    /// applied to a type that implements [`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html)
 +    /// in a macro that does formatting.
 +    ///
 +    /// ### Why is this bad?
 +    /// Since the type implements `Display`, the use of `to_string` is
 +    /// unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::panic::Location;
 +    /// println!("error: something failed at {}", Location::caller().to_string());
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::panic::Location;
 +    /// println!("error: something failed at {}", Location::caller());
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub TO_STRING_IN_FORMAT_ARGS,
 +    perf,
 +    "`to_string` applied to a type that implements `Display` in format args"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detect when a variable is not inlined in a format string,
 +    /// and suggests to inline it.
 +    ///
 +    /// ### Why is this bad?
 +    /// Non-inlined code is slightly more difficult to read and understand,
 +    /// as it requires arguments to be matched against the format string.
 +    /// The inlined syntax, where allowed, is simpler.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let var = 42;
 +    /// # let width = 1;
 +    /// # let prec = 2;
 +    /// format!("{}", var);
 +    /// format!("{v:?}", v = var);
 +    /// format!("{0} {0}", var);
 +    /// format!("{0:1$}", var, width);
 +    /// format!("{:.*}", prec, var);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let var = 42;
 +    /// # let width = 1;
 +    /// # let prec = 2;
 +    /// format!("{var}");
 +    /// format!("{var:?}");
 +    /// format!("{var} {var}");
 +    /// format!("{var:width$}");
 +    /// format!("{var:.prec$}");
 +    /// ```
 +    ///
 +    /// ### Known Problems
 +    ///
 +    /// There may be a false positive if the format string is expanded from certain proc macros:
 +    ///
 +    /// ```ignore
 +    /// println!(indoc!("{}"), var);
 +    /// ```
 +    ///
 +    /// If a format string contains a numbered argument that cannot be inlined
 +    /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`.
 +    #[clippy::version = "1.65.0"]
 +    pub UNINLINED_FORMAT_ARGS,
- impl_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, UNINLINED_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]);
++    style,
 +    "using non-inlined variables in `format!` calls"
 +}
 +
-         if_chain! {
-             if let Some(format_args) = FormatArgsExpn::parse(cx, expr);
-             let expr_expn_data = expr.span.ctxt().outer_expn_data();
-             let outermost_expn_data = outermost_expn_data(expr_expn_data);
-             if let Some(macro_def_id) = outermost_expn_data.macro_def_id;
-             if is_format_macro(cx, macro_def_id);
-             if let ExpnKind::Macro(_, name) = outermost_expn_data.kind;
-             then {
-                 for arg in &format_args.args {
-                     if !arg.format.is_default() {
-                         continue;
-                     }
-                     if is_aliased(&format_args, arg.param.value.hir_id) {
-                         continue;
-                     }
-                     check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
-                     check_to_string_in_format_args(cx, name, arg.param.value);
++declare_clippy_lint! {
++    /// ### What it does
++    /// Detects [formatting parameters] that have no effect on the output of
++    /// `format!()`, `println!()` or similar macros.
++    ///
++    /// ### Why is this bad?
++    /// Shorter format specifiers are easier to read, it may also indicate that
++    /// an expected formatting operation such as adding padding isn't happening.
++    ///
++    /// ### Example
++    /// ```rust
++    /// println!("{:.}", 1.0);
++    ///
++    /// println!("not padded: {:5}", format_args!("..."));
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// println!("{}", 1.0);
++    ///
++    /// println!("not padded: {}", format_args!("..."));
++    /// // OR
++    /// println!("padded: {:5}", format!("..."));
++    /// ```
++    ///
++    /// [formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters
++    #[clippy::version = "1.66.0"]
++    pub UNUSED_FORMAT_SPECS,
++    complexity,
++    "use of a format specifier that has no effect"
++}
++
++impl_lint_pass!(FormatArgs => [
++    FORMAT_IN_FORMAT_ARGS,
++    TO_STRING_IN_FORMAT_ARGS,
++    UNINLINED_FORMAT_ARGS,
++    UNUSED_FORMAT_SPECS,
++]);
 +
 +pub struct FormatArgs {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl FormatArgs {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for FormatArgs {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-                 if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) {
-                     check_uninlined_args(cx, &format_args, outermost_expn_data.call_site);
++        if let Some(format_args) = FormatArgsExpn::parse(cx, expr)
++            && let expr_expn_data = expr.span.ctxt().outer_expn_data()
++            && let outermost_expn_data = outermost_expn_data(expr_expn_data)
++            && let Some(macro_def_id) = outermost_expn_data.macro_def_id
++            && is_format_macro(cx, macro_def_id)
++            && let ExpnKind::Macro(_, name) = outermost_expn_data.kind
++        {
++            for arg in &format_args.args {
++                check_unused_format_specifier(cx, arg);
++                if !arg.format.is_default() {
++                    continue;
 +                }
- fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span) {
++                if is_aliased(&format_args, arg.param.value.hir_id) {
++                    continue;
 +                }
++                check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
++                check_to_string_in_format_args(cx, name, arg.param.value);
++            }
++            if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) {
++                check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id);
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
-         if let ExprKind::MethodCall(_, receiver, [], _) = value.kind;
++fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) {
++    let param_ty = cx.typeck_results().expr_ty(arg.param.value).peel_refs();
++
++    if let Count::Implied(Some(mut span)) = arg.format.precision
++        && !span.is_empty()
++    {
++        span_lint_and_then(
++            cx,
++            UNUSED_FORMAT_SPECS,
++            span,
++            "empty precision specifier has no effect",
++            |diag| {
++                if param_ty.is_floating_point() {
++                    diag.note("a precision specifier is not required to format floats");
++                }
++
++                if arg.format.is_default() {
++                    // If there's no other specifiers remove the `:` too
++                    span = arg.format_span();
++                }
++
++                diag.span_suggestion_verbose(span, "remove the `.`", "", Applicability::MachineApplicable);
++            },
++        );
++    }
++
++    if is_type_diagnostic_item(cx, param_ty, sym::Arguments) && !arg.format.is_default_for_trait() {
++        span_lint_and_then(
++            cx,
++            UNUSED_FORMAT_SPECS,
++            arg.span,
++            "format specifiers have no effect on `format_args!()`",
++            |diag| {
++                let mut suggest_format = |spec, span| {
++                    let message = format!("for the {spec} to apply consider using `format!()`");
++
++                    if let Some(mac_call) = root_macro_call(arg.param.value.span)
++                        && cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id)
++                        && arg.span.eq_ctxt(mac_call.span)
++                    {
++                        diag.span_suggestion(
++                            cx.sess().source_map().span_until_char(mac_call.span, '!'),
++                            message,
++                            "format",
++                            Applicability::MaybeIncorrect,
++                        );
++                    } else if let Some(span) = span {
++                        diag.span_help(span, message);
++                    }
++                };
++
++                if !arg.format.width.is_implied() {
++                    suggest_format("width", arg.format.width.span());
++                }
++
++                if !arg.format.precision.is_implied() {
++                    suggest_format("precision", arg.format.precision.span());
++                }
++
++                diag.span_suggestion_verbose(
++                    arg.format_span(),
++                    "if the current behavior is intentional, remove the format specifiers",
++                    "",
++                    Applicability::MaybeIncorrect,
++                );
++            },
++        );
++    }
++}
++
++fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span, def_id: DefId) {
 +    if args.format_string.span.from_expansion() {
 +        return;
 +    }
++    if call_site.edition() < Edition2021 && is_panic(cx, def_id) {
++        // panic! before 2021 edition considers a single string argument as non-format
++        return;
++    }
 +
 +    let mut fixes = Vec::new();
 +    // If any of the arguments are referenced by an index number,
 +    // and that argument is not a simple variable and cannot be inlined,
 +    // we cannot remove any other arguments in the format string,
 +    // because the index numbers might be wrong after inlining.
 +    // Example of an un-inlinable format:  print!("{}{1}", foo, 2)
 +    if !args.params().all(|p| check_one_arg(args, &p, &mut fixes)) || fixes.is_empty() {
 +        return;
 +    }
 +
 +    // Temporarily ignore multiline spans: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308
 +    if fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)) {
 +        return;
 +    }
 +
 +    span_lint_and_then(
 +        cx,
 +        UNINLINED_FORMAT_ARGS,
 +        call_site,
 +        "variables can be used directly in the `format!` string",
 +        |diag| {
 +            diag.multipart_suggestion("change this to", fixes, Applicability::MachineApplicable);
 +        },
 +    );
 +}
 +
 +fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut Vec<(Span, String)>) -> bool {
 +    if matches!(param.kind, Implicit | Starred | Named(_) | Numbered)
 +        && let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind
 +        && let [segment] = path.segments
 +        && let Some(arg_span) = args.value_with_prev_comma_span(param.value.hir_id)
 +    {
 +        let replacement = match param.usage {
 +            FormatParamUsage::Argument => segment.ident.name.to_string(),
 +            FormatParamUsage::Width => format!("{}$", segment.ident.name),
 +            FormatParamUsage::Precision => format!(".{}$", segment.ident.name),
 +        };
 +        fixes.push((param.span, replacement));
 +        fixes.push((arg_span, String::new()));
 +        true  // successful inlining, continue checking
 +    } else {
 +        // if we can't inline a numbered argument, we can't continue
 +        param.kind != Numbered
 +    }
 +}
 +
 +fn outermost_expn_data(expn_data: ExpnData) -> ExpnData {
 +    if expn_data.call_site.from_expansion() {
 +        outermost_expn_data(expn_data.call_site.ctxt().outer_expn_data())
 +    } else {
 +        expn_data
 +    }
 +}
 +
 +fn check_format_in_format_args(
 +    cx: &LateContext<'_>,
 +    call_site: Span,
 +    name: Symbol,
 +    arg: &Expr<'_>,
 +) {
 +    let expn_data = arg.span.ctxt().outer_expn_data();
 +    if expn_data.call_site.from_expansion() {
 +        return;
 +    }
 +    let Some(mac_id) = expn_data.macro_def_id else { return };
 +    if !cx.tcx.is_diagnostic_item(sym::format_macro, mac_id) {
 +        return;
 +    }
 +    span_lint_and_then(
 +        cx,
 +        FORMAT_IN_FORMAT_ARGS,
 +        call_site,
 +        &format!("`format!` in `{name}!` args"),
 +        |diag| {
 +            diag.help(&format!(
 +                "combine the `format!(..)` arguments with the outer `{name}!(..)` call"
 +            ));
 +            diag.help("or consider changing `format!` to `format_args!`");
 +        },
 +    );
 +}
 +
 +fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) {
 +    if_chain! {
 +        if !value.span.from_expansion();
-                     value.span.with_lo(receiver.span.hi()),
++        if let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind;
 +        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id);
 +        if is_diag_trait_item(cx, method_def_id, sym::ToString);
 +        let receiver_ty = cx.typeck_results().expr_ty(receiver);
 +        if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display);
 +        let (n_needed_derefs, target) =
 +            count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter());
 +        if implements_trait(cx, target, display_trait_id, &[]);
 +        if let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait();
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
 +            if n_needed_derefs == 0 && !needs_ref {
 +                span_lint_and_sugg(
 +                    cx,
 +                    TO_STRING_IN_FORMAT_ARGS,
++                    to_string_span.with_lo(receiver.span.hi()),
 +                    &format!(
 +                        "`to_string` applied to a type that implements `Display` in `{name}!` args"
 +                    ),
 +                    "remove this",
 +                    String::new(),
 +                    Applicability::MachineApplicable,
 +                );
 +            } else {
 +                span_lint_and_sugg(
 +                    cx,
 +                    TO_STRING_IN_FORMAT_ARGS,
 +                    value.span,
 +                    &format!(
 +                        "`to_string` applied to a type that implements `Display` in `{name}!` args"
 +                    ),
 +                    "use this",
 +                    format!(
 +                        "{}{:*>n_needed_derefs$}{receiver_snippet}",
 +                        if needs_ref { "&" } else { "" },
 +                        ""
 +                    ),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +/// Returns true if `hir_id` is referred to by multiple format params
 +fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
 +    args.params().filter(|param| param.value.hir_id == hir_id).at_most_one().is_err()
 +}
 +
 +fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
 +where
 +    I: Iterator<Item = &'tcx Adjustment<'tcx>>,
 +{
 +    let mut n_total = 0;
 +    let mut n_needed = 0;
 +    loop {
 +        if let Some(Adjustment { kind: Adjust::Deref(overloaded_deref), target }) = iter.next() {
 +            n_total += 1;
 +            if overloaded_deref.is_some() {
 +                n_needed = n_total;
 +            }
 +            ty = *target;
 +        } else {
 +            return (n_needed, ty);
 +        }
 +    }
 +}
index 5d25c1d06341f5bb57ccdd5e32b485226f61d30e,0000000000000000000000000000000000000000..95eda4ea88275b2a270bf0648ab96168d9c88140
mode 100644,000000..100644
--- /dev/null
@@@ -1,81 -1,0 +1,213 @@@
- use clippy_utils::diagnostics::span_lint_and_help;
- use clippy_utils::{meets_msrv, msrvs};
- use if_chain::if_chain;
- use rustc_hir as hir;
++use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::macros::span_is_local;
++use clippy_utils::source::snippet_opt;
++use clippy_utils::{meets_msrv, msrvs, path_def_id};
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::{walk_path, Visitor};
++use rustc_hir::{
++    GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path, PathSegment, Ty,
++    TyKind,
++};
 +use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::nested_filter::OnlyBodies;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
- use rustc_span::symbol::sym;
++use rustc_span::symbol::{kw, sym};
++use rustc_span::{Span, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct StringWrapper(String);
 +    ///
 +    /// impl Into<StringWrapper> for String {
 +    ///     fn into(self) -> StringWrapper {
 +    ///         StringWrapper(self)
 +    ///     }
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// struct StringWrapper(String);
 +    ///
 +    /// impl From<String> for StringWrapper {
 +    ///     fn from(s: String) -> StringWrapper {
 +    ///         StringWrapper(s)
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub FROM_OVER_INTO,
 +    style,
 +    "Warns on implementations of `Into<..>` to use `From<..>`"
 +}
 +
 +pub struct FromOverInto {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl FromOverInto {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        FromOverInto { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
 +
 +impl<'tcx> LateLintPass<'tcx> for FromOverInto {
-     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
-         if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) {
++    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
++        if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) {
 +            return;
 +        }
 +
-         if_chain! {
-             if let hir::ItemKind::Impl{ .. } = &item.kind;
-             if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.def_id);
-             if cx.tcx.is_diagnostic_item(sym::Into, impl_trait_ref.def_id);
-             then {
-                 span_lint_and_help(
-                     cx,
-                     FROM_OVER_INTO,
-                     cx.tcx.sess.source_map().guess_head_span(item.span),
-                     "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true",
-                     None,
-                     &format!("consider to implement `From<{}>` instead", impl_trait_ref.self_ty()),
-                 );
-             }
++        if let ItemKind::Impl(Impl {
++            of_trait: Some(hir_trait_ref),
++            self_ty,
++            items: [impl_item_ref],
++            ..
++        }) = item.kind
++            && let Some(into_trait_seg) = hir_trait_ref.path.segments.last()
++            // `impl Into<target_ty> for self_ty`
++            && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args
++            && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.def_id)
++            && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id)
++        {
++            span_lint_and_then(
++                cx,
++                FROM_OVER_INTO,
++                cx.tcx.sess.source_map().guess_head_span(item.span),
++                "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true",
++                |diag| {
++                    // If the target type is likely foreign mention the orphan rules as it's a common source of confusion
++                    if path_def_id(cx, target_ty.peel_refs()).map_or(true, |id| !id.is_local()) {
++                        diag.help(
++                            "`impl From<Local> for Foreign` is allowed by the orphan rules, for more information see\n\
++                            https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence"
++                        );
++                    }
++
++                    let message = format!("replace the `Into` implentation with `From<{}>`", middle_trait_ref.self_ty());
++                    if let Some(suggestions) = convert_to_from(cx, into_trait_seg, target_ty, self_ty, impl_item_ref) {
++                        diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable);
++                    } else {
++                        diag.help(message);
++                    }
++                },
++            );
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
++
++/// Finds the occurences of `Self` and `self`
++struct SelfFinder<'a, 'tcx> {
++    cx: &'a LateContext<'tcx>,
++    /// Occurences of `Self`
++    upper: Vec<Span>,
++    /// Occurences of `self`
++    lower: Vec<Span>,
++    /// If any of the `self`/`Self` usages were from an expansion, or the body contained a binding
++    /// already named `val`
++    invalid: bool,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for SelfFinder<'a, 'tcx> {
++    type NestedFilter = OnlyBodies;
++
++    fn nested_visit_map(&mut self) -> Self::Map {
++        self.cx.tcx.hir()
++    }
++
++    fn visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) {
++        for segment in path.segments {
++            match segment.ident.name {
++                kw::SelfLower => self.lower.push(segment.ident.span),
++                kw::SelfUpper => self.upper.push(segment.ident.span),
++                _ => continue,
++            }
++        }
++
++        self.invalid |= path.span.from_expansion();
++        if !self.invalid {
++            walk_path(self, path);
++        }
++    }
++
++    fn visit_name(&mut self, name: Symbol) {
++        if name == sym::val {
++            self.invalid = true;
++        }
++    }
++}
++
++fn convert_to_from(
++    cx: &LateContext<'_>,
++    into_trait_seg: &PathSegment<'_>,
++    target_ty: &Ty<'_>,
++    self_ty: &Ty<'_>,
++    impl_item_ref: &ImplItemRef,
++) -> Option<Vec<(Span, String)>> {
++    let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
++    let ImplItemKind::Fn(ref sig, body_id) = impl_item.kind else { return None };
++    let body = cx.tcx.hir().body(body_id);
++    let [input] = body.params else { return None };
++    let PatKind::Binding(.., self_ident, None) = input.pat.kind else { return None };
++
++    let from = snippet_opt(cx, self_ty.span)?;
++    let into = snippet_opt(cx, target_ty.span)?;
++
++    let mut suggestions = vec![
++        // impl Into<T> for U  ->  impl From<T> for U
++        //      ~~~~                    ~~~~
++        (into_trait_seg.ident.span, String::from("From")),
++        // impl Into<T> for U  ->  impl Into<U> for U
++        //           ~                       ~
++        (target_ty.span, from.clone()),
++        // impl Into<T> for U  ->  impl Into<T> for T
++        //                  ~                       ~
++        (self_ty.span, into),
++        // fn into(self) -> T  ->  fn from(self) -> T
++        //    ~~~~                    ~~~~
++        (impl_item.ident.span, String::from("from")),
++        // fn into([mut] self) -> T  ->  fn into([mut] v: T) -> T
++        //               ~~~~                          ~~~~
++        (self_ident.span, format!("val: {from}")),
++        // fn into(self) -> T  ->  fn into(self) -> Self
++        //                  ~                       ~~~~
++        (sig.decl.output.span(), String::from("Self")),
++    ];
++
++    let mut finder = SelfFinder {
++        cx,
++        upper: Vec::new(),
++        lower: Vec::new(),
++        invalid: false,
++    };
++    finder.visit_expr(body.value);
++
++    if finder.invalid {
++        return None;
++    }
++
++    // don't try to replace e.g. `Self::default()` with `&[T]::default()`
++    if !finder.upper.is_empty() && !matches!(self_ty.kind, TyKind::Path(_)) {
++        return None;
++    }
++
++    for span in finder.upper {
++        suggestions.push((span, from.clone()));
++    }
++    for span in finder.lower {
++        suggestions.push((span, String::from("val")));
++    }
++
++    Some(suggestions)
++}
index d263804f32cf48538be82cad8e979d6543d490f8,0000000000000000000000000000000000000000..3064b6c9d22f808f6593660db86a0f6adf589e2f
mode 100644,000000..100644
--- /dev/null
@@@ -1,267 -1,0 +1,269 @@@
- use rustc_span::{sym, Span};
 +use rustc_ast::ast::Attribute;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::{DefIdSet, LocalDefId};
 +use rustc_hir::{self as hir, def::Res, QPath};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::{
 +    lint::in_external_macro,
 +    ty::{self, Ty},
 +};
- use clippy_utils::{match_def_path, return_ty, trait_ref_of_method};
++use rustc_span::{sym, Span, Symbol};
 +
 +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::visitors::for_each_expr;
- static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]];
++use clippy_utils::{return_ty, trait_ref_of_method};
 +
 +use core::ops::ControlFlow;
 +
 +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 = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use);
 +    if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind {
 +        let is_public = cx.access_levels.is_exported(item.def_id.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.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.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 = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use);
 +        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.def_id).is_none()
 +        {
 +            check_must_use_candidate(
 +                cx,
 +                sig.decl,
 +                cx.tcx.hir().body(*body_id),
 +                item.span,
 +                item.def_id.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.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 = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use);
 +        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.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.def_id).pat_ty(pat), pat.span, tys)
 +    } else {
 +        false
 +    }
 +}
 +
-                 || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did(), path))
++static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::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(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did()))
 +                    && 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,
 +    }
 +}
 +
 +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 {
 +    for_each_expr(body.value, |e| {
 +        use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
 +
 +        match e.kind {
 +            Call(_, args) => {
 +                let mut tys = DefIdSet::default();
 +                for arg in args {
 +                    if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
 +                        && is_mutable_ty(
 +                            cx,
 +                            cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg),
 +                            arg.span,
 +                            &mut tys,
 +                        )
 +                        && is_mutated_static(arg)
 +                    {
 +                        return ControlFlow::Break(());
 +                    }
 +                    tys.clear();
 +                }
 +                ControlFlow::Continue(())
 +            },
 +            MethodCall(_, receiver, args, _) => {
 +                let mut tys = DefIdSet::default();
 +                for arg in std::iter::once(receiver).chain(args.iter()) {
 +                    if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
 +                        && is_mutable_ty(
 +                            cx,
 +                            cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg),
 +                            arg.span,
 +                            &mut tys,
 +                        )
 +                        && is_mutated_static(arg)
 +                    {
 +                        return ControlFlow::Break(());
 +                    }
 +                    tys.clear();
 +                }
 +                ControlFlow::Continue(())
 +            },
 +            Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target)
 +                if is_mutated_static(target) =>
 +            {
 +                ControlFlow::Break(())
 +            },
 +            _ => ControlFlow::Continue(()),
 +        }
 +    })
 +    .is_some()
 +}
index f83f8b40f94b75eff950f62fa8e3f25936a97c42,0000000000000000000000000000000000000000..bd473ac7e51b0e9695f741029dda1bd68380ecdf
mode 100644,000000..100644
--- /dev/null
@@@ -1,84 -1,0 +1,83 @@@
-     let code_snippet = match snippet_opt(cx, body.value.span) {
-         Some(s) => s,
-         _ => return,
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::FnKind;
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_span::Span;
 +
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::source::snippet_opt;
 +
 +use super::TOO_MANY_LINES;
 +
 +pub(super) fn check_fn(
 +    cx: &LateContext<'_>,
 +    kind: FnKind<'_>,
 +    span: Span,
 +    body: &hir::Body<'_>,
 +    too_many_lines_threshold: u64,
 +) {
 +    // Closures must be contained in a parent body, which will be checked for `too_many_lines`.
 +    // Don't check closures for `too_many_lines` to avoid duplicated lints.
 +    if matches!(kind, FnKind::Closure) || in_external_macro(cx.sess(), span) {
 +        return;
 +    }
 +
++    let Some(code_snippet) = snippet_opt(cx, body.value.span) else {
++        return
 +    };
 +    let mut line_count: u64 = 0;
 +    let mut in_comment = false;
 +    let mut code_in_line;
 +
 +    let function_lines = if matches!(body.value.kind, hir::ExprKind::Block(..))
 +        && code_snippet.as_bytes().first().copied() == Some(b'{')
 +        && code_snippet.as_bytes().last().copied() == Some(b'}')
 +    {
 +        // Removing the braces from the enclosing block
 +        &code_snippet[1..code_snippet.len() - 1]
 +    } else {
 +        &code_snippet
 +    }
 +    .trim() // Remove leading and trailing blank lines
 +    .lines();
 +
 +    for mut line in function_lines {
 +        code_in_line = false;
 +        loop {
 +            line = line.trim_start();
 +            if line.is_empty() {
 +                break;
 +            }
 +            if in_comment {
 +                if let Some(i) = line.find("*/") {
 +                    line = &line[i + 2..];
 +                    in_comment = false;
 +                    continue;
 +                }
 +            } else {
 +                let multi_idx = line.find("/*").unwrap_or(line.len());
 +                let single_idx = line.find("//").unwrap_or(line.len());
 +                code_in_line |= multi_idx > 0 && single_idx > 0;
 +                // Implies multi_idx is below line.len()
 +                if multi_idx < single_idx {
 +                    line = &line[multi_idx + 2..];
 +                    in_comment = true;
 +                    continue;
 +                }
 +            }
 +            break;
 +        }
 +        if code_in_line {
 +            line_count += 1;
 +        }
 +    }
 +
 +    if line_count > too_many_lines_threshold {
 +        span_lint(
 +            cx,
 +            TOO_MANY_LINES,
 +            span,
 +            &format!("this function has too many lines ({line_count}/{too_many_lines_threshold})"),
 +        );
 +    }
 +}
index 48edbf6ae576cc66cfe717a11a9eb5de8ec5f57a,0000000000000000000000000000000000000000..29d59c26d92c4a2f697399ff93df5c753bc89152
mode 100644,000000..100644
--- /dev/null
@@@ -1,166 -1,0 +1,166 @@@
-     pedantic,
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::{higher, is_integer_literal, peel_blocks_with_stmt, SpanlessEq};
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for implicit saturating subtraction.
 +    ///
 +    /// ### Why is this bad?
 +    /// Simplicity and readability. Instead we can easily use an builtin function.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let end: u32 = 10;
 +    /// # let start: u32 = 5;
 +    /// let mut i: u32 = end - start;
 +    ///
 +    /// if i != 0 {
 +    ///     i -= 1;
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let end: u32 = 10;
 +    /// # let start: u32 = 5;
 +    /// let mut i: u32 = end - start;
 +    ///
 +    /// i = i.saturating_sub(1);
 +    /// ```
 +    #[clippy::version = "1.44.0"]
 +    pub IMPLICIT_SATURATING_SUB,
++    style,
 +    "Perform saturating subtraction instead of implicitly checking lower bound of data type"
 +}
 +
 +declare_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +        if_chain! {
 +            if let Some(higher::If { cond, then, r#else: None }) = higher::If::hir(expr);
 +
 +            // Check if the conditional expression is a binary operation
 +            if let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind;
 +
 +            // Ensure that the binary operator is >, !=, or <
 +            if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node;
 +
 +            // Check if assign operation is done
 +            if let Some(target) = subtracts_one(cx, then);
 +
 +            // Extracting out the variable name
 +            if let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind;
 +
 +            then {
 +                // Handle symmetric conditions in the if statement
 +                let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) {
 +                    if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node {
 +                        (cond_left, cond_right)
 +                    } else {
 +                        return;
 +                    }
 +                } else if SpanlessEq::new(cx).eq_expr(cond_right, target) {
 +                    if BinOpKind::Lt == cond_op.node || BinOpKind::Ne == cond_op.node {
 +                        (cond_right, cond_left)
 +                    } else {
 +                        return;
 +                    }
 +                } else {
 +                    return;
 +                };
 +
 +                // Check if the variable in the condition statement is an integer
 +                if !cx.typeck_results().expr_ty(cond_var).is_integral() {
 +                    return;
 +                }
 +
 +                // Get the variable name
 +                let var_name = ares_path.segments[0].ident.name.as_str();
 +                match cond_num_val.kind {
 +                    ExprKind::Lit(ref cond_lit) => {
 +                        // Check if the constant is zero
 +                        if let LitKind::Int(0, _) = cond_lit.node {
 +                            if cx.typeck_results().expr_ty(cond_left).is_signed() {
 +                            } else {
 +                                print_lint_and_sugg(cx, var_name, expr);
 +                            };
 +                        }
 +                    },
 +                    ExprKind::Path(QPath::TypeRelative(_, name)) => {
 +                        if_chain! {
 +                            if name.ident.as_str() == "MIN";
 +                            if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id);
 +                            if let Some(impl_id) = cx.tcx.impl_of_method(const_id);
 +                            if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl
 +                            if cx.tcx.type_of(impl_id).is_integral();
 +                            then {
 +                                print_lint_and_sugg(cx, var_name, expr)
 +                            }
 +                        }
 +                    },
 +                    ExprKind::Call(func, []) => {
 +                        if_chain! {
 +                            if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind;
 +                            if name.ident.as_str() == "min_value";
 +                            if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id);
 +                            if let Some(impl_id) = cx.tcx.impl_of_method(func_id);
 +                            if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl
 +                            if cx.tcx.type_of(impl_id).is_integral();
 +                            then {
 +                                print_lint_and_sugg(cx, var_name, expr)
 +                            }
 +                        }
 +                    },
 +                    _ => (),
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
 +    match peel_blocks_with_stmt(expr).kind {
 +        ExprKind::AssignOp(ref op1, target, value) => {
 +            // Check if literal being subtracted is one
 +            (BinOpKind::Sub == op1.node && is_integer_literal(value, 1)).then_some(target)
 +        },
 +        ExprKind::Assign(target, value, _) => {
 +            if_chain! {
 +                if let ExprKind::Binary(ref op1, left1, right1) = value.kind;
 +                if BinOpKind::Sub == op1.node;
 +
 +                if SpanlessEq::new(cx).eq_expr(left1, target);
 +
 +                if is_integer_literal(right1, 1);
 +                then {
 +                    Some(target)
 +                } else {
 +                    None
 +                }
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: &str, expr: &Expr<'_>) {
 +    span_lint_and_sugg(
 +        cx,
 +        IMPLICIT_SATURATING_SUB,
 +        expr.span,
 +        "implicitly performing saturating subtraction",
 +        "try",
 +        format!("{var_name} = {var_name}.saturating_sub({});", '1'),
 +        Applicability::MachineApplicable,
 +    );
 +}
index 36e03e50a8e4f4c2c950bb297f88b8d374524f6d,0000000000000000000000000000000000000000..0ef77e03de9062bb5cc98ea27ec07c3ce94fd050
mode 100644,000000..100644
--- /dev/null
@@@ -1,161 -1,0 +1,159 @@@
-             let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized {
-                 val
-             } else {
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::layout::LayoutOf;
 +use rustc_middle::ty::{self, IntTy, UintTy};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::Span;
 +
 +use clippy_utils::comparisons;
 +use clippy_utils::comparisons::Rel;
 +use clippy_utils::consts::{constant_full_int, FullInt};
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::source::snippet;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for comparisons where the relation is always either
 +    /// true or false, but where one side has been upcast so that the comparison is
 +    /// necessary. Only integer types are checked.
 +    ///
 +    /// ### Why is this bad?
 +    /// An expression like `let x : u8 = ...; (x as u32) > 300`
 +    /// will mistakenly imply that it is possible for `x` to be outside the range of
 +    /// `u8`.
 +    ///
 +    /// ### Known problems
 +    /// https://github.com/rust-lang/rust-clippy/issues/886
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: u8 = 1;
 +    /// (x as u32) > 300;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub INVALID_UPCAST_COMPARISONS,
 +    pedantic,
 +    "a comparison involving an upcast which is always true or false"
 +}
 +
 +declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]);
 +
 +fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> {
 +    if let ExprKind::Cast(cast_exp, _) = expr.kind {
 +        let pre_cast_ty = cx.typeck_results().expr_ty(cast_exp);
 +        let cast_ty = cx.typeck_results().expr_ty(expr);
 +        // if it's a cast from i32 to u32 wrapping will invalidate all these checks
 +        if cx.layout_of(pre_cast_ty).ok().map(|l| l.size) == cx.layout_of(cast_ty).ok().map(|l| l.size) {
 +            return None;
 +        }
 +        match pre_cast_ty.kind() {
 +            ty::Int(int_ty) => Some(match int_ty {
 +                IntTy::I8 => (FullInt::S(i128::from(i8::MIN)), FullInt::S(i128::from(i8::MAX))),
 +                IntTy::I16 => (FullInt::S(i128::from(i16::MIN)), FullInt::S(i128::from(i16::MAX))),
 +                IntTy::I32 => (FullInt::S(i128::from(i32::MIN)), FullInt::S(i128::from(i32::MAX))),
 +                IntTy::I64 => (FullInt::S(i128::from(i64::MIN)), FullInt::S(i128::from(i64::MAX))),
 +                IntTy::I128 => (FullInt::S(i128::MIN), FullInt::S(i128::MAX)),
 +                IntTy::Isize => (FullInt::S(isize::MIN as i128), FullInt::S(isize::MAX as i128)),
 +            }),
 +            ty::Uint(uint_ty) => Some(match uint_ty {
 +                UintTy::U8 => (FullInt::U(u128::from(u8::MIN)), FullInt::U(u128::from(u8::MAX))),
 +                UintTy::U16 => (FullInt::U(u128::from(u16::MIN)), FullInt::U(u128::from(u16::MAX))),
 +                UintTy::U32 => (FullInt::U(u128::from(u32::MIN)), FullInt::U(u128::from(u32::MAX))),
 +                UintTy::U64 => (FullInt::U(u128::from(u64::MIN)), FullInt::U(u128::from(u64::MAX))),
 +                UintTy::U128 => (FullInt::U(u128::MIN), FullInt::U(u128::MAX)),
 +                UintTy::Usize => (FullInt::U(usize::MIN as u128), FullInt::U(usize::MAX as u128)),
 +            }),
 +            _ => None,
 +        }
 +    } else {
 +        None
 +    }
 +}
 +
 +fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) {
 +    if let ExprKind::Cast(cast_val, _) = expr.kind {
 +        span_lint(
 +            cx,
 +            INVALID_UPCAST_COMPARISONS,
 +            span,
 +            &format!(
 +                "because of the numeric bounds on `{}` prior to casting, this expression is always {}",
 +                snippet(cx, cast_val.span, "the expression"),
 +                if always { "true" } else { "false" },
 +            ),
 +        );
 +    }
 +}
 +
 +fn upcast_comparison_bounds_err<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
 +    rel: comparisons::Rel,
 +    lhs_bounds: Option<(FullInt, FullInt)>,
 +    lhs: &'tcx Expr<'_>,
 +    rhs: &'tcx Expr<'_>,
 +    invert: bool,
 +) {
 +    if let Some((lb, ub)) = lhs_bounds {
 +        if let Some(norm_rhs_val) = constant_full_int(cx, cx.typeck_results(), rhs) {
 +            if rel == Rel::Eq || rel == Rel::Ne {
 +                if norm_rhs_val < lb || norm_rhs_val > ub {
 +                    err_upcast_comparison(cx, span, lhs, rel == Rel::Ne);
 +                }
 +            } else if match rel {
 +                Rel::Lt => {
 +                    if invert {
 +                        norm_rhs_val < lb
 +                    } else {
 +                        ub < norm_rhs_val
 +                    }
 +                },
 +                Rel::Le => {
 +                    if invert {
 +                        norm_rhs_val <= lb
 +                    } else {
 +                        ub <= norm_rhs_val
 +                    }
 +                },
 +                Rel::Eq | Rel::Ne => unreachable!(),
 +            } {
 +                err_upcast_comparison(cx, span, lhs, true);
 +            } else if match rel {
 +                Rel::Lt => {
 +                    if invert {
 +                        norm_rhs_val >= ub
 +                    } else {
 +                        lb >= norm_rhs_val
 +                    }
 +                },
 +                Rel::Le => {
 +                    if invert {
 +                        norm_rhs_val > ub
 +                    } else {
 +                        lb > norm_rhs_val
 +                    }
 +                },
 +                Rel::Eq | Rel::Ne => unreachable!(),
 +            } {
 +                err_upcast_comparison(cx, span, lhs, false);
 +            }
 +        }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for InvalidUpcastComparisons {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind {
 +            let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs);
++            let Some((rel, normalized_lhs, normalized_rhs)) = normalized else {
 +                return;
 +            };
 +
 +            let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs);
 +            let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs);
 +
 +            upcast_comparison_bounds_err(cx, expr.span, rel, lhs_bounds, normalized_lhs, normalized_rhs, false);
 +            upcast_comparison_bounds_err(cx, expr.span, rel, rhs_bounds, normalized_rhs, normalized_lhs, true);
 +        }
 +    }
 +}
index eb13d0869c037d522b4b3d34b0a66886c7f324db,0000000000000000000000000000000000000000..8ed7e4bb196cd63fa14aaea0d5db007ff65f3d4a
mode 100644,000000..100644
--- /dev/null
@@@ -1,220 -1,0 +1,219 @@@
-             let (adt, subst) = match ty.kind() {
-                 Adt(adt, subst) => (adt, subst),
-                 _ => panic!("already checked whether this is an enum"),
 +//! lint when there is a large size difference between variants on an enum
 +
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{diagnostics::span_lint_and_then, ty::approx_ty_size, ty::is_copy};
 +use rustc_errors::Applicability;
 +use rustc_hir::{Item, ItemKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{Adt, AdtDef, GenericArg, List, Ty};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for large size differences between variants on
 +    /// `enum`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// Enum size is bounded by the largest variant. Having one
 +    /// large variant can penalize the memory layout of that enum.
 +    ///
 +    /// ### Known problems
 +    /// This lint obviously cannot take the distribution of
 +    /// variants in your running program into account. It is possible that the
 +    /// smaller variants make up less than 1% of all instances, in which case
 +    /// the overhead is negligible and the boxing is counter-productive. Always
 +    /// measure the change this lint suggests.
 +    ///
 +    /// For types that implement `Copy`, the suggestion to `Box` a variant's
 +    /// data would require removing the trait impl. The types can of course
 +    /// still be `Clone`, but that is worse ergonomically. Depending on the
 +    /// use case it may be possible to store the large data in an auxiliary
 +    /// structure (e.g. Arena or ECS).
 +    ///
 +    /// The lint will ignore the impact of generic types to the type layout by
 +    /// assuming every type parameter is zero-sized. Depending on your use case,
 +    /// this may lead to a false positive.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// enum Test {
 +    ///     A(i32),
 +    ///     B([i32; 8000]),
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// // Possibly better
 +    /// enum Test2 {
 +    ///     A(i32),
 +    ///     B(Box<[i32; 8000]>),
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub LARGE_ENUM_VARIANT,
 +    perf,
 +    "large size difference between variants on an enum"
 +}
 +
 +#[derive(Copy, Clone)]
 +pub struct LargeEnumVariant {
 +    maximum_size_difference_allowed: u64,
 +}
 +
 +impl LargeEnumVariant {
 +    #[must_use]
 +    pub fn new(maximum_size_difference_allowed: u64) -> Self {
 +        Self {
 +            maximum_size_difference_allowed,
 +        }
 +    }
 +}
 +
 +struct FieldInfo {
 +    ind: usize,
 +    size: u64,
 +}
 +
 +struct VariantInfo {
 +    ind: usize,
 +    size: u64,
 +    fields_size: Vec<FieldInfo>,
 +}
 +
 +fn variants_size<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    adt: AdtDef<'tcx>,
 +    subst: &'tcx List<GenericArg<'tcx>>,
 +) -> Vec<VariantInfo> {
 +    let mut variants_size = adt
 +        .variants()
 +        .iter()
 +        .enumerate()
 +        .map(|(i, variant)| {
 +            let mut fields_size = variant
 +                .fields
 +                .iter()
 +                .enumerate()
 +                .map(|(i, f)| FieldInfo {
 +                    ind: i,
 +                    size: approx_ty_size(cx, f.ty(cx.tcx, subst)),
 +                })
 +                .collect::<Vec<_>>();
 +            fields_size.sort_by(|a, b| (a.size.cmp(&b.size)));
 +
 +            VariantInfo {
 +                ind: i,
 +                size: fields_size.iter().map(|info| info.size).sum(),
 +                fields_size,
 +            }
 +        })
 +        .collect::<Vec<_>>();
 +    variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
 +    variants_size
 +}
 +
 +impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) {
 +        if in_external_macro(cx.tcx.sess, item.span) {
 +            return;
 +        }
 +        if let ItemKind::Enum(ref def, _) = item.kind {
 +            let ty = cx.tcx.type_of(item.def_id);
++            let Adt(adt, subst) = ty.kind() else {
++                panic!("already checked whether this is an enum")
 +            };
 +            if adt.variants().len() <= 1 {
 +                return;
 +            }
 +            let variants_size = variants_size(cx, *adt, subst);
 +
 +            let mut difference = variants_size[0].size - variants_size[1].size;
 +            if difference > self.maximum_size_difference_allowed {
 +                let help_text = "consider boxing the large fields to reduce the total size of the enum";
 +                span_lint_and_then(
 +                    cx,
 +                    LARGE_ENUM_VARIANT,
 +                    item.span,
 +                    "large size difference between variants",
 +                    |diag| {
 +                        diag.span_label(
 +                            item.span,
 +                            format!("the entire enum is at least {} bytes", approx_ty_size(cx, ty)),
 +                        );
 +                        diag.span_label(
 +                            def.variants[variants_size[0].ind].span,
 +                            format!("the largest variant contains at least {} bytes", variants_size[0].size),
 +                        );
 +                        diag.span_label(
 +                            def.variants[variants_size[1].ind].span,
 +                            &if variants_size[1].fields_size.is_empty() {
 +                                "the second-largest variant carries no data at all".to_owned()
 +                            } else {
 +                                format!(
 +                                    "the second-largest variant contains at least {} bytes",
 +                                    variants_size[1].size
 +                                )
 +                            },
 +                        );
 +
 +                        let fields = def.variants[variants_size[0].ind].data.fields();
 +                        let mut applicability = Applicability::MaybeIncorrect;
 +                        if is_copy(cx, ty) || maybe_copy(cx, ty) {
 +                            diag.span_note(
 +                                item.ident.span,
 +                                "boxing a variant would require the type no longer be `Copy`",
 +                            );
 +                        } else {
 +                            let sugg: Vec<(Span, String)> = variants_size[0]
 +                                .fields_size
 +                                .iter()
 +                                .rev()
 +                                .map_while(|val| {
 +                                    if difference > self.maximum_size_difference_allowed {
 +                                        difference = difference.saturating_sub(val.size);
 +                                        Some((
 +                                            fields[val.ind].ty.span,
 +                                            format!(
 +                                                "Box<{}>",
 +                                                snippet_with_applicability(
 +                                                    cx,
 +                                                    fields[val.ind].ty.span,
 +                                                    "..",
 +                                                    &mut applicability
 +                                                )
 +                                                .into_owned()
 +                                            ),
 +                                        ))
 +                                    } else {
 +                                        None
 +                                    }
 +                                })
 +                                .collect();
 +
 +                            if !sugg.is_empty() {
 +                                diag.multipart_suggestion(help_text, sugg, Applicability::MaybeIncorrect);
 +                                return;
 +                            }
 +                        }
 +                        diag.span_help(def.variants[variants_size[0].ind].span, help_text);
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn maybe_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    if let Adt(_def, substs) = ty.kind()
 +        && substs.types().next().is_some()
 +        && let Some(copy_trait) = cx.tcx.lang_items().copy_trait()
 +    {
 +        return cx.tcx.non_blanket_impls_for_ty(copy_trait, ty).next().is_some();
 +    }
 +    false
 +}
index 176787497ebf2e655f93ba77b15e1c41436aa87a,0000000000000000000000000000000000000000..b7798b1c1d749ae488a6ed08fee2824f7ca9999f
mode 100644,000000..100644
--- /dev/null
@@@ -1,171 -1,0 +1,174 @@@
- use clippy_utils::ty::{is_must_use_ty, match_type};
 +use clippy_utils::diagnostics::span_lint_and_help;
- const SYNC_GUARD_PATHS: [&[&str]; 6] = [
-     &paths::MUTEX_GUARD,
-     &paths::RWLOCK_READ_GUARD,
-     &paths::RWLOCK_WRITE_GUARD,
++use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, match_type};
 +use clippy_utils::{is_must_use_func_call, paths};
 +use if_chain::if_chain;
 +use rustc_hir::{Local, PatKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::subst::GenericArgKind;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::{sym, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `let _ = <expr>` where expr is `#[must_use]`
 +    ///
 +    /// ### Why is this bad?
 +    /// It's better to explicitly handle the value of a `#[must_use]`
 +    /// expr
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn f() -> Result<u32, u32> {
 +    ///     Ok(0)
 +    /// }
 +    ///
 +    /// let _ = f();
 +    /// // is_ok() is marked #[must_use]
 +    /// let _ = f().is_ok();
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub LET_UNDERSCORE_MUST_USE,
 +    restriction,
 +    "non-binding let on a `#[must_use]` expression"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `let _ = sync_lock`.
 +    /// This supports `mutex` and `rwlock` in `std::sync` and `parking_lot`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This statement immediately drops the lock instead of
 +    /// extending its lifetime to the end of the scope, which is often not intended.
 +    /// To extend lock lifetime to the end of the scope, use an underscore-prefixed
 +    /// name instead (i.e. _lock). If you want to explicitly drop the lock,
 +    /// `std::mem::drop` conveys your intention better and is less error-prone.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let _ = mutex.lock();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// let _lock = mutex.lock();
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub LET_UNDERSCORE_LOCK,
 +    correctness,
 +    "non-binding let on a synchronization lock"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `let _ = <expr>`
 +    /// where expr has a type that implements `Drop`
 +    ///
 +    /// ### Why is this bad?
 +    /// This statement immediately drops the initializer
 +    /// expression instead of extending its lifetime to the end of the scope, which
 +    /// is often not intended. To extend the expression's lifetime to the end of the
 +    /// scope, use an underscore-prefixed name instead (i.e. _var). If you want to
 +    /// explicitly drop the expression, `std::mem::drop` conveys your intention
 +    /// better and is less error-prone.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct DroppableItem;
 +    /// {
 +    ///     let _ = DroppableItem;
 +    ///     //                   ^ dropped here
 +    ///     /* more code */
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # struct DroppableItem;
 +    /// {
 +    ///     let _droppable = DroppableItem;
 +    ///     /* more code */
 +    ///     // dropped at end of scope
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.50.0"]
 +    pub LET_UNDERSCORE_DROP,
 +    pedantic,
 +    "non-binding let on a type that implements `Drop`"
 +}
 +
 +declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]);
 +
-                         SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path))
++const SYNC_GUARD_SYMS: [Symbol; 3] = [sym::MutexGuard, sym::RwLockReadGuard, sym::RwLockWriteGuard];
++
++const SYNC_GUARD_PATHS: [&[&str]; 3] = [
 +    &paths::PARKING_LOT_MUTEX_GUARD,
 +    &paths::PARKING_LOT_RWLOCK_READ_GUARD,
 +    &paths::PARKING_LOT_RWLOCK_WRITE_GUARD,
 +];
 +
 +impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
 +    fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
 +        if in_external_macro(cx.tcx.sess, local.span) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let PatKind::Wild = local.pat.kind;
 +            if let Some(init) = local.init;
 +            then {
 +                let init_ty = cx.typeck_results().expr_ty(init);
 +                let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
 +                    GenericArgKind::Type(inner_ty) => {
-                             binding or dropping explicitly with `std::mem::drop`"
++                        SYNC_GUARD_SYMS
++                            .iter()
++                            .any(|&sym| is_type_diagnostic_item(cx, inner_ty, sym))
++                            || SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path))
 +                    },
 +
 +                    GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
 +                });
 +                if contains_sync_guard {
 +                    span_lint_and_help(
 +                        cx,
 +                        LET_UNDERSCORE_LOCK,
 +                        local.span,
 +                        "non-binding let on a synchronization lock",
 +                        None,
 +                        "consider using an underscore-prefixed named \
-                             binding or dropping explicitly with `std::mem::drop`"
++                            binding or dropping explicitly with `std::mem::drop`",
 +                    );
 +                } else if init_ty.needs_drop(cx.tcx, cx.param_env) {
 +                    span_lint_and_help(
 +                        cx,
 +                        LET_UNDERSCORE_DROP,
 +                        local.span,
 +                        "non-binding `let` on a type that implements `Drop`",
 +                        None,
 +                        "consider using an underscore-prefixed named \
-                         "consider explicitly using expression value"
++                            binding or dropping explicitly with `std::mem::drop`",
 +                    );
 +                } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
 +                    span_lint_and_help(
 +                        cx,
 +                        LET_UNDERSCORE_MUST_USE,
 +                        local.span,
 +                        "non-binding let on an expression with `#[must_use]` type",
 +                        None,
-                         "consider explicitly using function result"
++                        "consider explicitly using expression value",
 +                    );
 +                } else if is_must_use_func_call(cx, init) {
 +                    span_lint_and_help(
 +                        cx,
 +                        LET_UNDERSCORE_MUST_USE,
 +                        local.span,
 +                        "non-binding let on a result of a `#[must_use]` function",
 +                        None,
++                        "consider explicitly using function result",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
index fe1f0b56646cde331bafc0fa2c6f82fc6cf59072,0000000000000000000000000000000000000000..f5ad52ba1892a508bdf9e02a7ee09ede16bd4893
mode 100644,000000..100644
--- /dev/null
@@@ -1,364 -1,0 +1,369 @@@
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::all", Some("clippy_all"), vec![
 +    LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
 +    LintId::of(approx_const::APPROX_CONSTANT),
 +    LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
 +    LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
 +    LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
 +    LintId::of(attrs::DEPRECATED_CFG_ATTR),
 +    LintId::of(attrs::DEPRECATED_SEMVER),
 +    LintId::of(attrs::MISMATCHED_TARGET_OS),
 +    LintId::of(attrs::USELESS_ATTRIBUTE),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
 +    LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
 +    LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
 +    LintId::of(bool_to_int_with_if::BOOL_TO_INT_WITH_IF),
 +    LintId::of(booleans::NONMINIMAL_BOOL),
 +    LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
 +    LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
 +    LintId::of(box_default::BOX_DEFAULT),
 +    LintId::of(casts::CAST_ABS_TO_UNSIGNED),
 +    LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
 +    LintId::of(casts::CAST_ENUM_TRUNCATION),
++    LintId::of(casts::CAST_NAN_TO_INT),
 +    LintId::of(casts::CAST_REF_TO_MUT),
 +    LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
 +    LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
 +    LintId::of(casts::CHAR_LIT_AS_U8),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
 +    LintId::of(casts::UNNECESSARY_CAST),
 +    LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
 +    LintId::of(collapsible_if::COLLAPSIBLE_IF),
 +    LintId::of(comparison_chain::COMPARISON_CHAIN),
 +    LintId::of(copies::IFS_SAME_COND),
 +    LintId::of(copies::IF_SAME_THEN_ELSE),
 +    LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
 +    LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
 +    LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY),
 +    LintId::of(dereference::EXPLICIT_AUTO_DEREF),
 +    LintId::of(dereference::NEEDLESS_BORROW),
 +    LintId::of(derivable_impls::DERIVABLE_IMPLS),
 +    LintId::of(derive::DERIVE_HASH_XOR_EQ),
 +    LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
 +    LintId::of(disallowed_macros::DISALLOWED_MACROS),
 +    LintId::of(disallowed_methods::DISALLOWED_METHODS),
 +    LintId::of(disallowed_names::DISALLOWED_NAMES),
 +    LintId::of(disallowed_types::DISALLOWED_TYPES),
 +    LintId::of(doc::MISSING_SAFETY_DOC),
 +    LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
 +    LintId::of(double_parens::DOUBLE_PARENS),
 +    LintId::of(drop_forget_ref::DROP_COPY),
 +    LintId::of(drop_forget_ref::DROP_NON_DROP),
 +    LintId::of(drop_forget_ref::DROP_REF),
 +    LintId::of(drop_forget_ref::FORGET_COPY),
 +    LintId::of(drop_forget_ref::FORGET_NON_DROP),
 +    LintId::of(drop_forget_ref::FORGET_REF),
 +    LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
 +    LintId::of(duplicate_mod::DUPLICATE_MOD),
 +    LintId::of(entry::MAP_ENTRY),
 +    LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
 +    LintId::of(enum_variants::ENUM_VARIANT_NAMES),
 +    LintId::of(enum_variants::MODULE_INCEPTION),
 +    LintId::of(escape::BOXED_LOCAL),
 +    LintId::of(eta_reduction::REDUNDANT_CLOSURE),
 +    LintId::of(explicit_write::EXPLICIT_WRITE),
 +    LintId::of(float_literal::EXCESSIVE_PRECISION),
 +    LintId::of(format::USELESS_FORMAT),
 +    LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
 +    LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
++    LintId::of(format_args::UNINLINED_FORMAT_ARGS),
++    LintId::of(format_args::UNUSED_FORMAT_SPECS),
 +    LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
 +    LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
 +    LintId::of(formatting::POSSIBLE_MISSING_COMMA),
 +    LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
 +    LintId::of(from_over_into::FROM_OVER_INTO),
 +    LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
 +    LintId::of(functions::DOUBLE_MUST_USE),
 +    LintId::of(functions::MUST_USE_UNIT),
 +    LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
 +    LintId::of(functions::RESULT_LARGE_ERR),
 +    LintId::of(functions::RESULT_UNIT_ERR),
 +    LintId::of(functions::TOO_MANY_ARGUMENTS),
 +    LintId::of(if_let_mutex::IF_LET_MUTEX),
 +    LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD),
++    LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
 +    LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
 +    LintId::of(infinite_iter::INFINITE_ITER),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
 +    LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
 +    LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
 +    LintId::of(int_plus_one::INT_PLUS_ONE),
 +    LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED),
 +    LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
 +    LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
 +    LintId::of(len_zero::COMPARISON_TO_EMPTY),
 +    LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
 +    LintId::of(len_zero::LEN_ZERO),
 +    LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
 +    LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
 +    LintId::of(lifetimes::NEEDLESS_LIFETIMES),
 +    LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
 +    LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
 +    LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
 +    LintId::of(loops::EMPTY_LOOP),
 +    LintId::of(loops::EXPLICIT_COUNTER_LOOP),
 +    LintId::of(loops::FOR_KV_MAP),
 +    LintId::of(loops::ITER_NEXT_LOOP),
 +    LintId::of(loops::MANUAL_FIND),
 +    LintId::of(loops::MANUAL_FLATTEN),
 +    LintId::of(loops::MANUAL_MEMCPY),
 +    LintId::of(loops::MISSING_SPIN_LOOP),
 +    LintId::of(loops::MUT_RANGE_BOUND),
 +    LintId::of(loops::NEEDLESS_COLLECT),
 +    LintId::of(loops::NEEDLESS_RANGE_LOOP),
 +    LintId::of(loops::NEVER_LOOP),
 +    LintId::of(loops::SAME_ITEM_PUSH),
 +    LintId::of(loops::SINGLE_ELEMENT_LOOP),
 +    LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
 +    LintId::of(loops::WHILE_LET_LOOP),
 +    LintId::of(loops::WHILE_LET_ON_ITERATOR),
 +    LintId::of(main_recursion::MAIN_RECURSION),
 +    LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
 +    LintId::of(manual_bits::MANUAL_BITS),
 +    LintId::of(manual_clamp::MANUAL_CLAMP),
 +    LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
 +    LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
 +    LintId::of(manual_retain::MANUAL_RETAIN),
 +    LintId::of(manual_strip::MANUAL_STRIP),
 +    LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
 +    LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
 +    LintId::of(match_result_ok::MATCH_RESULT_OK),
 +    LintId::of(matches::COLLAPSIBLE_MATCH),
 +    LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
++    LintId::of(matches::MANUAL_FILTER),
 +    LintId::of(matches::MANUAL_MAP),
 +    LintId::of(matches::MANUAL_UNWRAP_OR),
 +    LintId::of(matches::MATCH_AS_REF),
 +    LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
 +    LintId::of(matches::MATCH_OVERLAPPING_ARM),
 +    LintId::of(matches::MATCH_REF_PATS),
 +    LintId::of(matches::MATCH_SINGLE_BINDING),
 +    LintId::of(matches::MATCH_STR_CASE_MISMATCH),
 +    LintId::of(matches::NEEDLESS_MATCH),
 +    LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
 +    LintId::of(matches::SINGLE_MATCH),
 +    LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
 +    LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
 +    LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
 +    LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
 +    LintId::of(methods::BIND_INSTEAD_OF_MAP),
 +    LintId::of(methods::BYTES_COUNT_TO_LEN),
 +    LintId::of(methods::BYTES_NTH),
 +    LintId::of(methods::CHARS_LAST_CMP),
 +    LintId::of(methods::CHARS_NEXT_CMP),
 +    LintId::of(methods::CLONE_DOUBLE_REF),
 +    LintId::of(methods::CLONE_ON_COPY),
 +    LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
 +    LintId::of(methods::ERR_EXPECT),
 +    LintId::of(methods::EXPECT_FUN_CALL),
 +    LintId::of(methods::EXTEND_WITH_DRAIN),
 +    LintId::of(methods::FILTER_MAP_IDENTITY),
 +    LintId::of(methods::FILTER_NEXT),
 +    LintId::of(methods::FLAT_MAP_IDENTITY),
 +    LintId::of(methods::GET_FIRST),
 +    LintId::of(methods::GET_LAST_WITH_LEN),
 +    LintId::of(methods::INSPECT_FOR_EACH),
 +    LintId::of(methods::INTO_ITER_ON_REF),
 +    LintId::of(methods::IS_DIGIT_ASCII_RADIX),
 +    LintId::of(methods::ITERATOR_STEP_BY_ZERO),
 +    LintId::of(methods::ITER_CLONED_COLLECT),
 +    LintId::of(methods::ITER_COUNT),
 +    LintId::of(methods::ITER_KV_MAP),
 +    LintId::of(methods::ITER_NEXT_SLICE),
 +    LintId::of(methods::ITER_NTH),
 +    LintId::of(methods::ITER_NTH_ZERO),
 +    LintId::of(methods::ITER_OVEREAGER_CLONED),
 +    LintId::of(methods::ITER_SKIP_NEXT),
 +    LintId::of(methods::MANUAL_FILTER_MAP),
 +    LintId::of(methods::MANUAL_FIND_MAP),
 +    LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
 +    LintId::of(methods::MANUAL_SPLIT_ONCE),
 +    LintId::of(methods::MANUAL_STR_REPEAT),
 +    LintId::of(methods::MAP_CLONE),
 +    LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
 +    LintId::of(methods::MAP_FLATTEN),
 +    LintId::of(methods::MAP_IDENTITY),
 +    LintId::of(methods::MUT_MUTEX_LOCK),
 +    LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
 +    LintId::of(methods::NEEDLESS_OPTION_TAKE),
 +    LintId::of(methods::NEEDLESS_SPLITN),
 +    LintId::of(methods::NEW_RET_NO_SELF),
 +    LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
 +    LintId::of(methods::NO_EFFECT_REPLACE),
 +    LintId::of(methods::OBFUSCATED_IF_ELSE),
 +    LintId::of(methods::OK_EXPECT),
 +    LintId::of(methods::OPTION_AS_REF_DEREF),
 +    LintId::of(methods::OPTION_FILTER_MAP),
 +    LintId::of(methods::OPTION_MAP_OR_NONE),
 +    LintId::of(methods::OR_FUN_CALL),
 +    LintId::of(methods::OR_THEN_UNWRAP),
 +    LintId::of(methods::RANGE_ZIP_WITH_LEN),
 +    LintId::of(methods::REPEAT_ONCE),
 +    LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
 +    LintId::of(methods::SEARCH_IS_SOME),
 +    LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
 +    LintId::of(methods::SINGLE_CHAR_ADD_STR),
 +    LintId::of(methods::SINGLE_CHAR_PATTERN),
 +    LintId::of(methods::SKIP_WHILE_NEXT),
 +    LintId::of(methods::STRING_EXTEND_CHARS),
 +    LintId::of(methods::SUSPICIOUS_MAP),
 +    LintId::of(methods::SUSPICIOUS_SPLITN),
 +    LintId::of(methods::SUSPICIOUS_TO_OWNED),
 +    LintId::of(methods::UNINIT_ASSUMED_INIT),
 +    LintId::of(methods::UNIT_HASH),
 +    LintId::of(methods::UNNECESSARY_FILTER_MAP),
 +    LintId::of(methods::UNNECESSARY_FIND_MAP),
 +    LintId::of(methods::UNNECESSARY_FOLD),
 +    LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
 +    LintId::of(methods::UNNECESSARY_SORT_BY),
 +    LintId::of(methods::UNNECESSARY_TO_OWNED),
 +    LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
 +    LintId::of(methods::USELESS_ASREF),
 +    LintId::of(methods::VEC_RESIZE_TO_ZERO),
 +    LintId::of(methods::WRONG_SELF_CONVENTION),
 +    LintId::of(methods::ZST_OFFSET),
 +    LintId::of(minmax::MIN_MAX),
 +    LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
 +    LintId::of(misc::TOPLEVEL_REF_ARG),
 +    LintId::of(misc::ZERO_PTR),
 +    LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
 +    LintId::of(misc_early::DOUBLE_NEG),
 +    LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
 +    LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
 +    LintId::of(misc_early::REDUNDANT_PATTERN),
 +    LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
 +    LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
 +    LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION),
 +    LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
 +    LintId::of(mut_key::MUTABLE_KEY_TYPE),
 +    LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
 +    LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
 +    LintId::of(needless_bool::BOOL_COMPARISON),
 +    LintId::of(needless_bool::NEEDLESS_BOOL),
 +    LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
 +    LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
 +    LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
 +    LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
 +    LintId::of(needless_update::NEEDLESS_UPDATE),
 +    LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
 +    LintId::of(neg_multiply::NEG_MULTIPLY),
 +    LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
 +    LintId::of(no_effect::NO_EFFECT),
 +    LintId::of(no_effect::UNNECESSARY_OPERATION),
 +    LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
 +    LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
 +    LintId::of(octal_escapes::OCTAL_ESCAPES),
 +    LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
 +    LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
 +    LintId::of(operators::ASSIGN_OP_PATTERN),
 +    LintId::of(operators::BAD_BIT_MASK),
 +    LintId::of(operators::CMP_NAN),
 +    LintId::of(operators::CMP_OWNED),
 +    LintId::of(operators::DOUBLE_COMPARISONS),
 +    LintId::of(operators::DURATION_SUBSEC),
 +    LintId::of(operators::EQ_OP),
 +    LintId::of(operators::ERASING_OP),
 +    LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
 +    LintId::of(operators::IDENTITY_OP),
 +    LintId::of(operators::INEFFECTIVE_BIT_MASK),
 +    LintId::of(operators::MISREFACTORED_ASSIGN_OP),
 +    LintId::of(operators::MODULO_ONE),
 +    LintId::of(operators::OP_REF),
 +    LintId::of(operators::PTR_EQ),
 +    LintId::of(operators::SELF_ASSIGNMENT),
 +    LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
 +    LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
 +    LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
 +    LintId::of(partialeq_to_none::PARTIALEQ_TO_NONE),
 +    LintId::of(precedence::PRECEDENCE),
 +    LintId::of(ptr::CMP_NULL),
 +    LintId::of(ptr::INVALID_NULL_PTR_USAGE),
 +    LintId::of(ptr::MUT_FROM_REF),
 +    LintId::of(ptr::PTR_ARG),
 +    LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
 +    LintId::of(question_mark::QUESTION_MARK),
 +    LintId::of(ranges::MANUAL_RANGE_CONTAINS),
 +    LintId::of(ranges::REVERSED_EMPTY_RANGES),
 +    LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
 +    LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
 +    LintId::of(redundant_clone::REDUNDANT_CLONE),
 +    LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
 +    LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
 +    LintId::of(redundant_slicing::REDUNDANT_SLICING),
 +    LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
 +    LintId::of(reference::DEREF_ADDROF),
 +    LintId::of(regex::INVALID_REGEX),
 +    LintId::of(returns::LET_AND_RETURN),
 +    LintId::of(returns::NEEDLESS_RETURN),
 +    LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
 +    LintId::of(serde_api::SERDE_API_MISUSE),
 +    LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
 +    LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
 +    LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
 +    LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
 +    LintId::of(strings::TRIM_SPLIT_WHITESPACE),
 +    LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
 +    LintId::of(swap::ALMOST_SWAPPED),
 +    LintId::of(swap::MANUAL_SWAP),
 +    LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
 +    LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
 +    LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
 +    LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
 +    LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
 +    LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
 +    LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
 +    LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
 +    LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
 +    LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
 +    LintId::of(transmute::TRANSMUTING_NULL),
 +    LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
 +    LintId::of(transmute::USELESS_TRANSMUTE),
 +    LintId::of(transmute::WRONG_TRANSMUTE),
 +    LintId::of(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_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(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(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 a58d066fa6b6737bd58e69b9bf7294513490cf60,0000000000000000000000000000000000000000..8be9dc4baf19360a6977a0a7aa118a612e8bde4b
mode 100644,000000..100644
--- /dev/null
@@@ -1,109 -1,0 +1,111 @@@
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![
 +    LintId::of(attrs::DEPRECATED_CFG_ATTR),
 +    LintId::of(booleans::NONMINIMAL_BOOL),
 +    LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
 +    LintId::of(casts::CHAR_LIT_AS_U8),
 +    LintId::of(casts::UNNECESSARY_CAST),
 +    LintId::of(dereference::EXPLICIT_AUTO_DEREF),
 +    LintId::of(derivable_impls::DERIVABLE_IMPLS),
 +    LintId::of(double_parens::DOUBLE_PARENS),
 +    LintId::of(explicit_write::EXPLICIT_WRITE),
 +    LintId::of(format::USELESS_FORMAT),
++    LintId::of(format_args::UNUSED_FORMAT_SPECS),
 +    LintId::of(functions::TOO_MANY_ARGUMENTS),
 +    LintId::of(int_plus_one::INT_PLUS_ONE),
 +    LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
 +    LintId::of(lifetimes::NEEDLESS_LIFETIMES),
 +    LintId::of(loops::EXPLICIT_COUNTER_LOOP),
 +    LintId::of(loops::MANUAL_FIND),
 +    LintId::of(loops::MANUAL_FLATTEN),
 +    LintId::of(loops::SINGLE_ELEMENT_LOOP),
 +    LintId::of(loops::WHILE_LET_LOOP),
 +    LintId::of(manual_clamp::MANUAL_CLAMP),
 +    LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
 +    LintId::of(manual_strip::MANUAL_STRIP),
 +    LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
 +    LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
++    LintId::of(matches::MANUAL_FILTER),
 +    LintId::of(matches::MANUAL_UNWRAP_OR),
 +    LintId::of(matches::MATCH_AS_REF),
 +    LintId::of(matches::MATCH_SINGLE_BINDING),
 +    LintId::of(matches::NEEDLESS_MATCH),
 +    LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
 +    LintId::of(methods::BIND_INSTEAD_OF_MAP),
 +    LintId::of(methods::BYTES_COUNT_TO_LEN),
 +    LintId::of(methods::CLONE_ON_COPY),
 +    LintId::of(methods::FILTER_MAP_IDENTITY),
 +    LintId::of(methods::FILTER_NEXT),
 +    LintId::of(methods::FLAT_MAP_IDENTITY),
 +    LintId::of(methods::GET_LAST_WITH_LEN),
 +    LintId::of(methods::INSPECT_FOR_EACH),
 +    LintId::of(methods::ITER_COUNT),
 +    LintId::of(methods::ITER_KV_MAP),
 +    LintId::of(methods::MANUAL_FILTER_MAP),
 +    LintId::of(methods::MANUAL_FIND_MAP),
 +    LintId::of(methods::MANUAL_SPLIT_ONCE),
 +    LintId::of(methods::MAP_FLATTEN),
 +    LintId::of(methods::MAP_IDENTITY),
 +    LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
 +    LintId::of(methods::NEEDLESS_OPTION_TAKE),
 +    LintId::of(methods::NEEDLESS_SPLITN),
 +    LintId::of(methods::OPTION_AS_REF_DEREF),
 +    LintId::of(methods::OPTION_FILTER_MAP),
 +    LintId::of(methods::OR_THEN_UNWRAP),
 +    LintId::of(methods::RANGE_ZIP_WITH_LEN),
 +    LintId::of(methods::REPEAT_ONCE),
 +    LintId::of(methods::SEARCH_IS_SOME),
 +    LintId::of(methods::SKIP_WHILE_NEXT),
 +    LintId::of(methods::UNNECESSARY_FILTER_MAP),
 +    LintId::of(methods::UNNECESSARY_FIND_MAP),
 +    LintId::of(methods::UNNECESSARY_SORT_BY),
 +    LintId::of(methods::USELESS_ASREF),
 +    LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
 +    LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
 +    LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
 +    LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION),
 +    LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
 +    LintId::of(needless_bool::BOOL_COMPARISON),
 +    LintId::of(needless_bool::NEEDLESS_BOOL),
 +    LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
 +    LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
 +    LintId::of(needless_update::NEEDLESS_UPDATE),
 +    LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
 +    LintId::of(no_effect::NO_EFFECT),
 +    LintId::of(no_effect::UNNECESSARY_OPERATION),
 +    LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
 +    LintId::of(operators::DOUBLE_COMPARISONS),
 +    LintId::of(operators::DURATION_SUBSEC),
 +    LintId::of(operators::IDENTITY_OP),
 +    LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
 +    LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
 +    LintId::of(precedence::PRECEDENCE),
 +    LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
 +    LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
 +    LintId::of(redundant_slicing::REDUNDANT_SLICING),
 +    LintId::of(reference::DEREF_ADDROF),
 +    LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
 +    LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
 +    LintId::of(swap::MANUAL_SWAP),
 +    LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
 +    LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
 +    LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
 +    LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
 +    LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
 +    LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
 +    LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
 +    LintId::of(transmute::USELESS_TRANSMUTE),
 +    LintId::of(types::BORROWED_BOX),
 +    LintId::of(types::TYPE_COMPLEXITY),
 +    LintId::of(types::VEC_BOX),
 +    LintId::of(unit_types::UNIT_ARG),
 +    LintId::of(unwrap::UNNECESSARY_UNWRAP),
 +    LintId::of(useless_conversion::USELESS_CONVERSION),
 +    LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
 +])
index 71dfdab369b97e93228a82f1de68ea331b89639d,0000000000000000000000000000000000000000..40c94c6e8d33dc8101986d7ba7251433288adaa5
mode 100644,000000..100644
--- /dev/null
@@@ -1,22 -1,0 +1,22 @@@
-     LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL),
-     LintId::of(utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
-     LintId::of(utils::internal_lints::COMPILER_LINT_FUNCTIONS),
-     LintId::of(utils::internal_lints::DEFAULT_DEPRECATION_REASON),
-     LintId::of(utils::internal_lints::DEFAULT_LINT),
-     LintId::of(utils::internal_lints::IF_CHAIN_STYLE),
-     LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL),
-     LintId::of(utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE),
-     LintId::of(utils::internal_lints::INVALID_PATHS),
-     LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS),
-     LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE),
-     LintId::of(utils::internal_lints::MISSING_MSRV_ATTR_IMPL),
-     LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA),
-     LintId::of(utils::internal_lints::PRODUCE_ICE),
-     LintId::of(utils::internal_lints::UNNECESSARY_DEF_PATH),
-     LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR),
 +// 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::internal", Some("clippy_internal"), vec![
++    LintId::of(utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL),
++    LintId::of(utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS),
++    LintId::of(utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS),
++    LintId::of(utils::internal_lints::if_chain_style::IF_CHAIN_STYLE),
++    LintId::of(utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL),
++    LintId::of(utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR),
++    LintId::of(utils::internal_lints::invalid_paths::INVALID_PATHS),
++    LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON),
++    LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT),
++    LintId::of(utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE),
++    LintId::of(utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS),
++    LintId::of(utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE),
++    LintId::of(utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL),
++    LintId::of(utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA),
++    LintId::of(utils::internal_lints::produce_ice::PRODUCE_ICE),
++    LintId::of(utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH),
 +])
index 306cb6a61c94390c2f4d1c6db8067c2063f53728,0000000000000000000000000000000000000000..800e3a8767133d1597dedb4c5f452ac603c52b20
mode 100644,000000..100644
--- /dev/null
@@@ -1,614 -1,0 +1,620 @@@
-     utils::internal_lints::CLIPPY_LINTS_INTERNAL,
 +// 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::COLLAPSIBLE_SPAN_LINT_CALLS,
++    utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL,
 +    #[cfg(feature = "internal")]
-     utils::internal_lints::COMPILER_LINT_FUNCTIONS,
++    utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS,
 +    #[cfg(feature = "internal")]
-     utils::internal_lints::DEFAULT_DEPRECATION_REASON,
++    utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS,
 +    #[cfg(feature = "internal")]
-     utils::internal_lints::DEFAULT_LINT,
++    utils::internal_lints::if_chain_style::IF_CHAIN_STYLE,
 +    #[cfg(feature = "internal")]
-     utils::internal_lints::IF_CHAIN_STYLE,
++    utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL,
 +    #[cfg(feature = "internal")]
-     utils::internal_lints::INTERNING_DEFINED_SYMBOL,
++    utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR,
 +    #[cfg(feature = "internal")]
-     utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE,
++    utils::internal_lints::invalid_paths::INVALID_PATHS,
 +    #[cfg(feature = "internal")]
-     utils::internal_lints::INVALID_PATHS,
++    utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON,
 +    #[cfg(feature = "internal")]
-     utils::internal_lints::LINT_WITHOUT_LINT_PASS,
++    utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT,
 +    #[cfg(feature = "internal")]
-     utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
++    utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE,
 +    #[cfg(feature = "internal")]
-     utils::internal_lints::MISSING_MSRV_ATTR_IMPL,
++    utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS,
 +    #[cfg(feature = "internal")]
-     utils::internal_lints::OUTER_EXPN_EXPN_DATA,
++    utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE,
 +    #[cfg(feature = "internal")]
-     utils::internal_lints::PRODUCE_ICE,
++    utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL,
 +    #[cfg(feature = "internal")]
-     utils::internal_lints::UNNECESSARY_DEF_PATH,
++    utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA,
 +    #[cfg(feature = "internal")]
-     utils::internal_lints::UNNECESSARY_SYMBOL_STR,
++    utils::internal_lints::produce_ice::PRODUCE_ICE,
 +    #[cfg(feature = "internal")]
++    utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH,
 +    almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
 +    approx_const::APPROX_CONSTANT,
 +    as_conversions::AS_CONVERSIONS,
 +    asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
 +    asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
 +    assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
 +    assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES,
 +    async_yields_async::ASYNC_YIELDS_ASYNC,
 +    attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON,
 +    attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
 +    attrs::DEPRECATED_CFG_ATTR,
 +    attrs::DEPRECATED_SEMVER,
 +    attrs::EMPTY_LINE_AFTER_OUTER_ATTR,
 +    attrs::INLINE_ALWAYS,
 +    attrs::MISMATCHED_TARGET_OS,
 +    attrs::USELESS_ATTRIBUTE,
 +    await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE,
 +    await_holding_invalid::AWAIT_HOLDING_LOCK,
 +    await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
 +    blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
 +    bool_assert_comparison::BOOL_ASSERT_COMPARISON,
 +    bool_to_int_with_if::BOOL_TO_INT_WITH_IF,
 +    booleans::NONMINIMAL_BOOL,
 +    booleans::OVERLY_COMPLEX_BOOL_EXPR,
 +    borrow_deref_ref::BORROW_DEREF_REF,
 +    box_default::BOX_DEFAULT,
 +    cargo::CARGO_COMMON_METADATA,
 +    cargo::MULTIPLE_CRATE_VERSIONS,
 +    cargo::NEGATIVE_FEATURE_NAMES,
 +    cargo::REDUNDANT_FEATURE_NAMES,
 +    cargo::WILDCARD_DEPENDENCIES,
++    casts::AS_PTR_CAST_MUT,
 +    casts::AS_UNDERSCORE,
 +    casts::BORROW_AS_PTR,
 +    casts::CAST_ABS_TO_UNSIGNED,
 +    casts::CAST_ENUM_CONSTRUCTOR,
 +    casts::CAST_ENUM_TRUNCATION,
 +    casts::CAST_LOSSLESS,
++    casts::CAST_NAN_TO_INT,
 +    casts::CAST_POSSIBLE_TRUNCATION,
 +    casts::CAST_POSSIBLE_WRAP,
 +    casts::CAST_PRECISION_LOSS,
 +    casts::CAST_PTR_ALIGNMENT,
 +    casts::CAST_REF_TO_MUT,
 +    casts::CAST_SIGN_LOSS,
 +    casts::CAST_SLICE_DIFFERENT_SIZES,
 +    casts::CAST_SLICE_FROM_RAW_PARTS,
 +    casts::CHAR_LIT_AS_U8,
 +    casts::FN_TO_NUMERIC_CAST,
 +    casts::FN_TO_NUMERIC_CAST_ANY,
 +    casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
 +    casts::PTR_AS_PTR,
 +    casts::UNNECESSARY_CAST,
 +    checked_conversions::CHECKED_CONVERSIONS,
 +    cognitive_complexity::COGNITIVE_COMPLEXITY,
 +    collapsible_if::COLLAPSIBLE_ELSE_IF,
 +    collapsible_if::COLLAPSIBLE_IF,
 +    comparison_chain::COMPARISON_CHAIN,
 +    copies::BRANCHES_SHARING_CODE,
 +    copies::IFS_SAME_COND,
 +    copies::IF_SAME_THEN_ELSE,
 +    copies::SAME_FUNCTIONS_IN_IF_CONDITION,
 +    copy_iterator::COPY_ITERATOR,
 +    crate_in_macro_def::CRATE_IN_MACRO_DEF,
 +    create_dir::CREATE_DIR,
 +    dbg_macro::DBG_MACRO,
 +    default::DEFAULT_TRAIT_ACCESS,
 +    default::FIELD_REASSIGN_WITH_DEFAULT,
 +    default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY,
 +    default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
 +    default_union_representation::DEFAULT_UNION_REPRESENTATION,
 +    dereference::EXPLICIT_AUTO_DEREF,
 +    dereference::EXPLICIT_DEREF_METHODS,
 +    dereference::NEEDLESS_BORROW,
 +    dereference::REF_BINDING_TO_REFERENCE,
 +    derivable_impls::DERIVABLE_IMPLS,
 +    derive::DERIVE_HASH_XOR_EQ,
 +    derive::DERIVE_ORD_XOR_PARTIAL_ORD,
 +    derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ,
 +    derive::EXPL_IMPL_CLONE_ON_COPY,
 +    derive::UNSAFE_DERIVE_DESERIALIZE,
 +    disallowed_macros::DISALLOWED_MACROS,
 +    disallowed_methods::DISALLOWED_METHODS,
 +    disallowed_names::DISALLOWED_NAMES,
 +    disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
 +    disallowed_types::DISALLOWED_TYPES,
 +    doc::DOC_LINK_WITH_QUOTES,
 +    doc::DOC_MARKDOWN,
 +    doc::MISSING_ERRORS_DOC,
 +    doc::MISSING_PANICS_DOC,
 +    doc::MISSING_SAFETY_DOC,
 +    doc::NEEDLESS_DOCTEST_MAIN,
 +    double_parens::DOUBLE_PARENS,
 +    drop_forget_ref::DROP_COPY,
 +    drop_forget_ref::DROP_NON_DROP,
 +    drop_forget_ref::DROP_REF,
 +    drop_forget_ref::FORGET_COPY,
 +    drop_forget_ref::FORGET_NON_DROP,
 +    drop_forget_ref::FORGET_REF,
 +    drop_forget_ref::UNDROPPED_MANUALLY_DROPS,
 +    duplicate_mod::DUPLICATE_MOD,
 +    else_if_without_else::ELSE_IF_WITHOUT_ELSE,
 +    empty_drop::EMPTY_DROP,
 +    empty_enum::EMPTY_ENUM,
 +    empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS,
 +    entry::MAP_ENTRY,
 +    enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
 +    enum_variants::ENUM_VARIANT_NAMES,
 +    enum_variants::MODULE_INCEPTION,
 +    enum_variants::MODULE_NAME_REPETITIONS,
 +    equatable_if_let::EQUATABLE_IF_LET,
 +    escape::BOXED_LOCAL,
 +    eta_reduction::REDUNDANT_CLOSURE,
 +    eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
 +    excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
 +    excessive_bools::STRUCT_EXCESSIVE_BOOLS,
 +    exhaustive_items::EXHAUSTIVE_ENUMS,
 +    exhaustive_items::EXHAUSTIVE_STRUCTS,
 +    exit::EXIT,
 +    explicit_write::EXPLICIT_WRITE,
 +    fallible_impl_from::FALLIBLE_IMPL_FROM,
 +    float_literal::EXCESSIVE_PRECISION,
 +    float_literal::LOSSY_FLOAT_LITERAL,
 +    floating_point_arithmetic::IMPRECISE_FLOPS,
 +    floating_point_arithmetic::SUBOPTIMAL_FLOPS,
 +    format::USELESS_FORMAT,
 +    format_args::FORMAT_IN_FORMAT_ARGS,
 +    format_args::TO_STRING_IN_FORMAT_ARGS,
 +    format_args::UNINLINED_FORMAT_ARGS,
++    format_args::UNUSED_FORMAT_SPECS,
 +    format_impl::PRINT_IN_FORMAT_IMPL,
 +    format_impl::RECURSIVE_FORMAT_IMPL,
 +    format_push_string::FORMAT_PUSH_STRING,
 +    formatting::POSSIBLE_MISSING_COMMA,
 +    formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
 +    formatting::SUSPICIOUS_ELSE_FORMATTING,
 +    formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
 +    from_over_into::FROM_OVER_INTO,
 +    from_str_radix_10::FROM_STR_RADIX_10,
 +    functions::DOUBLE_MUST_USE,
 +    functions::MUST_USE_CANDIDATE,
 +    functions::MUST_USE_UNIT,
 +    functions::NOT_UNSAFE_PTR_ARG_DEREF,
 +    functions::RESULT_LARGE_ERR,
 +    functions::RESULT_UNIT_ERR,
 +    functions::TOO_MANY_ARGUMENTS,
 +    functions::TOO_MANY_LINES,
 +    future_not_send::FUTURE_NOT_SEND,
 +    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_add::IMPLICIT_SATURATING_ADD,
 +    implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
 +    inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
 +    index_refutable_slice::INDEX_REFUTABLE_SLICE,
 +    indexing_slicing::INDEXING_SLICING,
 +    indexing_slicing::OUT_OF_BOUNDS_INDEXING,
 +    infinite_iter::INFINITE_ITER,
 +    infinite_iter::MAYBE_INFINITE_ITER,
 +    inherent_impl::MULTIPLE_INHERENT_IMPL,
 +    inherent_to_string::INHERENT_TO_STRING,
 +    inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY,
 +    init_numbered_fields::INIT_NUMBERED_FIELDS,
 +    inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
 +    int_plus_one::INT_PLUS_ONE,
 +    invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
 +    invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED,
 +    items_after_statements::ITEMS_AFTER_STATEMENTS,
 +    iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
 +    large_const_arrays::LARGE_CONST_ARRAYS,
 +    large_enum_variant::LARGE_ENUM_VARIANT,
 +    large_include_file::LARGE_INCLUDE_FILE,
 +    large_stack_arrays::LARGE_STACK_ARRAYS,
 +    len_zero::COMPARISON_TO_EMPTY,
 +    len_zero::LEN_WITHOUT_IS_EMPTY,
 +    len_zero::LEN_ZERO,
 +    let_if_seq::USELESS_LET_IF_SEQ,
 +    let_underscore::LET_UNDERSCORE_DROP,
 +    let_underscore::LET_UNDERSCORE_LOCK,
 +    let_underscore::LET_UNDERSCORE_MUST_USE,
 +    lifetimes::EXTRA_UNUSED_LIFETIMES,
 +    lifetimes::NEEDLESS_LIFETIMES,
 +    literal_representation::DECIMAL_LITERAL_REPRESENTATION,
 +    literal_representation::INCONSISTENT_DIGIT_GROUPING,
 +    literal_representation::LARGE_DIGIT_GROUPS,
 +    literal_representation::MISTYPED_LITERAL_SUFFIXES,
 +    literal_representation::UNREADABLE_LITERAL,
 +    literal_representation::UNUSUAL_BYTE_GROUPINGS,
 +    loops::EMPTY_LOOP,
 +    loops::EXPLICIT_COUNTER_LOOP,
 +    loops::EXPLICIT_INTO_ITER_LOOP,
 +    loops::EXPLICIT_ITER_LOOP,
 +    loops::FOR_KV_MAP,
 +    loops::ITER_NEXT_LOOP,
 +    loops::MANUAL_FIND,
 +    loops::MANUAL_FLATTEN,
 +    loops::MANUAL_MEMCPY,
 +    loops::MISSING_SPIN_LOOP,
 +    loops::MUT_RANGE_BOUND,
 +    loops::NEEDLESS_COLLECT,
 +    loops::NEEDLESS_RANGE_LOOP,
 +    loops::NEVER_LOOP,
 +    loops::SAME_ITEM_PUSH,
 +    loops::SINGLE_ELEMENT_LOOP,
 +    loops::WHILE_IMMUTABLE_CONDITION,
 +    loops::WHILE_LET_LOOP,
 +    loops::WHILE_LET_ON_ITERATOR,
 +    macro_use::MACRO_USE_IMPORTS,
 +    main_recursion::MAIN_RECURSION,
 +    manual_assert::MANUAL_ASSERT,
 +    manual_async_fn::MANUAL_ASYNC_FN,
 +    manual_bits::MANUAL_BITS,
 +    manual_clamp::MANUAL_CLAMP,
 +    manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
 +    manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
 +    manual_rem_euclid::MANUAL_REM_EUCLID,
 +    manual_retain::MANUAL_RETAIN,
 +    manual_string_new::MANUAL_STRING_NEW,
 +    manual_strip::MANUAL_STRIP,
 +    map_unit_fn::OPTION_MAP_UNIT_FN,
 +    map_unit_fn::RESULT_MAP_UNIT_FN,
 +    match_result_ok::MATCH_RESULT_OK,
 +    matches::COLLAPSIBLE_MATCH,
 +    matches::INFALLIBLE_DESTRUCTURING_MATCH,
++    matches::MANUAL_FILTER,
 +    matches::MANUAL_MAP,
 +    matches::MANUAL_UNWRAP_OR,
 +    matches::MATCH_AS_REF,
 +    matches::MATCH_BOOL,
 +    matches::MATCH_LIKE_MATCHES_MACRO,
 +    matches::MATCH_ON_VEC_ITEMS,
 +    matches::MATCH_OVERLAPPING_ARM,
 +    matches::MATCH_REF_PATS,
 +    matches::MATCH_SAME_ARMS,
 +    matches::MATCH_SINGLE_BINDING,
 +    matches::MATCH_STR_CASE_MISMATCH,
 +    matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +    matches::MATCH_WILD_ERR_ARM,
 +    matches::NEEDLESS_MATCH,
 +    matches::REDUNDANT_PATTERN_MATCHING,
 +    matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +    matches::SIGNIFICANT_DROP_IN_SCRUTINEE,
 +    matches::SINGLE_MATCH,
 +    matches::SINGLE_MATCH_ELSE,
 +    matches::TRY_ERR,
 +    matches::WILDCARD_ENUM_MATCH_ARM,
 +    matches::WILDCARD_IN_OR_PATTERNS,
 +    mem_forget::MEM_FORGET,
 +    mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
 +    mem_replace::MEM_REPLACE_WITH_DEFAULT,
 +    mem_replace::MEM_REPLACE_WITH_UNINIT,
 +    methods::BIND_INSTEAD_OF_MAP,
 +    methods::BYTES_COUNT_TO_LEN,
 +    methods::BYTES_NTH,
 +    methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
 +    methods::CHARS_LAST_CMP,
 +    methods::CHARS_NEXT_CMP,
 +    methods::CLONED_INSTEAD_OF_COPIED,
 +    methods::CLONE_DOUBLE_REF,
 +    methods::CLONE_ON_COPY,
 +    methods::CLONE_ON_REF_PTR,
 +    methods::COLLAPSIBLE_STR_REPLACE,
 +    methods::ERR_EXPECT,
 +    methods::EXPECT_FUN_CALL,
 +    methods::EXPECT_USED,
 +    methods::EXTEND_WITH_DRAIN,
 +    methods::FILETYPE_IS_FILE,
 +    methods::FILTER_MAP_IDENTITY,
 +    methods::FILTER_MAP_NEXT,
 +    methods::FILTER_NEXT,
 +    methods::FLAT_MAP_IDENTITY,
 +    methods::FLAT_MAP_OPTION,
 +    methods::FROM_ITER_INSTEAD_OF_COLLECT,
 +    methods::GET_FIRST,
 +    methods::GET_LAST_WITH_LEN,
 +    methods::GET_UNWRAP,
 +    methods::IMPLICIT_CLONE,
 +    methods::INEFFICIENT_TO_STRING,
 +    methods::INSPECT_FOR_EACH,
 +    methods::INTO_ITER_ON_REF,
 +    methods::IS_DIGIT_ASCII_RADIX,
 +    methods::ITERATOR_STEP_BY_ZERO,
 +    methods::ITER_CLONED_COLLECT,
 +    methods::ITER_COUNT,
 +    methods::ITER_KV_MAP,
 +    methods::ITER_NEXT_SLICE,
 +    methods::ITER_NTH,
 +    methods::ITER_NTH_ZERO,
 +    methods::ITER_ON_EMPTY_COLLECTIONS,
 +    methods::ITER_ON_SINGLE_ITEMS,
 +    methods::ITER_OVEREAGER_CLONED,
 +    methods::ITER_SKIP_NEXT,
 +    methods::ITER_WITH_DRAIN,
 +    methods::MANUAL_FILTER_MAP,
 +    methods::MANUAL_FIND_MAP,
 +    methods::MANUAL_OK_OR,
 +    methods::MANUAL_SATURATING_ARITHMETIC,
 +    methods::MANUAL_SPLIT_ONCE,
 +    methods::MANUAL_STR_REPEAT,
 +    methods::MAP_CLONE,
 +    methods::MAP_COLLECT_RESULT_UNIT,
 +    methods::MAP_ERR_IGNORE,
 +    methods::MAP_FLATTEN,
 +    methods::MAP_IDENTITY,
 +    methods::MAP_UNWRAP_OR,
 +    methods::MUT_MUTEX_LOCK,
 +    methods::NAIVE_BYTECOUNT,
 +    methods::NEEDLESS_OPTION_AS_DEREF,
 +    methods::NEEDLESS_OPTION_TAKE,
 +    methods::NEEDLESS_SPLITN,
 +    methods::NEW_RET_NO_SELF,
 +    methods::NONSENSICAL_OPEN_OPTIONS,
 +    methods::NO_EFFECT_REPLACE,
 +    methods::OBFUSCATED_IF_ELSE,
 +    methods::OK_EXPECT,
 +    methods::OPTION_AS_REF_DEREF,
 +    methods::OPTION_FILTER_MAP,
 +    methods::OPTION_MAP_OR_NONE,
 +    methods::OR_FUN_CALL,
 +    methods::OR_THEN_UNWRAP,
 +    methods::PATH_BUF_PUSH_OVERWRITE,
 +    methods::RANGE_ZIP_WITH_LEN,
 +    methods::REPEAT_ONCE,
 +    methods::RESULT_MAP_OR_INTO_OPTION,
 +    methods::SEARCH_IS_SOME,
 +    methods::SHOULD_IMPLEMENT_TRAIT,
 +    methods::SINGLE_CHAR_ADD_STR,
 +    methods::SINGLE_CHAR_PATTERN,
 +    methods::SKIP_WHILE_NEXT,
 +    methods::STABLE_SORT_PRIMITIVE,
 +    methods::STRING_EXTEND_CHARS,
 +    methods::SUSPICIOUS_MAP,
 +    methods::SUSPICIOUS_SPLITN,
 +    methods::SUSPICIOUS_TO_OWNED,
 +    methods::UNINIT_ASSUMED_INIT,
 +    methods::UNIT_HASH,
 +    methods::UNNECESSARY_FILTER_MAP,
 +    methods::UNNECESSARY_FIND_MAP,
 +    methods::UNNECESSARY_FOLD,
 +    methods::UNNECESSARY_JOIN,
 +    methods::UNNECESSARY_LAZY_EVALUATIONS,
 +    methods::UNNECESSARY_SORT_BY,
 +    methods::UNNECESSARY_TO_OWNED,
 +    methods::UNWRAP_OR_ELSE_DEFAULT,
 +    methods::UNWRAP_USED,
 +    methods::USELESS_ASREF,
 +    methods::VEC_RESIZE_TO_ZERO,
 +    methods::VERBOSE_FILE_READS,
 +    methods::WRONG_SELF_CONVENTION,
 +    methods::ZST_OFFSET,
 +    minmax::MIN_MAX,
 +    misc::SHORT_CIRCUIT_STATEMENT,
 +    misc::TOPLEVEL_REF_ARG,
 +    misc::USED_UNDERSCORE_BINDING,
 +    misc::ZERO_PTR,
 +    misc_early::BUILTIN_TYPE_SHADOW,
 +    misc_early::DOUBLE_NEG,
 +    misc_early::DUPLICATE_UNDERSCORE_ARGUMENT,
 +    misc_early::MIXED_CASE_HEX_LITERALS,
 +    misc_early::REDUNDANT_PATTERN,
 +    misc_early::SEPARATED_LITERAL_SUFFIX,
 +    misc_early::UNNEEDED_FIELD_PATTERN,
 +    misc_early::UNNEEDED_WILDCARD_PATTERN,
 +    misc_early::UNSEPARATED_LITERAL_SUFFIX,
 +    misc_early::ZERO_PREFIXED_LITERAL,
 +    mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER,
 +    missing_const_for_fn::MISSING_CONST_FOR_FN,
 +    missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
 +    missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
 +    missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
++    missing_trait_methods::MISSING_TRAIT_METHODS,
 +    mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION,
 +    mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION,
 +    module_style::MOD_MODULE_FILES,
 +    module_style::SELF_NAMED_MODULE_FILES,
 +    multi_assignments::MULTI_ASSIGNMENTS,
 +    mut_key::MUTABLE_KEY_TYPE,
 +    mut_mut::MUT_MUT,
 +    mut_reference::UNNECESSARY_MUT_PASSED,
 +    mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
 +    mutex_atomic::MUTEX_ATOMIC,
 +    mutex_atomic::MUTEX_INTEGER,
 +    needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
 +    needless_bool::BOOL_COMPARISON,
 +    needless_bool::NEEDLESS_BOOL,
 +    needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
 +    needless_continue::NEEDLESS_CONTINUE,
 +    needless_for_each::NEEDLESS_FOR_EACH,
 +    needless_late_init::NEEDLESS_LATE_INIT,
 +    needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS,
 +    needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
 +    needless_question_mark::NEEDLESS_QUESTION_MARK,
 +    needless_update::NEEDLESS_UPDATE,
 +    neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD,
 +    neg_multiply::NEG_MULTIPLY,
 +    new_without_default::NEW_WITHOUT_DEFAULT,
 +    no_effect::NO_EFFECT,
 +    no_effect::NO_EFFECT_UNDERSCORE_BINDING,
 +    no_effect::UNNECESSARY_OPERATION,
 +    non_copy_const::BORROW_INTERIOR_MUTABLE_CONST,
 +    non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST,
 +    non_expressive_names::JUST_UNDERSCORES_AND_DIGITS,
 +    non_expressive_names::MANY_SINGLE_CHAR_NAMES,
 +    non_expressive_names::SIMILAR_NAMES,
 +    non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
 +    non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY,
 +    nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
 +    octal_escapes::OCTAL_ESCAPES,
 +    only_used_in_recursion::ONLY_USED_IN_RECURSION,
 +    operators::ABSURD_EXTREME_COMPARISONS,
 +    operators::ARITHMETIC_SIDE_EFFECTS,
 +    operators::ASSIGN_OP_PATTERN,
 +    operators::BAD_BIT_MASK,
 +    operators::CMP_NAN,
 +    operators::CMP_OWNED,
 +    operators::DOUBLE_COMPARISONS,
 +    operators::DURATION_SUBSEC,
 +    operators::EQ_OP,
 +    operators::ERASING_OP,
 +    operators::FLOAT_ARITHMETIC,
 +    operators::FLOAT_CMP,
 +    operators::FLOAT_CMP_CONST,
 +    operators::FLOAT_EQUALITY_WITHOUT_ABS,
 +    operators::IDENTITY_OP,
 +    operators::INEFFECTIVE_BIT_MASK,
 +    operators::INTEGER_ARITHMETIC,
 +    operators::INTEGER_DIVISION,
 +    operators::MISREFACTORED_ASSIGN_OP,
 +    operators::MODULO_ARITHMETIC,
 +    operators::MODULO_ONE,
 +    operators::NEEDLESS_BITWISE_BOOL,
 +    operators::OP_REF,
 +    operators::PTR_EQ,
 +    operators::SELF_ASSIGNMENT,
 +    operators::VERBOSE_BIT_MASK,
 +    option_env_unwrap::OPTION_ENV_UNWRAP,
 +    option_if_let_else::OPTION_IF_LET_ELSE,
 +    overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
 +    panic_in_result_fn::PANIC_IN_RESULT_FN,
 +    panic_unimplemented::PANIC,
 +    panic_unimplemented::TODO,
 +    panic_unimplemented::UNIMPLEMENTED,
 +    panic_unimplemented::UNREACHABLE,
++    partial_pub_fields::PARTIAL_PUB_FIELDS,
 +    partialeq_ne_impl::PARTIALEQ_NE_IMPL,
 +    partialeq_to_none::PARTIALEQ_TO_NONE,
 +    pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
 +    pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
 +    pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
 +    precedence::PRECEDENCE,
 +    ptr::CMP_NULL,
 +    ptr::INVALID_NULL_PTR_USAGE,
 +    ptr::MUT_FROM_REF,
 +    ptr::PTR_ARG,
 +    ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
 +    pub_use::PUB_USE,
 +    question_mark::QUESTION_MARK,
 +    ranges::MANUAL_RANGE_CONTAINS,
 +    ranges::RANGE_MINUS_ONE,
 +    ranges::RANGE_PLUS_ONE,
 +    ranges::REVERSED_EMPTY_RANGES,
 +    rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT,
 +    read_zero_byte_vec::READ_ZERO_BYTE_VEC,
 +    redundant_clone::REDUNDANT_CLONE,
 +    redundant_closure_call::REDUNDANT_CLOSURE_CALL,
 +    redundant_else::REDUNDANT_ELSE,
 +    redundant_field_names::REDUNDANT_FIELD_NAMES,
 +    redundant_pub_crate::REDUNDANT_PUB_CRATE,
 +    redundant_slicing::DEREF_BY_SLICING,
 +    redundant_slicing::REDUNDANT_SLICING,
 +    redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
 +    ref_option_ref::REF_OPTION_REF,
 +    reference::DEREF_ADDROF,
 +    regex::INVALID_REGEX,
 +    regex::TRIVIAL_REGEX,
 +    return_self_not_must_use::RETURN_SELF_NOT_MUST_USE,
 +    returns::LET_AND_RETURN,
 +    returns::NEEDLESS_RETURN,
 +    same_name_method::SAME_NAME_METHOD,
 +    self_named_constructors::SELF_NAMED_CONSTRUCTORS,
 +    semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
 +    serde_api::SERDE_API_MISUSE,
 +    shadow::SHADOW_REUSE,
 +    shadow::SHADOW_SAME,
 +    shadow::SHADOW_UNRELATED,
 +    single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES,
 +    single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
 +    size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
 +    slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
 +    std_instead_of_core::ALLOC_INSTEAD_OF_CORE,
 +    std_instead_of_core::STD_INSTEAD_OF_ALLOC,
 +    std_instead_of_core::STD_INSTEAD_OF_CORE,
 +    strings::STRING_ADD,
 +    strings::STRING_ADD_ASSIGN,
 +    strings::STRING_FROM_UTF8_AS_BYTES,
 +    strings::STRING_LIT_AS_BYTES,
 +    strings::STRING_SLICE,
 +    strings::STRING_TO_STRING,
 +    strings::STR_TO_STRING,
 +    strings::TRIM_SPLIT_WHITESPACE,
 +    strlen_on_c_strings::STRLEN_ON_C_STRINGS,
 +    suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS,
 +    suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
 +    suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
 +    swap::ALMOST_SWAPPED,
 +    swap::MANUAL_SWAP,
 +    swap_ptr_to_ref::SWAP_PTR_TO_REF,
 +    tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
 +    temporary_assignment::TEMPORARY_ASSIGNMENT,
 +    to_digit_is_some::TO_DIGIT_IS_SOME,
 +    trailing_empty_array::TRAILING_EMPTY_ARRAY,
 +    trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS,
 +    trait_bounds::TYPE_REPETITION_IN_BOUNDS,
 +    transmute::CROSSPOINTER_TRANSMUTE,
 +    transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
 +    transmute::TRANSMUTE_BYTES_TO_STR,
 +    transmute::TRANSMUTE_FLOAT_TO_INT,
 +    transmute::TRANSMUTE_INT_TO_BOOL,
 +    transmute::TRANSMUTE_INT_TO_CHAR,
 +    transmute::TRANSMUTE_INT_TO_FLOAT,
 +    transmute::TRANSMUTE_NUM_TO_BYTES,
 +    transmute::TRANSMUTE_PTR_TO_PTR,
 +    transmute::TRANSMUTE_PTR_TO_REF,
 +    transmute::TRANSMUTE_UNDEFINED_REPR,
 +    transmute::TRANSMUTING_NULL,
 +    transmute::UNSOUND_COLLECTION_TRANSMUTE,
 +    transmute::USELESS_TRANSMUTE,
 +    transmute::WRONG_TRANSMUTE,
 +    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_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_wraps::UNNECESSARY_WRAPS,
 +    unnested_or_patterns::UNNESTED_OR_PATTERNS,
 +    unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
 +    unused_async::UNUSED_ASYNC,
 +    unused_io_amount::UNUSED_IO_AMOUNT,
 +    unused_peekable::UNUSED_PEEKABLE,
 +    unused_rounding::UNUSED_ROUNDING,
 +    unused_self::UNUSED_SELF,
 +    unused_unit::UNUSED_UNIT,
 +    unwrap::PANICKING_UNWRAP,
 +    unwrap::UNNECESSARY_UNWRAP,
 +    unwrap_in_result::UNWRAP_IN_RESULT,
 +    upper_case_acronyms::UPPER_CASE_ACRONYMS,
 +    use_self::USE_SELF,
 +    useless_conversion::USELESS_CONVERSION,
 +    vec::USELESS_VEC,
 +    vec_init_then_push::VEC_INIT_THEN_PUSH,
 +    wildcard_imports::ENUM_GLOB_USE,
 +    wildcard_imports::WILDCARD_IMPORTS,
 +    write::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 e0b4639af53e631ba4250bdfb5f88f3fc5ed061d,0000000000000000000000000000000000000000..65616d28d8f10aef2e4aad619c7f7a20c7bb4a07
mode 100644,000000..100644
--- /dev/null
@@@ -1,38 -1,0 +1,39 @@@
 +// 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(casts::AS_PTR_CAST_MUT),
 +    LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
 +    LintId::of(copies::BRANCHES_SHARING_CODE),
 +    LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),
 +    LintId::of(equatable_if_let::EQUATABLE_IF_LET),
 +    LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
 +    LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
 +    LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS),
 +    LintId::of(future_not_send::FUTURE_NOT_SEND),
 +    LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE),
 +    LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
 +    LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
 +    LintId::of(methods::ITER_ON_EMPTY_COLLECTIONS),
 +    LintId::of(methods::ITER_ON_SINGLE_ITEMS),
 +    LintId::of(methods::ITER_WITH_DRAIN),
 +    LintId::of(methods::PATH_BUF_PUSH_OVERWRITE),
 +    LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
 +    LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
 +    LintId::of(mutex_atomic::MUTEX_ATOMIC),
 +    LintId::of(mutex_atomic::MUTEX_INTEGER),
 +    LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
 +    LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
 +    LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
 +    LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
 +    LintId::of(regex::TRIVIAL_REGEX),
 +    LintId::of(strings::STRING_LIT_AS_BYTES),
 +    LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
 +    LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY),
 +    LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR),
 +    LintId::of(unused_peekable::UNUSED_PEEKABLE),
 +    LintId::of(unused_rounding::UNUSED_ROUNDING),
 +    LintId::of(use_self::USE_SELF),
 +])
index bc2f0beb358a50f0d8c7981d3e49e7c8dd435b4c,0000000000000000000000000000000000000000..060d3bdc7c6fad240c0757cb084934757e238142
mode 100644,000000..100644
--- /dev/null
@@@ -1,105 -1,0 +1,103 @@@
-     LintId::of(format_args::UNINLINED_FORMAT_ARGS),
 +// 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(casts::BORROW_AS_PTR),
 +    LintId::of(casts::CAST_LOSSLESS),
 +    LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
 +    LintId::of(casts::CAST_POSSIBLE_WRAP),
 +    LintId::of(casts::CAST_PRECISION_LOSS),
 +    LintId::of(casts::CAST_PTR_ALIGNMENT),
 +    LintId::of(casts::CAST_SIGN_LOSS),
 +    LintId::of(casts::PTR_AS_PTR),
 +    LintId::of(checked_conversions::CHECKED_CONVERSIONS),
 +    LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION),
 +    LintId::of(copy_iterator::COPY_ITERATOR),
 +    LintId::of(default::DEFAULT_TRAIT_ACCESS),
 +    LintId::of(dereference::EXPLICIT_DEREF_METHODS),
 +    LintId::of(dereference::REF_BINDING_TO_REFERENCE),
 +    LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY),
 +    LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE),
 +    LintId::of(doc::DOC_LINK_WITH_QUOTES),
 +    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(implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
 +    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(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR),
 +    LintId::of(infinite_iter::MAYBE_INFINITE_ITER),
 +    LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS),
 +    LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS),
 +    LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR),
 +    LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS),
 +    LintId::of(let_underscore::LET_UNDERSCORE_DROP),
 +    LintId::of(literal_representation::LARGE_DIGIT_GROUPS),
 +    LintId::of(literal_representation::UNREADABLE_LITERAL),
 +    LintId::of(loops::EXPLICIT_INTO_ITER_LOOP),
 +    LintId::of(loops::EXPLICIT_ITER_LOOP),
 +    LintId::of(macro_use::MACRO_USE_IMPORTS),
 +    LintId::of(manual_assert::MANUAL_ASSERT),
 +    LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED),
 +    LintId::of(manual_string_new::MANUAL_STRING_NEW),
 +    LintId::of(matches::MATCH_BOOL),
 +    LintId::of(matches::MATCH_ON_VEC_ITEMS),
 +    LintId::of(matches::MATCH_SAME_ARMS),
 +    LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
 +    LintId::of(matches::MATCH_WILD_ERR_ARM),
 +    LintId::of(matches::SINGLE_MATCH_ELSE),
 +    LintId::of(methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
 +    LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
 +    LintId::of(methods::FILTER_MAP_NEXT),
 +    LintId::of(methods::FLAT_MAP_OPTION),
 +    LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
 +    LintId::of(methods::IMPLICIT_CLONE),
 +    LintId::of(methods::INEFFICIENT_TO_STRING),
 +    LintId::of(methods::MANUAL_OK_OR),
 +    LintId::of(methods::MAP_UNWRAP_OR),
 +    LintId::of(methods::NAIVE_BYTECOUNT),
 +    LintId::of(methods::STABLE_SORT_PRIMITIVE),
 +    LintId::of(methods::UNNECESSARY_JOIN),
 +    LintId::of(misc::USED_UNDERSCORE_BINDING),
 +    LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER),
 +    LintId::of(mut_mut::MUT_MUT),
 +    LintId::of(needless_continue::NEEDLESS_CONTINUE),
 +    LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
 +    LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
 +    LintId::of(no_effect::NO_EFFECT_UNDERSCORE_BINDING),
 +    LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
 +    LintId::of(non_expressive_names::SIMILAR_NAMES),
 +    LintId::of(operators::FLOAT_CMP),
 +    LintId::of(operators::NEEDLESS_BITWISE_BOOL),
 +    LintId::of(operators::VERBOSE_BIT_MASK),
 +    LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
 +    LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
 +    LintId::of(ranges::RANGE_MINUS_ONE),
 +    LintId::of(ranges::RANGE_PLUS_ONE),
 +    LintId::of(redundant_else::REDUNDANT_ELSE),
 +    LintId::of(ref_option_ref::REF_OPTION_REF),
 +    LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
 +    LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
 +    LintId::of(strings::STRING_ADD_ASSIGN),
 +    LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
 +    LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
 +    LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
 +    LintId::of(types::LINKEDLIST),
 +    LintId::of(types::OPTION_OPTION),
 +    LintId::of(unicode::UNICODE_NOT_NFC),
 +    LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS),
 +    LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS),
 +    LintId::of(unused_async::UNUSED_ASYNC),
 +    LintId::of(unused_self::UNUSED_SELF),
 +    LintId::of(wildcard_imports::ENUM_GLOB_USE),
 +    LintId::of(wildcard_imports::WILDCARD_IMPORTS),
 +    LintId::of(zero_sized_map_values::ZERO_SIZED_MAP_VALUES),
 +])
index 6eb9b3d3b9b7aa61be5ce4de886d42d3c3dabd14,0000000000000000000000000000000000000000..f62d57af5b47f1aed2849a352043102bb818b5cc
mode 100644,000000..100644
--- /dev/null
@@@ -1,88 -1,0 +1,90 @@@
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
 +    LintId::of(as_conversions::AS_CONVERSIONS),
 +    LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
 +    LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
 +    LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES),
 +    LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
 +    LintId::of(casts::AS_UNDERSCORE),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
 +    LintId::of(create_dir::CREATE_DIR),
 +    LintId::of(dbg_macro::DBG_MACRO),
 +    LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),
 +    LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION),
 +    LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
 +    LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
 +    LintId::of(empty_drop::EMPTY_DROP),
 +    LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS),
 +    LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
 +    LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
 +    LintId::of(exit::EXIT),
 +    LintId::of(float_literal::LOSSY_FLOAT_LITERAL),
 +    LintId::of(format_push_string::FORMAT_PUSH_STRING),
 +    LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE),
 +    LintId::of(implicit_return::IMPLICIT_RETURN),
 +    LintId::of(indexing_slicing::INDEXING_SLICING),
 +    LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL),
 +    LintId::of(large_include_file::LARGE_INCLUDE_FILE),
 +    LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
 +    LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
 +    LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
 +    LintId::of(matches::TRY_ERR),
 +    LintId::of(matches::WILDCARD_ENUM_MATCH_ARM),
 +    LintId::of(mem_forget::MEM_FORGET),
 +    LintId::of(methods::CLONE_ON_REF_PTR),
 +    LintId::of(methods::EXPECT_USED),
 +    LintId::of(methods::FILETYPE_IS_FILE),
 +    LintId::of(methods::GET_UNWRAP),
 +    LintId::of(methods::MAP_ERR_IGNORE),
 +    LintId::of(methods::UNWRAP_USED),
 +    LintId::of(methods::VERBOSE_FILE_READS),
 +    LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
 +    LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
 +    LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
 +    LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
 +    LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES),
 +    LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
++    LintId::of(missing_trait_methods::MISSING_TRAIT_METHODS),
 +    LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION),
 +    LintId::of(module_style::MOD_MODULE_FILES),
 +    LintId::of(module_style::SELF_NAMED_MODULE_FILES),
 +    LintId::of(operators::ARITHMETIC_SIDE_EFFECTS),
 +    LintId::of(operators::FLOAT_ARITHMETIC),
 +    LintId::of(operators::FLOAT_CMP_CONST),
 +    LintId::of(operators::INTEGER_ARITHMETIC),
 +    LintId::of(operators::INTEGER_DIVISION),
 +    LintId::of(operators::MODULO_ARITHMETIC),
 +    LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
 +    LintId::of(panic_unimplemented::PANIC),
 +    LintId::of(panic_unimplemented::TODO),
 +    LintId::of(panic_unimplemented::UNIMPLEMENTED),
 +    LintId::of(panic_unimplemented::UNREACHABLE),
++    LintId::of(partial_pub_fields::PARTIAL_PUB_FIELDS),
 +    LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
 +    LintId::of(pub_use::PUB_USE),
 +    LintId::of(redundant_slicing::DEREF_BY_SLICING),
 +    LintId::of(same_name_method::SAME_NAME_METHOD),
 +    LintId::of(shadow::SHADOW_REUSE),
 +    LintId::of(shadow::SHADOW_SAME),
 +    LintId::of(shadow::SHADOW_UNRELATED),
 +    LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES),
 +    LintId::of(std_instead_of_core::ALLOC_INSTEAD_OF_CORE),
 +    LintId::of(std_instead_of_core::STD_INSTEAD_OF_ALLOC),
 +    LintId::of(std_instead_of_core::STD_INSTEAD_OF_CORE),
 +    LintId::of(strings::STRING_ADD),
 +    LintId::of(strings::STRING_SLICE),
 +    LintId::of(strings::STRING_TO_STRING),
 +    LintId::of(strings::STR_TO_STRING),
 +    LintId::of(types::RC_BUFFER),
 +    LintId::of(types::RC_MUTEX),
 +    LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS),
 +    LintId::of(unicode::NON_ASCII_LITERAL),
 +    LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
 +    LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
 +    LintId::of(write::PRINT_STDERR),
 +    LintId::of(write::PRINT_STDOUT),
 +    LintId::of(write::USE_DEBUG),
 +])
index 8e1390167dc81fd1e7bfd856ee63d6fea4b19577,0000000000000000000000000000000000000000..6894d69e928a716b5a07e086d74c3e8938c527ac
mode 100644,000000..100644
--- /dev/null
@@@ -1,130 -1,0 +1,132 @@@
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::style", Some("clippy_style"), vec![
 +    LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
 +    LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
 +    LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
 +    LintId::of(bool_to_int_with_if::BOOL_TO_INT_WITH_IF),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
 +    LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
 +    LintId::of(collapsible_if::COLLAPSIBLE_IF),
 +    LintId::of(comparison_chain::COMPARISON_CHAIN),
 +    LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
 +    LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY),
 +    LintId::of(dereference::NEEDLESS_BORROW),
 +    LintId::of(disallowed_macros::DISALLOWED_MACROS),
 +    LintId::of(disallowed_methods::DISALLOWED_METHODS),
 +    LintId::of(disallowed_names::DISALLOWED_NAMES),
 +    LintId::of(disallowed_types::DISALLOWED_TYPES),
 +    LintId::of(doc::MISSING_SAFETY_DOC),
 +    LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
 +    LintId::of(enum_variants::ENUM_VARIANT_NAMES),
 +    LintId::of(enum_variants::MODULE_INCEPTION),
 +    LintId::of(eta_reduction::REDUNDANT_CLOSURE),
 +    LintId::of(float_literal::EXCESSIVE_PRECISION),
++    LintId::of(format_args::UNINLINED_FORMAT_ARGS),
 +    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(implicit_saturating_add::IMPLICIT_SATURATING_ADD),
++    LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING),
 +    LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
 +    LintId::of(len_zero::COMPARISON_TO_EMPTY),
 +    LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
 +    LintId::of(len_zero::LEN_ZERO),
 +    LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
 +    LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
 +    LintId::of(loops::FOR_KV_MAP),
 +    LintId::of(loops::NEEDLESS_RANGE_LOOP),
 +    LintId::of(loops::SAME_ITEM_PUSH),
 +    LintId::of(loops::WHILE_LET_ON_ITERATOR),
 +    LintId::of(main_recursion::MAIN_RECURSION),
 +    LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
 +    LintId::of(manual_bits::MANUAL_BITS),
 +    LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
 +    LintId::of(match_result_ok::MATCH_RESULT_OK),
 +    LintId::of(matches::COLLAPSIBLE_MATCH),
 +    LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
 +    LintId::of(matches::MANUAL_MAP),
 +    LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
 +    LintId::of(matches::MATCH_OVERLAPPING_ARM),
 +    LintId::of(matches::MATCH_REF_PATS),
 +    LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
 +    LintId::of(matches::SINGLE_MATCH),
 +    LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
 +    LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
 +    LintId::of(methods::BYTES_NTH),
 +    LintId::of(methods::CHARS_LAST_CMP),
 +    LintId::of(methods::CHARS_NEXT_CMP),
 +    LintId::of(methods::ERR_EXPECT),
 +    LintId::of(methods::GET_FIRST),
 +    LintId::of(methods::INTO_ITER_ON_REF),
 +    LintId::of(methods::IS_DIGIT_ASCII_RADIX),
 +    LintId::of(methods::ITER_CLONED_COLLECT),
 +    LintId::of(methods::ITER_NEXT_SLICE),
 +    LintId::of(methods::ITER_NTH_ZERO),
 +    LintId::of(methods::ITER_SKIP_NEXT),
 +    LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
 +    LintId::of(methods::MAP_CLONE),
 +    LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
 +    LintId::of(methods::MUT_MUTEX_LOCK),
 +    LintId::of(methods::NEW_RET_NO_SELF),
 +    LintId::of(methods::OBFUSCATED_IF_ELSE),
 +    LintId::of(methods::OK_EXPECT),
 +    LintId::of(methods::OPTION_MAP_OR_NONE),
 +    LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
 +    LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
 +    LintId::of(methods::SINGLE_CHAR_ADD_STR),
 +    LintId::of(methods::STRING_EXTEND_CHARS),
 +    LintId::of(methods::UNNECESSARY_FOLD),
 +    LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
 +    LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
 +    LintId::of(methods::WRONG_SELF_CONVENTION),
 +    LintId::of(misc::TOPLEVEL_REF_ARG),
 +    LintId::of(misc::ZERO_PTR),
 +    LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
 +    LintId::of(misc_early::DOUBLE_NEG),
 +    LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
 +    LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
 +    LintId::of(misc_early::REDUNDANT_PATTERN),
 +    LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
 +    LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
 +    LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
 +    LintId::of(neg_multiply::NEG_MULTIPLY),
 +    LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
 +    LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
 +    LintId::of(operators::ASSIGN_OP_PATTERN),
 +    LintId::of(operators::OP_REF),
 +    LintId::of(operators::PTR_EQ),
 +    LintId::of(partialeq_to_none::PARTIALEQ_TO_NONE),
 +    LintId::of(ptr::CMP_NULL),
 +    LintId::of(ptr::PTR_ARG),
 +    LintId::of(question_mark::QUESTION_MARK),
 +    LintId::of(ranges::MANUAL_RANGE_CONTAINS),
 +    LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
 +    LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
 +    LintId::of(returns::LET_AND_RETURN),
 +    LintId::of(returns::NEEDLESS_RETURN),
 +    LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
 +    LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
 +    LintId::of(strings::TRIM_SPLIT_WHITESPACE),
 +    LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
 +    LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
 +    LintId::of(unit_types::LET_UNIT_VALUE),
 +    LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
 +    LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
 +    LintId::of(unused_unit::UNUSED_UNIT),
 +    LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
 +    LintId::of(write::PRINTLN_EMPTY_STRING),
 +    LintId::of(write::PRINT_LITERAL),
 +    LintId::of(write::PRINT_WITH_NEWLINE),
 +    LintId::of(write::WRITELN_EMPTY_STRING),
 +    LintId::of(write::WRITE_LITERAL),
 +    LintId::of(write::WRITE_WITH_NEWLINE),
 +])
index d6d95c95c85d2f7f065b97fceb2997521a2da61d,0000000000000000000000000000000000000000..b70c4bb73e57d129bbf291c7d6220f428e877a55
mode 100644,000000..100644
--- /dev/null
@@@ -1,37 -1,0 +1,38 @@@
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![
 +    LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
 +    LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
 +    LintId::of(casts::CAST_ABS_TO_UNSIGNED),
 +    LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
 +    LintId::of(casts::CAST_ENUM_TRUNCATION),
++    LintId::of(casts::CAST_NAN_TO_INT),
 +    LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
 +    LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
 +    LintId::of(drop_forget_ref::DROP_NON_DROP),
 +    LintId::of(drop_forget_ref::FORGET_NON_DROP),
 +    LintId::of(duplicate_mod::DUPLICATE_MOD),
 +    LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
 +    LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
 +    LintId::of(loops::EMPTY_LOOP),
 +    LintId::of(loops::MUT_RANGE_BOUND),
 +    LintId::of(methods::NO_EFFECT_REPLACE),
 +    LintId::of(methods::SUSPICIOUS_MAP),
 +    LintId::of(methods::SUSPICIOUS_TO_OWNED),
 +    LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
 +    LintId::of(mut_key::MUTABLE_KEY_TYPE),
 +    LintId::of(octal_escapes::OCTAL_ESCAPES),
 +    LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
 +    LintId::of(operators::MISREFACTORED_ASSIGN_OP),
 +    LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
 +    LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
 +])
index 89ffca8128a9e1bfb8d18706db5a8cbdceb9ed49,0000000000000000000000000000000000000000..1307096b28d7bae1c627d777ff69d55e4907e825
mode 100644,000000..100644
--- /dev/null
@@@ -1,974 -1,0 +1,983 @@@
- extern crate rustc_mir_dataflow;
 +#![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(lint_reasons)]
 +#![feature(never_type)]
 +#![feature(once_cell)]
 +#![feature(rustc_private)]
 +#![feature(stmt_expr_attributes)]
 +#![recursion_limit = "512"]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
 +#![warn(trivial_casts, trivial_numeric_casts)]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +// Disable this rustc lint for now, as it was also done in rustc
 +#![allow(rustc::potential_query_instability)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_arena;
 +extern crate rustc_ast;
 +extern crate rustc_ast_pretty;
 +extern crate rustc_attr;
 +extern crate rustc_data_structures;
 +extern crate rustc_driver;
 +extern crate rustc_errors;
 +extern crate rustc_hir;
 +extern crate rustc_hir_analysis;
 +extern crate rustc_hir_typeck;
 +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;
-             sess.err(&format!(
 +extern crate rustc_parse;
 +extern crate rustc_session;
 +extern crate rustc_span;
 +extern crate rustc_target;
 +extern crate rustc_trait_selection;
 +
 +#[macro_use]
 +extern crate clippy_utils;
 +
 +use clippy_utils::parse_msrv;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_lint::LintId;
 +use rustc_semver::RustcVersion;
 +use rustc_session::Session;
 +
 +/// Macro used to declare a Clippy lint.
 +///
 +/// Every lint declaration consists of 4 parts:
 +///
 +/// 1. The documentation, which is used for the website
 +/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions.
 +/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or
 +///    `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of.
 +/// 4. The `description` that contains a short explanation on what's wrong with code where the
 +///    lint is triggered.
 +///
 +/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are
 +/// enabled by default. As said in the README.md of this repository, if the lint level mapping
 +/// changes, please update README.md.
 +///
 +/// # Example
 +///
 +/// ```
 +/// #![feature(rustc_private)]
 +/// extern crate rustc_session;
 +/// use rustc_session::declare_tool_lint;
 +/// use clippy_lints::declare_clippy_lint;
 +///
 +/// declare_clippy_lint! {
 +///     /// ### What it does
 +///     /// Checks for ... (describe what the lint matches).
 +///     ///
 +///     /// ### Why is this bad?
 +///     /// Supply the reason for linting the code.
 +///     ///
 +///     /// ### Example
 +///     /// ```rust
 +///     /// Insert a short example of code that triggers the lint
 +///     /// ```
 +///     ///
 +///     /// Use instead:
 +///     /// ```rust
 +///     /// Insert a short example of improved code that doesn't trigger the lint
 +///     /// ```
 +///     pub LINT_NAME,
 +///     pedantic,
 +///     "description"
 +/// }
 +/// ```
 +/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
 +#[macro_export]
 +macro_rules! declare_clippy_lint {
 +    { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, suspicious, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +}
 +
 +#[cfg(feature = "internal")]
 +pub mod deprecated_lints;
 +#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
 +mod utils;
 +
 +mod renamed_lints;
 +
 +// begin lints modules, do not remove this comment, it’s used in `update_lints`
 +mod almost_complete_letter_range;
 +mod approx_const;
 +mod as_conversions;
 +mod asm_syntax;
 +mod assertions_on_constants;
 +mod assertions_on_result_states;
 +mod async_yields_async;
 +mod attrs;
 +mod await_holding_invalid;
 +mod blocks_in_if_conditions;
 +mod bool_assert_comparison;
 +mod bool_to_int_with_if;
 +mod booleans;
 +mod borrow_deref_ref;
 +mod box_default;
 +mod cargo;
 +mod casts;
 +mod checked_conversions;
 +mod cognitive_complexity;
 +mod collapsible_if;
 +mod comparison_chain;
 +mod copies;
 +mod copy_iterator;
 +mod crate_in_macro_def;
 +mod create_dir;
 +mod dbg_macro;
 +mod default;
 +mod default_instead_of_iter_empty;
 +mod default_numeric_fallback;
 +mod default_union_representation;
 +mod dereference;
 +mod derivable_impls;
 +mod derive;
 +mod disallowed_macros;
 +mod disallowed_methods;
 +mod disallowed_names;
 +mod disallowed_script_idents;
 +mod disallowed_types;
 +mod doc;
 +mod double_parens;
 +mod drop_forget_ref;
 +mod duplicate_mod;
 +mod else_if_without_else;
 +mod empty_drop;
 +mod empty_enum;
 +mod empty_structs_with_brackets;
 +mod entry;
 +mod enum_clike;
 +mod enum_variants;
 +mod equatable_if_let;
 +mod escape;
 +mod eta_reduction;
 +mod excessive_bools;
 +mod exhaustive_items;
 +mod exit;
 +mod explicit_write;
 +mod fallible_impl_from;
 +mod float_literal;
 +mod floating_point_arithmetic;
 +mod format;
 +mod format_args;
 +mod format_impl;
 +mod format_push_string;
 +mod formatting;
 +mod from_over_into;
 +mod from_str_radix_10;
 +mod functions;
 +mod future_not_send;
 +mod if_let_mutex;
 +mod if_not_else;
 +mod if_then_some_else_none;
 +mod implicit_hasher;
 +mod implicit_return;
 +mod implicit_saturating_add;
 +mod implicit_saturating_sub;
 +mod inconsistent_struct_constructor;
 +mod index_refutable_slice;
 +mod indexing_slicing;
 +mod infinite_iter;
 +mod inherent_impl;
 +mod inherent_to_string;
 +mod init_numbered_fields;
 +mod inline_fn_without_body;
 +mod int_plus_one;
 +mod invalid_upcast_comparisons;
 +mod invalid_utf8_in_unchecked;
 +mod items_after_statements;
 +mod iter_not_returning_iterator;
 +mod large_const_arrays;
 +mod large_enum_variant;
 +mod large_include_file;
 +mod large_stack_arrays;
 +mod len_zero;
 +mod let_if_seq;
 +mod let_underscore;
 +mod lifetimes;
 +mod literal_representation;
 +mod loops;
 +mod macro_use;
 +mod main_recursion;
 +mod manual_assert;
 +mod manual_async_fn;
 +mod manual_bits;
 +mod manual_clamp;
 +mod manual_instant_elapsed;
 +mod manual_non_exhaustive;
 +mod manual_rem_euclid;
 +mod manual_retain;
 +mod manual_string_new;
 +mod manual_strip;
 +mod map_unit_fn;
 +mod match_result_ok;
 +mod matches;
 +mod mem_forget;
 +mod mem_replace;
 +mod methods;
 +mod minmax;
 +mod misc;
 +mod misc_early;
 +mod mismatching_type_param_order;
 +mod missing_const_for_fn;
 +mod missing_doc;
 +mod missing_enforced_import_rename;
 +mod missing_inline;
++mod missing_trait_methods;
 +mod mixed_read_write_in_expression;
 +mod module_style;
 +mod multi_assignments;
 +mod mut_key;
 +mod mut_mut;
 +mod mut_reference;
 +mod mutable_debug_assertion;
 +mod mutex_atomic;
 +mod needless_arbitrary_self_type;
 +mod needless_bool;
 +mod needless_borrowed_ref;
 +mod needless_continue;
 +mod needless_for_each;
 +mod needless_late_init;
 +mod needless_parens_on_range_literals;
 +mod needless_pass_by_value;
 +mod needless_question_mark;
 +mod needless_update;
 +mod neg_cmp_op_on_partial_ord;
 +mod neg_multiply;
 +mod new_without_default;
 +mod no_effect;
 +mod non_copy_const;
 +mod non_expressive_names;
 +mod non_octal_unix_permissions;
 +mod non_send_fields_in_send_ty;
 +mod nonstandard_macro_braces;
 +mod octal_escapes;
 +mod only_used_in_recursion;
 +mod operators;
 +mod option_env_unwrap;
 +mod option_if_let_else;
 +mod overflow_check_conditional;
 +mod panic_in_result_fn;
 +mod panic_unimplemented;
++mod partial_pub_fields;
 +mod partialeq_ne_impl;
 +mod partialeq_to_none;
 +mod pass_by_ref_or_value;
 +mod pattern_type_mismatch;
 +mod precedence;
 +mod ptr;
 +mod ptr_offset_with_cast;
 +mod pub_use;
 +mod question_mark;
 +mod ranges;
 +mod rc_clone_in_vec_init;
 +mod read_zero_byte_vec;
 +mod redundant_clone;
 +mod redundant_closure_call;
 +mod redundant_else;
 +mod redundant_field_names;
 +mod redundant_pub_crate;
 +mod redundant_slicing;
 +mod redundant_static_lifetimes;
 +mod ref_option_ref;
 +mod reference;
 +mod regex;
 +mod return_self_not_must_use;
 +mod returns;
 +mod same_name_method;
 +mod self_named_constructors;
 +mod semicolon_if_nothing_returned;
 +mod serde_api;
 +mod shadow;
 +mod single_char_lifetime_names;
 +mod single_component_path_imports;
 +mod size_of_in_element_count;
 +mod slow_vector_initialization;
 +mod std_instead_of_core;
 +mod strings;
 +mod strlen_on_c_strings;
 +mod suspicious_operation_groupings;
 +mod suspicious_trait_impl;
 +mod swap;
 +mod swap_ptr_to_ref;
 +mod tabs_in_doc_comments;
 +mod temporary_assignment;
 +mod to_digit_is_some;
 +mod trailing_empty_array;
 +mod trait_bounds;
 +mod transmute;
 +mod types;
 +mod undocumented_unsafe_blocks;
 +mod unicode;
 +mod uninit_vec;
 +mod unit_return_expecting_ord;
 +mod unit_types;
 +mod unnamed_address;
 +mod unnecessary_owned_empty_strings;
 +mod unnecessary_self_imports;
 +mod unnecessary_wraps;
 +mod unnested_or_patterns;
 +mod unsafe_removed_from_name;
 +mod unused_async;
 +mod unused_io_amount;
 +mod unused_peekable;
 +mod unused_rounding;
 +mod unused_self;
 +mod unused_unit;
 +mod unwrap;
 +mod unwrap_in_result;
 +mod upper_case_acronyms;
 +mod use_self;
 +mod useless_conversion;
 +mod vec;
 +mod vec_init_then_push;
 +mod wildcard_imports;
 +mod write;
 +mod zero_div_zero;
 +mod zero_sized_map_values;
 +// end lints modules, do not remove this comment, it’s used in `update_lints`
 +
 +pub use crate::utils::conf::Conf;
 +use crate::utils::conf::{format_error, TryConf};
 +
 +/// Register all pre expansion lints
 +///
 +/// Pre-expansion lints run before any macro expansion has happened.
 +///
 +/// Note that due to the architecture of the compiler, currently `cfg_attr` attributes on crate
 +/// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
 +///
 +/// Used in `./src/driver.rs`.
 +pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
 +    // NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
 +
 +    let msrv = conf.msrv.as_ref().and_then(|s| {
 +        parse_msrv(s, None, None).or_else(|| {
-             sess.err(&format!(
++            sess.err(format!(
 +                "error reading Clippy's configuration file. `{s}` is not a valid Rust version"
 +            ));
 +            None
 +        })
 +    });
 +
 +    store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv }));
 +}
 +
 +fn read_msrv(conf: &Conf, sess: &Session) -> Option<RustcVersion> {
 +    let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
 +        .ok()
 +        .and_then(|v| parse_msrv(&v, None, None));
 +    let clippy_msrv = conf.msrv.as_ref().and_then(|s| {
 +        parse_msrv(s, None, None).or_else(|| {
-                 sess.warn(&format!(
++            sess.err(format!(
 +                "error reading Clippy's configuration file. `{s}` is not a valid Rust version"
 +            ));
 +            None
 +        })
 +    });
 +
 +    if let Some(cargo_msrv) = cargo_msrv {
 +        if let Some(clippy_msrv) = clippy_msrv {
 +            // if both files have an msrv, let's compare them and emit a warning if they differ
 +            if clippy_msrv != cargo_msrv {
-         sess.err(&format!(
++                sess.warn(format!(
 +                    "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`"
 +                ));
 +            }
 +
 +            Some(clippy_msrv)
 +        } else {
 +            Some(cargo_msrv)
 +        }
 +    } else {
 +        clippy_msrv
 +    }
 +}
 +
 +#[doc(hidden)]
 +pub fn read_conf(sess: &Session) -> Conf {
 +    let file_name = match utils::conf::lookup_conf_file() {
 +        Ok(Some(path)) => path,
 +        Ok(None) => return Conf::default(),
 +        Err(error) => {
 +            sess.struct_err(&format!("error finding Clippy's configuration file: {error}"))
 +                .emit();
 +            return Conf::default();
 +        },
 +    };
 +
 +    let TryConf { conf, errors, warnings } = utils::conf::read(&file_name);
 +    // all conf errors are non-fatal, we just use the default conf in case of error
 +    for error in errors {
-         sess.struct_warn(&format!(
++        sess.err(format!(
 +            "error reading Clippy's configuration file `{}`: {}",
 +            file_name.display(),
 +            format_error(error)
 +        ));
 +    }
 +
 +    for warning in warnings {
-         store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal));
-         store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce));
-         store.register_late_pass(|_| Box::new(utils::internal_lints::CollapsibleCalls));
-         store.register_late_pass(|_| Box::new(utils::internal_lints::CompilerLintFunctions::new()));
-         store.register_late_pass(|_| Box::new(utils::internal_lints::IfChainStyle));
-         store.register_late_pass(|_| Box::new(utils::internal_lints::InvalidPaths));
-         store.register_late_pass(|_| Box::<utils::internal_lints::InterningDefinedSymbol>::default());
-         store.register_late_pass(|_| Box::<utils::internal_lints::LintWithoutLintPass>::default());
-         store.register_late_pass(|_| Box::new(utils::internal_lints::UnnecessaryDefPath));
-         store.register_late_pass(|_| Box::new(utils::internal_lints::OuterExpnDataPass));
-         store.register_late_pass(|_| Box::new(utils::internal_lints::MsrvAttrImpl));
++        sess.struct_warn(format!(
 +            "error reading Clippy's configuration file `{}`: {}",
 +            file_name.display(),
 +            format_error(warning)
 +        ))
 +        .emit();
 +    }
 +
 +    conf
 +}
 +
 +/// Register all lints and lint groups with the rustc plugin registry
 +///
 +/// Used in `./src/driver.rs`.
 +#[expect(clippy::too_many_lines)]
 +pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
 +    register_removed_non_tool_lints(store);
 +
 +    include!("lib.deprecated.rs");
 +
 +    include!("lib.register_lints.rs");
 +    include!("lib.register_restriction.rs");
 +    include!("lib.register_pedantic.rs");
 +
 +    #[cfg(feature = "internal")]
 +    include!("lib.register_internal.rs");
 +
 +    include!("lib.register_all.rs");
 +    include!("lib.register_style.rs");
 +    include!("lib.register_complexity.rs");
 +    include!("lib.register_correctness.rs");
 +    include!("lib.register_suspicious.rs");
 +    include!("lib.register_perf.rs");
 +    include!("lib.register_cargo.rs");
 +    include!("lib.register_nursery.rs");
 +
 +    #[cfg(feature = "internal")]
 +    {
 +        if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
 +            store.register_late_pass(|_| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
 +            return;
 +        }
 +    }
 +
 +    // all the internal lints
 +    #[cfg(feature = "internal")]
 +    {
++        store.register_early_pass(|| Box::new(utils::internal_lints::clippy_lints_internal::ClippyLintsInternal));
++        store.register_early_pass(|| Box::new(utils::internal_lints::produce_ice::ProduceIce));
++        store.register_late_pass(|_| Box::new(utils::internal_lints::collapsible_calls::CollapsibleCalls));
++        store.register_late_pass(|_| {
++            Box::new(utils::internal_lints::compiler_lint_functions::CompilerLintFunctions::new())
++        });
++        store.register_late_pass(|_| Box::new(utils::internal_lints::if_chain_style::IfChainStyle));
++        store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths));
++        store.register_late_pass(|_| {
++            Box::<utils::internal_lints::interning_defined_symbol::InterningDefinedSymbol>::default()
++        });
++        store.register_late_pass(|_| {
++            Box::<utils::internal_lints::lint_without_lint_pass::LintWithoutLintPass>::default()
++        });
++        store.register_late_pass(|_| Box::<utils::internal_lints::unnecessary_def_path::UnnecessaryDefPath>::default());
++        store.register_late_pass(|_| Box::new(utils::internal_lints::outer_expn_data_pass::OuterExpnDataPass));
++        store.register_late_pass(|_| Box::new(utils::internal_lints::msrv_attr_impl::MsrvAttrImpl));
 +    }
 +
 +    let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone();
 +    store.register_late_pass(move |_| {
 +        Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(
 +            arithmetic_side_effects_allowed.clone(),
 +        ))
 +    });
 +    store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
 +    store.register_late_pass(|_| Box::new(utils::author::Author));
 +    let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
 +    store.register_late_pass(move |_| {
 +        Box::new(await_holding_invalid::AwaitHolding::new(
 +            await_holding_invalid_types.clone(),
 +        ))
 +    });
 +    store.register_late_pass(|_| Box::new(serde_api::SerdeApi));
 +    let vec_box_size_threshold = conf.vec_box_size_threshold;
 +    let type_complexity_threshold = conf.type_complexity_threshold;
 +    let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
 +    store.register_late_pass(move |_| {
 +        Box::new(types::Types::new(
 +            vec_box_size_threshold,
 +            type_complexity_threshold,
 +            avoid_breaking_exported_api,
 +        ))
 +    });
 +    store.register_late_pass(|_| Box::new(booleans::NonminimalBool));
 +    store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant));
 +    store.register_late_pass(|_| Box::new(float_literal::FloatLiteral));
 +    store.register_late_pass(|_| Box::new(ptr::Ptr));
 +    store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool));
 +    store.register_late_pass(|_| Box::new(needless_bool::BoolComparison));
 +    store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach));
 +    store.register_late_pass(|_| Box::new(misc::MiscLints));
 +    store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction));
 +    store.register_late_pass(|_| Box::new(mut_mut::MutMut));
 +    store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed));
 +    store.register_late_pass(|_| Box::new(len_zero::LenZero));
 +    store.register_late_pass(|_| Box::new(attrs::Attributes));
 +    store.register_late_pass(|_| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
 +    store.register_late_pass(|_| Box::new(unicode::Unicode));
 +    store.register_late_pass(|_| Box::new(uninit_vec::UninitVec));
 +    store.register_late_pass(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
 +    store.register_late_pass(|_| Box::new(strings::StringAdd));
 +    store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn));
 +    store.register_late_pass(|_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub));
 +    store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback));
 +    store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor));
 +    store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
 +    store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
 +
 +    let msrv = read_msrv(conf, sess);
 +    let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
 +    let allow_expect_in_tests = conf.allow_expect_in_tests;
 +    let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
 +    store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv)));
 +    store.register_late_pass(move |_| {
 +        Box::new(methods::Methods::new(
 +            avoid_breaking_exported_api,
 +            msrv,
 +            allow_expect_in_tests,
 +            allow_unwrap_in_tests,
 +        ))
 +    });
 +    store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv)));
 +    store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv)));
 +    store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)));
 +    store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(needless_question_mark::NeedlessQuestionMark));
 +    store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv)));
 +    store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
 +    store.register_late_pass(|_| Box::new(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::<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::<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(zero_div_zero::ZeroDiv));
 +    store.register_late_pass(|_| Box::new(mutex_atomic::Mutex));
 +    store.register_late_pass(|_| Box::new(needless_update::NeedlessUpdate));
 +    store.register_late_pass(|_| Box::new(needless_borrowed_ref::NeedlessBorrowedRef));
 +    store.register_late_pass(|_| Box::new(borrow_deref_ref::BorrowDerefRef));
 +    store.register_late_pass(|_| Box::new(no_effect::NoEffect));
 +    store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment));
 +    store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv)));
 +    let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
 +    store.register_late_pass(move |_| {
 +        Box::new(cognitive_complexity::CognitiveComplexity::new(
 +            cognitive_complexity_threshold,
 +        ))
 +    });
 +    let too_large_for_stack = conf.too_large_for_stack;
 +    store.register_late_pass(move |_| Box::new(escape::BoxedLocal { too_large_for_stack }));
 +    store.register_late_pass(move |_| Box::new(vec::UselessVec { too_large_for_stack }));
 +    store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented));
 +    store.register_late_pass(|_| Box::new(strings::StringLitAsBytes));
 +    store.register_late_pass(|_| Box::new(derive::Derive));
 +    store.register_late_pass(|_| Box::new(derivable_impls::DerivableImpls));
 +    store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef));
 +    store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum));
 +    store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
 +    store.register_late_pass(|_| Box::new(regex::Regex));
 +    store.register_late_pass(|_| Box::new(copies::CopyAndPaste));
 +    store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator));
 +    store.register_late_pass(|_| Box::new(format::UselessFormat));
 +    store.register_late_pass(|_| Box::new(swap::Swap));
 +    store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional));
 +    store.register_late_pass(|_| Box::<new_without_default::NewWithoutDefault>::default());
 +    let disallowed_names = conf.disallowed_names.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone())));
 +    let too_many_arguments_threshold = conf.too_many_arguments_threshold;
 +    let too_many_lines_threshold = conf.too_many_lines_threshold;
 +    let large_error_threshold = conf.large_error_threshold;
 +    store.register_late_pass(move |_| {
 +        Box::new(functions::Functions::new(
 +            too_many_arguments_threshold,
 +            too_many_lines_threshold,
 +            large_error_threshold,
 +        ))
 +    });
 +    let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_late_pass(move |_| Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
 +    store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply));
 +    store.register_late_pass(|_| Box::new(mem_forget::MemForget));
 +    store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq));
 +    store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
 +    store.register_late_pass(|_| Box::new(missing_doc::MissingDoc::new()));
 +    store.register_late_pass(|_| Box::new(missing_inline::MissingInline));
 +    store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems));
 +    store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk));
 +    store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl));
 +    store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount));
 +    let enum_variant_size_threshold = conf.enum_variant_size_threshold;
 +    store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)));
 +    store.register_late_pass(|_| Box::new(explicit_write::ExplicitWrite));
 +    store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue));
 +    let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new(
 +        conf.trivial_copy_size_limit,
 +        conf.pass_by_value_size_limit,
 +        conf.avoid_breaking_exported_api,
 +        &sess.target,
 +    );
 +    store.register_late_pass(move |_| Box::new(pass_by_ref_or_value));
 +    store.register_late_pass(|_| Box::new(ref_option_ref::RefOptionRef));
 +    store.register_late_pass(|_| Box::new(infinite_iter::InfiniteIter));
 +    store.register_late_pass(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody));
 +    store.register_late_pass(|_| Box::<useless_conversion::UselessConversion>::default());
 +    store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher));
 +    store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom));
 +    store.register_late_pass(|_| Box::new(question_mark::QuestionMark));
 +    store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
 +    store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl));
 +    store.register_late_pass(|_| Box::new(map_unit_fn::MapUnit));
 +    store.register_late_pass(|_| Box::new(inherent_impl::MultipleInherentImpl));
 +    store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd));
 +    store.register_late_pass(|_| Box::new(unwrap::Unwrap));
 +    store.register_late_pass(|_| Box::new(indexing_slicing::IndexingSlicing));
 +    store.register_late_pass(|_| Box::new(non_copy_const::NonCopyConst));
 +    store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
 +    store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone));
 +    store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit));
 +    store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
 +    store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants));
 +    store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates));
 +    store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString));
 +    let max_trait_bounds = conf.max_trait_bounds;
 +    store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
 +    store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain));
 +    store.register_late_pass(|_| Box::new(mut_key::MutableKeyType));
 +    store.register_early_pass(|| Box::new(reference::DerefAddrOf));
 +    store.register_early_pass(|| Box::new(double_parens::DoubleParens));
 +    store.register_late_pass(|_| Box::new(format_impl::FormatImpl::new()));
 +    store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
 +    store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse));
 +    store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne));
 +    store.register_early_pass(|| Box::new(formatting::Formatting));
 +    store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints));
 +    store.register_early_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
 +    store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall));
 +    store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
 +    store.register_late_pass(|_| Box::new(returns::Return));
 +    store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf));
 +    store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements));
 +    store.register_early_pass(|| Box::new(precedence::Precedence));
 +    store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals));
 +    store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue));
 +    store.register_early_pass(|| Box::new(redundant_else::RedundantElse));
 +    store.register_late_pass(|_| Box::new(create_dir::CreateDir));
 +    store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType));
 +    let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions;
 +    store.register_early_pass(move || {
 +        Box::new(literal_representation::LiteralDigitGrouping::new(
 +            literal_representation_lint_fraction_readability,
 +        ))
 +    });
 +    let literal_representation_threshold = conf.literal_representation_threshold;
 +    store.register_early_pass(move || {
 +        Box::new(literal_representation::DecimalLiteralRepresentation::new(
 +            literal_representation_threshold,
 +        ))
 +    });
 +    let enum_variant_name_threshold = conf.enum_variant_name_threshold;
 +    store.register_late_pass(move |_| {
 +        Box::new(enum_variants::EnumVariantNames::new(
 +            enum_variant_name_threshold,
 +            avoid_breaking_exported_api,
 +        ))
 +    });
 +    store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
 +    let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive;
 +    store.register_late_pass(move |_| {
 +        Box::new(upper_case_acronyms::UpperCaseAcronyms::new(
 +            avoid_breaking_exported_api,
 +            upper_case_acronyms_aggressive,
 +        ))
 +    });
 +    store.register_late_pass(|_| Box::<default::Default>::default());
 +    store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(avoid_breaking_exported_api)));
 +    store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
 +    store.register_late_pass(|_| Box::new(exit::Exit));
 +    store.register_late_pass(|_| Box::new(to_digit_is_some::ToDigitIsSome));
 +    let array_size_threshold = conf.array_size_threshold;
 +    store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold)));
 +    store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold)));
 +    store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic));
 +    store.register_early_pass(|| Box::new(as_conversions::AsConversions));
 +    store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore));
 +    store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports));
 +    let max_fn_params_bools = conf.max_fn_params_bools;
 +    let max_struct_bools = conf.max_struct_bools;
 +    store.register_early_pass(move || {
 +        Box::new(excessive_bools::ExcessiveBools::new(
 +            max_struct_bools,
 +            max_fn_params_bools,
 +        ))
 +    });
 +    store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
 +    let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
 +    store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
 +    store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default());
 +    store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress));
 +    store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv)));
 +    store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
 +    store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
 +    store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex));
 +    store.register_late_pass(|_| Box::new(if_not_else::IfNotElse));
 +    store.register_late_pass(|_| Box::new(equatable_if_let::PatternEquality));
 +    store.register_late_pass(|_| Box::new(manual_async_fn::ManualAsyncFn));
 +    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::<macro_use::MacroUseImports>::default());
 +    store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch));
 +    store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult));
 +    store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
 +    store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync));
 +    let disallowed_macros = conf.disallowed_macros.clone();
 +    store.register_late_pass(move |_| Box::new(disallowed_macros::DisallowedMacros::new(disallowed_macros.clone())));
 +    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::<vec_init_then_push::VecInitThenPush>::default());
 +    store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing));
 +    store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10));
 +    store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
 +    store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison));
 +    store.register_early_pass(move || Box::new(module_style::ModStyle));
 +    store.register_late_pass(|_| Box::new(unused_async::UnusedAsync));
 +    let disallowed_types = conf.disallowed_types.clone();
 +    store.register_late_pass(move |_| Box::new(disallowed_types::DisallowedTypes::new(disallowed_types.clone())));
 +    let import_renames = conf.enforced_import_renames.clone();
 +    store.register_late_pass(move |_| {
 +        Box::new(missing_enforced_import_rename::ImportRename::new(
 +            import_renames.clone(),
 +        ))
 +    });
 +    let scripts = conf.allowed_scripts.clone();
 +    store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(&scripts)));
 +    store.register_late_pass(|_| Box::new(strlen_on_c_strings::StrlenOnCStrings));
 +    store.register_late_pass(move |_| Box::new(self_named_constructors::SelfNamedConstructors));
 +    store.register_late_pass(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator));
 +    store.register_late_pass(move |_| Box::new(manual_assert::ManualAssert));
 +    let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send;
 +    store.register_late_pass(move |_| {
 +        Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(
 +            enable_raw_pointer_heuristic_for_send,
 +        ))
 +    });
 +    store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks));
 +    store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv)));
 +    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(manual_bits::ManualBits::new(msrv)));
 +    store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation));
 +    store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
 +    let allow_dbg_in_tests = conf.allow_dbg_in_tests;
 +    store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
 +    let cargo_ignore_publish = conf.cargo_ignore_publish;
 +    store.register_late_pass(move |_| {
 +        Box::new(cargo::Cargo {
 +            ignore_publish: cargo_ignore_publish,
 +        })
 +    });
 +    store.register_late_pass(|_| Box::<write::Write>::default());
 +    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));
 +    let max_include_file_size = conf.max_include_file_size;
 +    store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
 +    store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace));
 +    store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
 +    store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default());
 +    store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
 +    store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
 +    store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef));
 +    store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch));
 +    store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec));
 +    store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
 +    store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv)));
 +    let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
 +    store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
 +    store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
 +    store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default());
 +    store.register_late_pass(|_| Box::new(manual_instant_elapsed::ManualInstantElapsed));
 +    store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
 +    store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv)));
 +    store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew));
 +    store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable));
 +    store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
 +    store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf));
 +    store.register_late_pass(|_| Box::new(box_default::BoxDefault));
 +    store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd));
++    store.register_early_pass(|| Box::new(partial_pub_fields::PartialPubFields));
++    store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods));
 +    // 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 00cfc6d49f19a7f3043a70ff8d1f7760471466cd,0000000000000000000000000000000000000000..27ba27202bf7e8b6ec2c764d51c133c434d6593a
mode 100644,000000..100644
--- /dev/null
@@@ -1,382 -1,0 +1,393 @@@
- use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq};
 +use super::NEEDLESS_RANGE_LOOP;
 +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::has_iter_method;
 +use clippy_utils::visitors::is_local_used;
-                         let extent = self.cx
++use clippy_utils::{contains_name, higher, is_integer_const, sugg, SpanlessEq};
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{BinOpKind, BorrowKind, Closure, Expr, ExprKind, HirId, Mutability, Pat, PatKind, QPath};
 +use rustc_lint::LateContext;
 +use rustc_middle::middle::region;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_span::symbol::{sym, Symbol};
 +use std::iter::{self, Iterator};
 +use std::mem;
 +
 +/// Checks for looping over a range and then indexing a sequence with it.
 +/// The iteratee must be a range literal.
 +#[expect(clippy::too_many_lines)]
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    pat: &'tcx Pat<'_>,
 +    arg: &'tcx Expr<'_>,
 +    body: &'tcx Expr<'_>,
 +    expr: &'tcx Expr<'_>,
 +) {
 +    if let Some(higher::Range {
 +        start: Some(start),
 +        ref end,
 +        limits,
 +    }) = higher::Range::hir(arg)
 +    {
 +        // the var must be a single name
 +        if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind {
 +            let mut visitor = VarVisitor {
 +                cx,
 +                var: canonical_id,
 +                indexed_mut: FxHashSet::default(),
 +                indexed_indirectly: FxHashMap::default(),
 +                indexed_directly: FxHashMap::default(),
 +                referenced: FxHashSet::default(),
 +                nonindex: false,
 +                prefer_mutable: false,
 +            };
 +            walk_expr(&mut visitor, body);
 +
 +            // linting condition: we only indexed one variable, and indexed it directly
 +            if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 {
 +                let (indexed, (indexed_extent, indexed_ty)) = visitor
 +                    .indexed_directly
 +                    .into_iter()
 +                    .next()
 +                    .expect("already checked that we have exactly 1 element");
 +
 +                // ensure that the indexed variable was declared before the loop, see #601
 +                if let Some(indexed_extent) = indexed_extent {
 +                    let parent_def_id = cx.tcx.hir().get_parent_item(expr.hir_id);
 +                    let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id);
 +                    let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id).unwrap();
 +                    if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) {
 +                        return;
 +                    }
 +                }
 +
 +                // don't lint if the container that is indexed does not have .iter() method
 +                let has_iter = has_iter_method(cx, indexed_ty);
 +                if has_iter.is_none() {
 +                    return;
 +                }
 +
 +                // don't lint if the container that is indexed into is also used without
 +                // indexing
 +                if visitor.referenced.contains(&indexed) {
 +                    return;
 +                }
 +
 +                let starts_at_zero = is_integer_const(cx, start, 0);
 +
 +                let skip = if starts_at_zero {
 +                    String::new()
 +                } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) {
 +                    return;
 +                } else {
 +                    format!(".skip({})", snippet(cx, start.span, ".."))
 +                };
 +
 +                let mut end_is_start_plus_val = false;
 +
 +                let take = if let Some(end) = *end {
 +                    let mut take_expr = end;
 +
 +                    if let ExprKind::Binary(ref op, left, right) = end.kind {
 +                        if op.node == BinOpKind::Add {
 +                            let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left);
 +                            let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right);
 +
 +                            if start_equal_left {
 +                                take_expr = right;
 +                            } else if start_equal_right {
 +                                take_expr = left;
 +                            }
 +
 +                            end_is_start_plus_val = start_equal_left | start_equal_right;
 +                        }
 +                    }
 +
 +                    if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) {
 +                        String::new()
 +                    } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) {
 +                        return;
 +                    } else {
 +                        match limits {
 +                            ast::RangeLimits::Closed => {
 +                                let take_expr = sugg::Sugg::hir(cx, take_expr, "<count>");
 +                                format!(".take({})", take_expr + sugg::ONE)
 +                            },
 +                            ast::RangeLimits::HalfOpen => {
 +                                format!(".take({})", snippet(cx, take_expr.span, ".."))
 +                            },
 +                        }
 +                    }
 +                } else {
 +                    String::new()
 +                };
 +
 +                let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) {
 +                    ("mut ", "iter_mut")
 +                } else {
 +                    ("", "iter")
 +                };
 +
 +                let take_is_empty = take.is_empty();
 +                let mut method_1 = take;
 +                let mut method_2 = skip;
 +
 +                if end_is_start_plus_val {
 +                    mem::swap(&mut method_1, &mut method_2);
 +                }
 +
 +                if visitor.nonindex {
 +                    span_lint_and_then(
 +                        cx,
 +                        NEEDLESS_RANGE_LOOP,
 +                        arg.span,
 +                        &format!("the loop variable `{}` is used to index `{indexed}`", ident.name),
 +                        |diag| {
 +                            multispan_sugg(
 +                                diag,
 +                                "consider using an iterator",
 +                                vec![
 +                                    (pat.span, format!("({}, <item>)", ident.name)),
 +                                    (
 +                                        arg.span,
 +                                        format!("{indexed}.{method}().enumerate(){method_1}{method_2}"),
 +                                    ),
 +                                ],
 +                            );
 +                        },
 +                    );
 +                } else {
 +                    let repl = if starts_at_zero && take_is_empty {
 +                        format!("&{ref_mut}{indexed}")
 +                    } else {
 +                        format!("{indexed}.{method}(){method_1}{method_2}")
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        NEEDLESS_RANGE_LOOP,
 +                        arg.span,
 +                        &format!("the loop variable `{}` is only used to index `{indexed}`", ident.name),
 +                        |diag| {
 +                            multispan_sugg(
 +                                diag,
 +                                "consider using an iterator",
 +                                vec![(pat.span, "<item>".to_string()), (arg.span, repl)],
 +                            );
 +                        },
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool {
 +    if_chain! {
 +        if let ExprKind::MethodCall(method, recv, [], _) = expr.kind;
 +        if method.ident.name == sym::len;
 +        if let ExprKind::Path(QPath::Resolved(_, path)) = recv.kind;
 +        if path.segments.len() == 1;
 +        if path.segments[0].ident.name == var;
 +        then {
 +            return true;
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn is_end_eq_array_len<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    end: &Expr<'_>,
 +    limits: ast::RangeLimits,
 +    indexed_ty: Ty<'tcx>,
 +) -> bool {
 +    if_chain! {
 +        if let ExprKind::Lit(ref lit) = end.kind;
 +        if let ast::LitKind::Int(end_int, _) = lit.node;
 +        if let ty::Array(_, arr_len_const) = indexed_ty.kind();
 +        if let Some(arr_len) = arr_len_const.try_eval_usize(cx.tcx, cx.param_env);
 +        then {
 +            return match limits {
 +                ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(),
 +                ast::RangeLimits::HalfOpen => end_int >= arr_len.into(),
 +            };
 +        }
 +    }
 +
 +    false
 +}
 +
 +struct VarVisitor<'a, 'tcx> {
 +    /// context reference
 +    cx: &'a LateContext<'tcx>,
 +    /// var name to look for as index
 +    var: HirId,
 +    /// indexed variables that are used mutably
 +    indexed_mut: FxHashSet<Symbol>,
 +    /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global
 +    indexed_indirectly: FxHashMap<Symbol, Option<region::Scope>>,
 +    /// subset of `indexed` of vars that are indexed directly: `v[i]`
 +    /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
 +    indexed_directly: FxHashMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>,
 +    /// Any names that are used outside an index operation.
 +    /// Used to detect things like `&mut vec` used together with `vec[i]`
 +    referenced: FxHashSet<Symbol>,
 +    /// has the loop variable been used in expressions other than the index of
 +    /// an index op?
 +    nonindex: bool,
 +    /// Whether we are inside the `$` in `&mut $` or `$ = foo` or `$.bar`, where bar
 +    /// takes `&mut self`
 +    prefer_mutable: bool,
 +}
 +
 +impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
 +    fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
 +        if_chain! {
 +            // the indexed container is referenced by a name
 +            if let ExprKind::Path(ref seqpath) = seqexpr.kind;
 +            if let QPath::Resolved(None, seqvar) = *seqpath;
 +            if seqvar.segments.len() == 1;
 +            if is_local_used(self.cx, idx, self.var);
 +            then {
 +                if self.prefer_mutable {
 +                    self.indexed_mut.insert(seqvar.segments[0].ident.name);
 +                }
 +                let index_used_directly = matches!(idx.kind, ExprKind::Path(_));
 +                let res = self.cx.qpath_res(seqpath, seqexpr.hir_id);
 +                match res {
 +                    Res::Local(hir_id) => {
 +                        let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id);
-                             self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
++                        let extent = self
++                            .cx
 +                            .tcx
 +                            .region_scope_tree(parent_def_id)
 +                            .var_scope(hir_id.local_id)
 +                            .unwrap();
 +                        if index_used_directly {
 +                            self.indexed_directly.insert(
 +                                seqvar.segments[0].ident.name,
 +                                (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)),
 +                            );
 +                        } else {
-                         return false;  // no need to walk further *on the variable*
-                     }
-                     Res::Def(DefKind::Static (_)| DefKind::Const, ..) => {
++                            self.indexed_indirectly
++                                .insert(seqvar.segments[0].ident.name, Some(extent));
 +                        }
-                         return false;  // no need to walk further *on the variable*
-                     }
++                        return false; // no need to walk further *on the variable*
++                    },
++                    Res::Def(DefKind::Static(_) | DefKind::Const, ..) => {
 +                        if index_used_directly {
 +                            self.indexed_directly.insert(
 +                                seqvar.segments[0].ident.name,
 +                                (None, self.cx.typeck_results().node_type(seqexpr.hir_id)),
 +                            );
 +                        } else {
 +                            self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
 +                        }
-             if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX))
-                 || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT));
++                        return false; // no need to walk further *on the variable*
++                    },
 +                    _ => (),
 +                }
 +            }
 +        }
 +        true
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            // a range index op
 +            if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind;
-             then { return }
++            if let Some(trait_id) = self
++                .cx
++                .typeck_results()
++                .type_dependent_def_id(expr.hir_id)
++                .and_then(|def_id| self.cx.tcx.trait_of_item(def_id));
++            if (meth.ident.name == sym::index && self.cx.tcx.lang_items().index_trait() == Some(trait_id))
++                || (meth.ident.name == sym::index_mut && self.cx.tcx.lang_items().index_mut_trait() == Some(trait_id));
 +            if !self.check(args_1, args_0, expr);
-             then { return }
++            then {
++                return;
++            }
 +        }
 +
 +        if_chain! {
 +            // an index op
 +            if let ExprKind::Index(seqexpr, idx) = expr.kind;
 +            if !self.check(idx, seqexpr, expr);
++            then {
++                return;
++            }
 +        }
 +
 +        if_chain! {
 +            // directly using a variable
 +            if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind;
 +            if let Res::Local(local_id) = path.res;
 +            then {
 +                if local_id == self.var {
 +                    self.nonindex = true;
 +                } else {
 +                    // not the correct variable, but still a variable
 +                    self.referenced.insert(path.segments[0].ident.name);
 +                }
 +            }
 +        }
 +
 +        let old = self.prefer_mutable;
 +        match expr.kind {
 +            ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => {
 +                self.prefer_mutable = true;
 +                self.visit_expr(lhs);
 +                self.prefer_mutable = false;
 +                self.visit_expr(rhs);
 +            },
 +            ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => {
 +                if mutbl == Mutability::Mut {
 +                    self.prefer_mutable = true;
 +                }
 +                self.visit_expr(expr);
 +            },
 +            ExprKind::Call(f, args) => {
 +                self.visit_expr(f);
 +                for expr in args {
 +                    let ty = self.cx.typeck_results().expr_ty_adjusted(expr);
 +                    self.prefer_mutable = false;
 +                    if let ty::Ref(_, _, mutbl) = *ty.kind() {
 +                        if mutbl == Mutability::Mut {
 +                            self.prefer_mutable = true;
 +                        }
 +                    }
 +                    self.visit_expr(expr);
 +                }
 +            },
 +            ExprKind::MethodCall(_, receiver, args, _) => {
 +                let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
 +                for (ty, expr) in iter::zip(
 +                    self.cx.tcx.fn_sig(def_id).inputs().skip_binder(),
 +                    std::iter::once(receiver).chain(args.iter()),
 +                ) {
 +                    self.prefer_mutable = false;
 +                    if let ty::Ref(_, _, mutbl) = *ty.kind() {
 +                        if mutbl == Mutability::Mut {
 +                            self.prefer_mutable = true;
 +                        }
 +                    }
 +                    self.visit_expr(expr);
 +                }
 +            },
 +            ExprKind::Closure(&Closure { body, .. }) => {
 +                let body = self.cx.tcx.hir().body(body);
 +                self.visit_expr(body.value);
 +            },
 +            _ => walk_expr(self, expr),
 +        }
 +        self.prefer_mutable = old;
 +    }
 +}
index 153f97e4e66c88ffe334d220c0d8425cf6589796,0000000000000000000000000000000000000000..55989f8a4465018f3fe9f5bbf3e0d782ece39959
mode 100644,000000..100644
--- /dev/null
@@@ -1,360 -1,0 +1,359 @@@
-         let local_id = match iter_expr.path {
-             Res::Local(id) => id,
-             _ => return true,
 +use super::WHILE_LET_ON_ITERATOR;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{
 +    get_enclosing_loop_or_multi_call_closure, is_refutable, is_res_lang_ctor, is_trait_method, visitors::is_res_used,
 +};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{def::Res, Closure, Expr, ExprKind, HirId, LangItem, Local, Mutability, PatKind, UnOp};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::nested_filter::OnlyBodies;
 +use rustc_middle::ty::adjustment::Adjust;
 +use rustc_span::{symbol::sym, Symbol};
 +
 +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +    let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! {
 +        if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr);
 +        // check for `Some(..)` pattern
 +        if let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind;
 +        if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome);
 +        // check for call to `Iterator::next`
 +        if let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind;
 +        if method_name.ident.name == sym::next;
 +        if is_trait_method(cx, let_expr, sym::Iterator);
 +        if let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr);
 +        // get the loop containing the match expression
 +        if !uses_iter(cx, &iter_expr_struct, if_then);
 +        then {
 +            (let_expr, iter_expr_struct, iter_expr, some_pat, expr)
 +        } else {
 +            return;
 +        }
 +    };
 +
 +    let mut applicability = Applicability::MachineApplicable;
 +    let loop_var = if let Some(some_pat) = some_pat.first() {
 +        if is_refutable(cx, some_pat) {
 +            // Refutable patterns don't work with for loops.
 +            return;
 +        }
 +        snippet_with_applicability(cx, some_pat.span, "..", &mut applicability)
 +    } else {
 +        "_".into()
 +    };
 +
 +    // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
 +    // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
 +    // afterwards a mutable borrow of a field isn't necessary.
 +    let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut)
 +        || !iter_expr_struct.can_move
 +        || !iter_expr_struct.fields.is_empty()
 +        || needs_mutable_borrow(cx, &iter_expr_struct, loop_expr)
 +    {
 +        ".by_ref()"
 +    } else {
 +        ""
 +    };
 +
 +    let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability);
 +    span_lint_and_sugg(
 +        cx,
 +        WHILE_LET_ON_ITERATOR,
 +        expr.span.with_hi(scrutinee_expr.span.hi()),
 +        "this loop could be written as a `for` loop",
 +        "try",
 +        format!("for {loop_var} in {iterator}{by_ref}"),
 +        applicability,
 +    );
 +}
 +
 +#[derive(Debug)]
 +struct IterExpr {
 +    /// The fields used, in order of child to parent.
 +    fields: Vec<Symbol>,
 +    /// The path being used.
 +    path: Res,
 +    /// Whether or not the iterator can be moved.
 +    can_move: bool,
 +}
 +
 +/// Parses any expression to find out which field of which variable is used. Will return `None` if
 +/// the expression might have side effects.
 +fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> {
 +    let mut fields = Vec::new();
 +    let mut can_move = true;
 +    loop {
 +        if cx
 +            .typeck_results()
 +            .expr_adjustments(e)
 +            .iter()
 +            .any(|a| matches!(a.kind, Adjust::Deref(Some(..))))
 +        {
 +            // Custom deref impls need to borrow the whole value as it's captured by reference
 +            can_move = false;
 +            fields.clear();
 +        }
 +        match e.kind {
 +            ExprKind::Path(ref path) => {
 +                break Some(IterExpr {
 +                    fields,
 +                    path: cx.qpath_res(path, e.hir_id),
 +                    can_move,
 +                });
 +            },
 +            ExprKind::Field(base, name) => {
 +                fields.push(name.name);
 +                e = base;
 +            },
 +            // Dereferencing a pointer has no side effects and doesn't affect which field is being used.
 +            ExprKind::Unary(UnOp::Deref, base) if cx.typeck_results().expr_ty(base).is_ref() => e = base,
 +
 +            // Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have
 +            // already been seen.
 +            ExprKind::Index(base, idx) if !idx.can_have_side_effects() => {
 +                can_move = false;
 +                fields.clear();
 +                e = base;
 +            },
 +            ExprKind::Unary(UnOp::Deref, base) => {
 +                can_move = false;
 +                fields.clear();
 +                e = base;
 +            },
 +
 +            // No effect and doesn't affect which field is being used.
 +            ExprKind::DropTemps(base) | ExprKind::AddrOf(_, _, base) | ExprKind::Type(base, _) => e = base,
 +            _ => break None,
 +        }
 +    }
 +}
 +
 +fn is_expr_same_field(cx: &LateContext<'_>, mut e: &Expr<'_>, mut fields: &[Symbol], path_res: Res) -> bool {
 +    loop {
 +        match (&e.kind, fields) {
 +            (&ExprKind::Field(base, name), [head_field, tail_fields @ ..]) if name.name == *head_field => {
 +                e = base;
 +                fields = tail_fields;
 +            },
 +            (ExprKind::Path(path), []) => {
 +                break cx.qpath_res(path, e.hir_id) == path_res;
 +            },
 +            (&(ExprKind::DropTemps(base) | ExprKind::AddrOf(_, _, base) | ExprKind::Type(base, _)), _) => e = base,
 +            _ => break false,
 +        }
 +    }
 +}
 +
 +/// Checks if the given expression is the same field as, is a child of, or is the parent of the
 +/// given field. Used to check if the expression can be used while the given field is borrowed
 +/// mutably. e.g. if checking for `x.y`, then `x.y`, `x.y.z`, and `x` will all return true, but
 +/// `x.z`, and `y` will return false.
 +fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fields: &[Symbol], path_res: Res) -> bool {
 +    match expr.kind {
 +        ExprKind::Field(base, name) => {
 +            if let Some((head_field, tail_fields)) = fields.split_first() {
 +                if name.name == *head_field && is_expr_same_field(cx, base, tail_fields, path_res) {
 +                    return true;
 +                }
 +                // Check if the expression is a parent field
 +                let mut fields_iter = tail_fields.iter();
 +                while let Some(field) = fields_iter.next() {
 +                    if *field == name.name && is_expr_same_field(cx, base, fields_iter.as_slice(), path_res) {
 +                        return true;
 +                    }
 +                }
 +            }
 +
 +            // Check if the expression is a child field.
 +            let mut e = base;
 +            loop {
 +                match e.kind {
 +                    ExprKind::Field(..) if is_expr_same_field(cx, e, fields, path_res) => break true,
 +                    ExprKind::Field(base, _) | ExprKind::DropTemps(base) | ExprKind::Type(base, _) => e = base,
 +                    ExprKind::Path(ref path) if fields.is_empty() => {
 +                        break cx.qpath_res(path, e.hir_id) == path_res;
 +                    },
 +                    _ => break false,
 +                }
 +            }
 +        },
 +        // If the path matches, this is either an exact match, or the expression is a parent of the field.
 +        ExprKind::Path(ref path) => cx.qpath_res(path, expr.hir_id) == path_res,
 +        ExprKind::DropTemps(base) | ExprKind::Type(base, _) | ExprKind::AddrOf(_, _, base) => {
 +            is_expr_same_child_or_parent_field(cx, base, fields, path_res)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +/// Strips off all field and path expressions. This will return true if a field or path has been
 +/// skipped. Used to skip them after failing to check for equality.
 +fn skip_fields_and_path<'tcx>(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) {
 +    let mut e = expr;
 +    let e = loop {
 +        match e.kind {
 +            ExprKind::Field(base, _) | ExprKind::DropTemps(base) | ExprKind::Type(base, _) => e = base,
 +            ExprKind::Path(_) => return (None, true),
 +            _ => break e,
 +        }
 +    };
 +    (Some(e), e.hir_id != expr.hir_id)
 +}
 +
 +/// Checks if the given expression uses the iterator.
 +fn uses_iter<'tcx>(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool {
 +    struct V<'a, 'b, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        iter_expr: &'b IterExpr,
 +        uses_iter: bool,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, '_, 'tcx> {
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.uses_iter {
 +                // return
 +            } else if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
 +                self.uses_iter = true;
 +            } else if let (e, true) = skip_fields_and_path(e) {
 +                if let Some(e) = e {
 +                    self.visit_expr(e);
 +                }
 +            } else if let ExprKind::Closure(&Closure { body: id, .. }) = e.kind {
 +                if is_res_used(self.cx, self.iter_expr.path, id) {
 +                    self.uses_iter = true;
 +                }
 +            } else {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +
 +    let mut v = V {
 +        cx,
 +        iter_expr,
 +        uses_iter: false,
 +    };
 +    v.visit_expr(container);
 +    v.uses_iter
 +}
 +
 +#[expect(clippy::too_many_lines)]
 +fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &Expr<'_>) -> bool {
 +    struct AfterLoopVisitor<'a, 'b, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        iter_expr: &'b IterExpr,
 +        loop_id: HirId,
 +        after_loop: bool,
 +        used_iter: bool,
 +    }
 +    impl<'tcx> Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> {
 +        type NestedFilter = OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.used_iter {
 +                return;
 +            }
 +            if self.after_loop {
 +                if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
 +                    self.used_iter = true;
 +                } else if let (e, true) = skip_fields_and_path(e) {
 +                    if let Some(e) = e {
 +                        self.visit_expr(e);
 +                    }
 +                } else if let ExprKind::Closure(&Closure { body: id, .. }) = e.kind {
 +                    self.used_iter = is_res_used(self.cx, self.iter_expr.path, id);
 +                } else {
 +                    walk_expr(self, e);
 +                }
 +            } else if self.loop_id == e.hir_id {
 +                self.after_loop = true;
 +            } else {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +
 +    struct NestedLoopVisitor<'a, 'b, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        iter_expr: &'b IterExpr,
 +        local_id: HirId,
 +        loop_id: HirId,
 +        after_loop: bool,
 +        found_local: bool,
 +        used_after: bool,
 +    }
 +    impl<'a, 'b, 'tcx> Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
 +        type NestedFilter = OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_local(&mut self, l: &'tcx Local<'_>) {
 +            if !self.after_loop {
 +                l.pat.each_binding_or_first(&mut |_, id, _, _| {
 +                    if id == self.local_id {
 +                        self.found_local = true;
 +                    }
 +                });
 +            }
 +            if let Some(e) = l.init {
 +                self.visit_expr(e);
 +            }
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.used_after {
 +                return;
 +            }
 +            if self.after_loop {
 +                if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
 +                    self.used_after = true;
 +                } else if let (e, true) = skip_fields_and_path(e) {
 +                    if let Some(e) = e {
 +                        self.visit_expr(e);
 +                    }
 +                } else if let ExprKind::Closure(&Closure { body: id, .. }) = e.kind {
 +                    self.used_after = is_res_used(self.cx, self.iter_expr.path, id);
 +                } else {
 +                    walk_expr(self, e);
 +                }
 +            } else if e.hir_id == self.loop_id {
 +                self.after_loop = true;
 +            } else {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +
 +    if let Some(e) = get_enclosing_loop_or_multi_call_closure(cx, loop_expr) {
++        let Res::Local(local_id) = iter_expr.path else {
++            return true
 +        };
 +        let mut v = NestedLoopVisitor {
 +            cx,
 +            iter_expr,
 +            local_id,
 +            loop_id: loop_expr.hir_id,
 +            after_loop: false,
 +            found_local: false,
 +            used_after: false,
 +        };
 +        v.visit_expr(e);
 +        v.used_after || !v.found_local
 +    } else {
 +        let mut v = AfterLoopVisitor {
 +            cx,
 +            iter_expr,
 +            loop_id: loop_expr.hir_id,
 +            after_loop: false,
 +            used_iter: false,
 +        };
 +        v.visit_expr(cx.tcx.hir().body(cx.enclosing_body.unwrap()).value);
 +        v.used_iter
 +    }
 +}
index 9a0a26c0991b3eb59dd9a50caf98308f2ca217d1,0000000000000000000000000000000000000000..090f9f8ff73cfd40e9825954d4b5c163ae6a6ccc
mode 100644,000000..100644
--- /dev/null
@@@ -1,202 -1,0 +1,208 @@@
- use clippy_utils::match_function_call;
- use clippy_utils::paths::FUTURE_FROM_GENERATOR;
 +use clippy_utils::diagnostics::span_lint_and_then;
-         if let TypeBindingKind::Equality{term: Term::Ty(output)} = binding.kind;
++use clippy_utils::match_function_call_with_def_id;
 +use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::FnKind;
 +use rustc_hir::{
 +    AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound,
 +    HirId, IsAsync, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// It checks for manual implementations of `async` functions.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's more idiomatic to use the dedicated syntax.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::future::Future;
 +    ///
 +    /// fn foo() -> impl Future<Output = i32> { async { 42 } }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// async fn foo() -> i32 { 42 }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub MANUAL_ASYNC_FN,
 +    style,
 +    "manual implementations of `async` functions can be simplified using the dedicated syntax"
 +}
 +
 +declare_lint_pass!(ManualAsyncFn => [MANUAL_ASYNC_FN]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: FnKind<'tcx>,
 +        decl: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        span: Span,
 +        _: HirId,
 +    ) {
 +        if_chain! {
 +            if let Some(header) = kind.header();
 +            if header.asyncness == IsAsync::NotAsync;
 +            // Check that this function returns `impl Future`
 +            if let FnRetTy::Return(ret_ty) = decl.output;
 +            if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty);
 +            if let Some(output) = future_output_ty(trait_ref);
 +            if captures_all_lifetimes(decl.inputs, &output_lifetimes);
 +            // Check that the body of the function consists of one async block
 +            if let ExprKind::Block(block, _) = body.value.kind;
 +            if block.stmts.is_empty();
 +            if let Some(closure_body) = desugared_async_block(cx, block);
 +            then {
 +                let header_span = span.with_hi(ret_ty.span.hi());
 +
 +                span_lint_and_then(
 +                    cx,
 +                    MANUAL_ASYNC_FN,
 +                    header_span,
 +                    "this function can be simplified using the `async fn` syntax",
 +                    |diag| {
 +                        if_chain! {
 +                            if let Some(header_snip) = snippet_opt(cx, header_span);
 +                            if let Some(ret_pos) = position_before_rarrow(&header_snip);
 +                            if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output);
 +                            then {
 +                                let help = format!("make the function `async` and {ret_sugg}");
 +                                diag.span_suggestion(
 +                                    header_span,
 +                                    &help,
 +                                    format!("async {}{ret_snip}", &header_snip[..ret_pos]),
 +                                    Applicability::MachineApplicable
 +                                );
 +
 +                                let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span));
 +                                diag.span_suggestion(
 +                                    block.span,
 +                                    "move the body of the async block to the enclosing function",
 +                                    body_snip,
 +                                    Applicability::MachineApplicable
 +                                );
 +                            }
 +                        }
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn future_trait_ref<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: &'tcx Ty<'tcx>,
 +) -> Option<(&'tcx TraitRef<'tcx>, Vec<LifetimeName>)> {
 +    if_chain! {
 +        if let TyKind::OpaqueDef(item_id, bounds, false) = ty.kind;
 +        let item = cx.tcx.hir().item(item_id);
 +        if let ItemKind::OpaqueTy(opaque) = &item.kind;
 +        if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| {
 +            if let GenericBound::Trait(poly, _) = bound {
 +                Some(&poly.trait_ref)
 +            } else {
 +                None
 +            }
 +        });
 +        if trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait();
 +        then {
 +            let output_lifetimes = bounds
 +                .iter()
 +                .filter_map(|bound| {
 +                    if let GenericArg::Lifetime(lt) = bound {
 +                        Some(lt.name)
 +                    } else {
 +                        None
 +                    }
 +                })
 +                .collect();
 +
 +            return Some((trait_ref, output_lifetimes));
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'tcx>> {
 +    if_chain! {
 +        if let Some(segment) = trait_ref.path.segments.last();
 +        if let Some(args) = segment.args;
 +        if args.bindings.len() == 1;
 +        let binding = &args.bindings[0];
 +        if binding.ident.name == sym::Output;
-             return Some(output)
++        if let TypeBindingKind::Equality { term: Term::Ty(output) } = binding.kind;
 +        then {
-         if let Some(args) = match_function_call(cx, block_expr, &FUTURE_FROM_GENERATOR);
++            return Some(output);
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) -> bool {
 +    let input_lifetimes: Vec<LifetimeName> = inputs
 +        .iter()
 +        .filter_map(|ty| {
 +            if let TyKind::Rptr(lt, _) = ty.kind {
 +                Some(lt.name)
 +            } else {
 +                None
 +            }
 +        })
 +        .collect();
 +
 +    // The lint should trigger in one of these cases:
 +    // - There are no input lifetimes
 +    // - There's only one output lifetime bound using `+ '_`
 +    // - All input lifetimes are explicitly bound to the output
 +    input_lifetimes.is_empty()
 +        || (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Infer))
 +        || input_lifetimes
 +            .iter()
 +            .all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt))
 +}
 +
 +fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> {
 +    if_chain! {
 +        if let Some(block_expr) = block.expr;
-         if let Expr{kind: ExprKind::Closure(&Closure { body, .. }), ..} = args[0];
++        if let Some(args) = cx
++            .tcx
++            .lang_items()
++            .from_generator_fn()
++            .and_then(|def_id| match_function_call_with_def_id(cx, block_expr, def_id));
 +        if args.len() == 1;
++        if let Expr {
++            kind: ExprKind::Closure(&Closure { body, .. }),
++            ..
++        } = args[0];
 +        let closure_body = cx.tcx.hir().body(body);
 +        if closure_body.generator_kind == Some(GeneratorKind::Async(AsyncGeneratorKind::Block));
 +        then {
 +            return Some(closure_body);
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str, String)> {
 +    match output.kind {
 +        TyKind::Tup(tys) if tys.is_empty() => {
 +            let sugg = "remove the return type";
 +            Some((sugg, String::new()))
 +        },
 +        _ => {
 +            let sugg = "return the output of the future directly";
 +            snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {snip}")))
 +        },
 +    }
 +}
index ece4df95505ceb2de65d5bb5ba0e03f7cdc4d7cc,0000000000000000000000000000000000000000..02dc8755dd61c9e86f23bbe209066680d964d5f0
mode 100644,000000..100644
--- /dev/null
@@@ -1,713 -1,0 +1,717 @@@
-     eq_expr_value, get_trait_def_id,
 +use itertools::Itertools;
 +use rustc_errors::Diagnostic;
 +use rustc_hir::{
 +    def::Res, Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::Ty;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{symbol::sym, Span};
 +use std::ops::Deref;
 +
 +use clippy_utils::{
 +    diagnostics::{span_lint_and_then, span_lint_hir_and_then},
-     is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, paths, peel_blocks,
++    eq_expr_value,
 +    higher::If,
-         } else if get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])) {
++    is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, peel_blocks,
 +    peel_blocks_with_stmt,
 +    sugg::Sugg,
 +    ty::implements_trait,
 +    visitors::is_const_evaluatable,
 +    MaybePath,
 +};
 +use rustc_errors::Applicability;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Identifies good opportunities for a clamp function from std or core, and suggests using it.
 +    ///
 +    /// ### Why is this bad?
 +    /// clamp is much shorter, easier to read, and doesn't use any control flow.
 +    ///
 +    /// ### Known issue(s)
 +    /// If the clamped variable is NaN this suggestion will cause the code to propagate NaN
 +    /// rather than returning either `max` or `min`.
 +    ///
 +    /// `clamp` functions will panic if `max < min`, `max.is_nan()`, or `min.is_nan()`.
 +    /// Some may consider panicking in these situations to be desirable, but it also may
 +    /// introduce panicking where there wasn't any before.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let (input, min, max) = (0, -2, 1);
 +    /// if input > max {
 +    ///     max
 +    /// } else if input < min {
 +    ///     min
 +    /// } else {
 +    ///     input
 +    /// }
 +    /// # ;
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # let (input, min, max) = (0, -2, 1);
 +    /// input.max(min).min(max)
 +    /// # ;
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # let (input, min, max) = (0, -2, 1);
 +    /// match input {
 +    ///     x if x > max => max,
 +    ///     x if x < min => min,
 +    ///     x => x,
 +    /// }
 +    /// # ;
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # let (input, min, max) = (0, -2, 1);
 +    /// let mut x = input;
 +    /// if x < min { x = min; }
 +    /// if x > max { x = max; }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let (input, min, max) = (0, -2, 1);
 +    /// input.clamp(min, max)
 +    /// # ;
 +    /// ```
 +    #[clippy::version = "1.66.0"]
 +    pub MANUAL_CLAMP,
 +    complexity,
 +    "using a clamp pattern instead of the clamp function"
 +}
 +impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]);
 +
 +pub struct ManualClamp {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl ManualClamp {
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +#[derive(Debug)]
 +struct ClampSuggestion<'tcx> {
 +    params: InputMinMax<'tcx>,
 +    span: Span,
 +    make_assignment: Option<&'tcx Expr<'tcx>>,
 +    hir_with_ignore_attr: Option<HirId>,
 +}
 +
 +#[derive(Debug)]
 +struct InputMinMax<'tcx> {
 +    input: &'tcx Expr<'tcx>,
 +    min: &'tcx Expr<'tcx>,
 +    max: &'tcx Expr<'tcx>,
 +    is_float: bool,
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for ManualClamp {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
 +        if !meets_msrv(self.msrv, msrvs::CLAMP) {
 +            return;
 +        }
 +        if !expr.span.from_expansion() {
 +            let suggestion = is_if_elseif_else_pattern(cx, expr)
 +                .or_else(|| is_max_min_pattern(cx, expr))
 +                .or_else(|| is_call_max_min_pattern(cx, expr))
 +                .or_else(|| is_match_pattern(cx, expr))
 +                .or_else(|| is_if_elseif_pattern(cx, expr));
 +            if let Some(suggestion) = suggestion {
 +                emit_suggestion(cx, &suggestion);
 +            }
 +        }
 +    }
 +
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
 +        if !meets_msrv(self.msrv, msrvs::CLAMP) {
 +            return;
 +        }
 +        for suggestion in is_two_if_pattern(cx, block) {
 +            emit_suggestion(cx, &suggestion);
 +        }
 +    }
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn emit_suggestion<'tcx>(cx: &LateContext<'tcx>, suggestion: &ClampSuggestion<'tcx>) {
 +    let ClampSuggestion {
 +        params: InputMinMax {
 +            input,
 +            min,
 +            max,
 +            is_float,
 +        },
 +        span,
 +        make_assignment,
 +        hir_with_ignore_attr,
 +    } = suggestion;
 +    let input = Sugg::hir(cx, input, "..").maybe_par();
 +    let min = Sugg::hir(cx, min, "..");
 +    let max = Sugg::hir(cx, max, "..");
 +    let semicolon = if make_assignment.is_some() { ";" } else { "" };
 +    let assignment = if let Some(assignment) = make_assignment {
 +        let assignment = Sugg::hir(cx, assignment, "..");
 +        format!("{assignment} = ")
 +    } else {
 +        String::new()
 +    };
 +    let suggestion = format!("{assignment}{input}.clamp({min}, {max}){semicolon}");
 +    let msg = "clamp-like pattern without using clamp function";
 +    let lint_builder = |d: &mut Diagnostic| {
 +        d.span_suggestion(*span, "replace with clamp", suggestion, Applicability::MaybeIncorrect);
 +        if *is_float {
 +            d.note("clamp will panic if max < min, min.is_nan(), or max.is_nan()")
 +                .note("clamp returns NaN if the input is NaN");
 +        } else {
 +            d.note("clamp will panic if max < min");
 +        }
 +    };
 +    if let Some(hir_id) = hir_with_ignore_attr {
 +        span_lint_hir_and_then(cx, MANUAL_CLAMP, *hir_id, *span, msg, lint_builder);
 +    } else {
 +        span_lint_and_then(cx, MANUAL_CLAMP, *span, msg, lint_builder);
 +    }
 +}
 +
 +#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 +enum TypeClampability {
 +    Float,
 +    Ord,
 +}
 +
 +impl TypeClampability {
 +    fn is_clampable<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<TypeClampability> {
 +        if ty.is_floating_point() {
 +            Some(TypeClampability::Float)
++        } else if cx
++            .tcx
++            .get_diagnostic_item(sym::Ord)
++            .map_or(false, |id| implements_trait(cx, ty, id, &[]))
++        {
 +            Some(TypeClampability::Ord)
 +        } else {
 +            None
 +        }
 +    }
 +
 +    fn is_float(self) -> bool {
 +        matches!(self, TypeClampability::Float)
 +    }
 +}
 +
 +/// Targets patterns like
 +///
 +/// ```
 +/// # let (input, min, max) = (0, -3, 12);
 +///
 +/// if input < min {
 +///     min
 +/// } else if input > max {
 +///     max
 +/// } else {
 +///     input
 +/// }
 +/// # ;
 +/// ```
 +fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<ClampSuggestion<'tcx>> {
 +    if let Some(If {
 +        cond,
 +        then,
 +        r#else: Some(else_if),
 +    }) = If::hir(expr)
 +    && let Some(If {
 +        cond: else_if_cond,
 +        then: else_if_then,
 +        r#else: Some(else_body),
 +    }) = If::hir(peel_blocks(else_if))
 +    {
 +        let params = is_clamp_meta_pattern(
 +            cx,
 +            &BinaryOp::new(peel_blocks(cond))?,
 +            &BinaryOp::new(peel_blocks(else_if_cond))?,
 +            peel_blocks(then),
 +            peel_blocks(else_if_then),
 +            None,
 +        )?;
 +        // Contents of the else should be the resolved input.
 +        if !eq_expr_value(cx, params.input, peel_blocks(else_body)) {
 +            return None;
 +        }
 +        Some(ClampSuggestion {
 +            params,
 +            span: expr.span,
 +            make_assignment: None,
 +            hir_with_ignore_attr: None,
 +        })
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Targets patterns like
 +///
 +/// ```
 +/// # let (input, min_value, max_value) = (0, -3, 12);
 +///
 +/// input.max(min_value).min(max_value)
 +/// # ;
 +/// ```
 +fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<ClampSuggestion<'tcx>> {
 +    if let ExprKind::MethodCall(seg_second, receiver, [arg_second], _) = &expr.kind
 +        && (cx.typeck_results().expr_ty_adjusted(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord))
 +        && let ExprKind::MethodCall(seg_first, input, [arg_first], _) = &receiver.kind
 +        && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() || is_trait_method(cx, receiver, sym::Ord))
 +    {
 +        let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point();
 +        let (min, max) = match (seg_first.ident.as_str(), seg_second.ident.as_str()) {
 +            ("min", "max") => (arg_second, arg_first),
 +            ("max", "min") => (arg_first, arg_second),
 +            _ => return None,
 +        };
 +        Some(ClampSuggestion {
 +            params: InputMinMax { input, min, max, is_float },
 +            span: expr.span,
 +            make_assignment: None,
 +            hir_with_ignore_attr: None,
 +        })
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Targets patterns like
 +///
 +/// ```
 +/// # let (input, min_value, max_value) = (0, -3, 12);
 +/// # use std::cmp::{max, min};
 +/// min(max(input, min_value), max_value)
 +/// # ;
 +/// ```
 +fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<ClampSuggestion<'tcx>> {
 +    fn segment<'tcx>(cx: &LateContext<'_>, func: &Expr<'tcx>) -> Option<FunctionType<'tcx>> {
 +        match func.kind {
 +            ExprKind::Path(QPath::Resolved(None, path)) => {
 +                let id = path.res.opt_def_id()?;
 +                match cx.tcx.get_diagnostic_name(id) {
 +                    Some(sym::cmp_min) => Some(FunctionType::CmpMin),
 +                    Some(sym::cmp_max) => Some(FunctionType::CmpMax),
 +                    _ if is_diag_trait_item(cx, id, sym::Ord) => {
 +                        Some(FunctionType::OrdOrFloat(path.segments.last().expect("infallible")))
 +                    },
 +                    _ => None,
 +                }
 +            },
 +            ExprKind::Path(QPath::TypeRelative(ty, seg)) => {
 +                matches!(path_res(cx, ty), Res::PrimTy(PrimTy::Float(_))).then(|| FunctionType::OrdOrFloat(seg))
 +            },
 +            _ => None,
 +        }
 +    }
 +
 +    enum FunctionType<'tcx> {
 +        CmpMin,
 +        CmpMax,
 +        OrdOrFloat(&'tcx PathSegment<'tcx>),
 +    }
 +
 +    fn check<'tcx>(
 +        cx: &LateContext<'tcx>,
 +        outer_fn: &'tcx Expr<'tcx>,
 +        inner_call: &'tcx Expr<'tcx>,
 +        outer_arg: &'tcx Expr<'tcx>,
 +        span: Span,
 +    ) -> Option<ClampSuggestion<'tcx>> {
 +        if let ExprKind::Call(inner_fn, [first, second]) = &inner_call.kind
 +            && let Some(inner_seg) = segment(cx, inner_fn)
 +            && let Some(outer_seg) = segment(cx, outer_fn)
 +        {
 +            let (input, inner_arg) = match (is_const_evaluatable(cx, first), is_const_evaluatable(cx, second)) {
 +                (true, false) => (second, first),
 +                (false, true) => (first, second),
 +                _ => return None,
 +            };
 +            let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point();
 +            let (min, max) = match (inner_seg, outer_seg) {
 +                (FunctionType::CmpMin, FunctionType::CmpMax) => (outer_arg, inner_arg),
 +                (FunctionType::CmpMax, FunctionType::CmpMin) => (inner_arg, outer_arg),
 +                (FunctionType::OrdOrFloat(first_segment), FunctionType::OrdOrFloat(second_segment)) => {
 +                    match (first_segment.ident.as_str(), second_segment.ident.as_str()) {
 +                        ("min", "max") => (outer_arg, inner_arg),
 +                        ("max", "min") => (inner_arg, outer_arg),
 +                        _ => return None,
 +                    }
 +                }
 +                _ => return None,
 +            };
 +            Some(ClampSuggestion {
 +                params: InputMinMax { input, min, max, is_float },
 +                span,
 +                make_assignment: None,
 +                hir_with_ignore_attr: None,
 +            })
 +        } else {
 +            None
 +        }
 +    }
 +
 +    if let ExprKind::Call(outer_fn, [first, second]) = &expr.kind {
 +        check(cx, outer_fn, first, second, expr.span).or_else(|| check(cx, outer_fn, second, first, expr.span))
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Targets patterns like
 +///
 +/// ```
 +/// # let (input, min, max) = (0, -3, 12);
 +///
 +/// match input {
 +///     input if input > max => max,
 +///     input if input < min => min,
 +///     input => input,
 +/// }
 +/// # ;
 +/// ```
 +fn is_match_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<ClampSuggestion<'tcx>> {
 +    if let ExprKind::Match(value, [first_arm, second_arm, last_arm], rustc_hir::MatchSource::Normal) = &expr.kind {
 +        // Find possible min/max branches
 +        let minmax_values = |a: &'tcx Arm<'tcx>| {
 +            if let PatKind::Binding(_, var_hir_id, _, None) = &a.pat.kind
 +            && let Some(Guard::If(e)) = a.guard {
 +                Some((e, var_hir_id, a.body))
 +            } else {
 +                None
 +            }
 +        };
 +        let (first, first_hir_id, first_expr) = minmax_values(first_arm)?;
 +        let (second, second_hir_id, second_expr) = minmax_values(second_arm)?;
 +        let first = BinaryOp::new(first)?;
 +        let second = BinaryOp::new(second)?;
 +        if let PatKind::Binding(_, binding, _, None) = &last_arm.pat.kind
 +            && path_to_local_id(peel_blocks_with_stmt(last_arm.body), *binding)
 +            && last_arm.guard.is_none()
 +        {
 +            // Proceed as normal
 +        } else {
 +            return None;
 +        }
 +        if let Some(params) = is_clamp_meta_pattern(
 +            cx,
 +            &first,
 +            &second,
 +            first_expr,
 +            second_expr,
 +            Some((*first_hir_id, *second_hir_id)),
 +        ) {
 +            return Some(ClampSuggestion {
 +                params: InputMinMax {
 +                    input: value,
 +                    min: params.min,
 +                    max: params.max,
 +                    is_float: params.is_float,
 +                },
 +                span: expr.span,
 +                make_assignment: None,
 +                hir_with_ignore_attr: None,
 +            });
 +        }
 +    }
 +    None
 +}
 +
 +/// Targets patterns like
 +///
 +/// ```
 +/// # let (input, min, max) = (0, -3, 12);
 +///
 +/// let mut x = input;
 +/// if x < min { x = min; }
 +/// if x > max { x = max; }
 +/// ```
 +fn is_two_if_pattern<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Vec<ClampSuggestion<'tcx>> {
 +    block_stmt_with_last(block)
 +        .tuple_windows()
 +        .filter_map(|(maybe_set_first, maybe_set_second)| {
 +            if let StmtKind::Expr(first_expr) = *maybe_set_first
 +                && let StmtKind::Expr(second_expr) = *maybe_set_second
 +                && let Some(If { cond: first_cond, then: first_then, r#else: None }) = If::hir(first_expr)
 +                && let Some(If { cond: second_cond, then: second_then, r#else: None }) = If::hir(second_expr)
 +                && let ExprKind::Assign(
 +                    maybe_input_first_path,
 +                    maybe_min_max_first,
 +                    _
 +                ) = peel_blocks_with_stmt(first_then).kind
 +                && let ExprKind::Assign(
 +                    maybe_input_second_path,
 +                    maybe_min_max_second,
 +                    _
 +                ) = peel_blocks_with_stmt(second_then).kind
 +                && eq_expr_value(cx, maybe_input_first_path, maybe_input_second_path)
 +                && let Some(first_bin) = BinaryOp::new(first_cond)
 +                && let Some(second_bin) = BinaryOp::new(second_cond)
 +                && let Some(input_min_max) = is_clamp_meta_pattern(
 +                    cx,
 +                    &first_bin,
 +                    &second_bin,
 +                    maybe_min_max_first,
 +                    maybe_min_max_second,
 +                    None
 +                )
 +            {
 +                Some(ClampSuggestion {
 +                    params: InputMinMax {
 +                        input: maybe_input_first_path,
 +                        min: input_min_max.min,
 +                        max: input_min_max.max,
 +                        is_float: input_min_max.is_float,
 +                    },
 +                    span: first_expr.span.to(second_expr.span),
 +                    make_assignment: Some(maybe_input_first_path),
 +                    hir_with_ignore_attr: Some(first_expr.hir_id()),
 +                })
 +            } else {
 +                None
 +            }
 +        })
 +        .collect()
 +}
 +
 +/// Targets patterns like
 +///
 +/// ```
 +/// # let (mut input, min, max) = (0, -3, 12);
 +///
 +/// if input < min {
 +///     input = min;
 +/// } else if input > max {
 +///     input = max;
 +/// }
 +/// ```
 +fn is_if_elseif_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<ClampSuggestion<'tcx>> {
 +    if let Some(If {
 +        cond,
 +        then,
 +        r#else: Some(else_if),
 +    }) = If::hir(expr)
 +        && let Some(If {
 +            cond: else_if_cond,
 +            then: else_if_then,
 +            r#else: None,
 +        }) = If::hir(peel_blocks(else_if))
 +        && let ExprKind::Assign(
 +            maybe_input_first_path,
 +            maybe_min_max_first,
 +            _
 +        ) = peel_blocks_with_stmt(then).kind
 +        && let ExprKind::Assign(
 +            maybe_input_second_path,
 +            maybe_min_max_second,
 +            _
 +        ) = peel_blocks_with_stmt(else_if_then).kind
 +    {
 +        let params = is_clamp_meta_pattern(
 +            cx,
 +            &BinaryOp::new(peel_blocks(cond))?,
 +            &BinaryOp::new(peel_blocks(else_if_cond))?,
 +            peel_blocks(maybe_min_max_first),
 +            peel_blocks(maybe_min_max_second),
 +            None,
 +        )?;
 +        if !eq_expr_value(cx, maybe_input_first_path, maybe_input_second_path) {
 +            return None;
 +        }
 +        Some(ClampSuggestion {
 +            params,
 +            span: expr.span,
 +            make_assignment: Some(maybe_input_first_path),
 +            hir_with_ignore_attr: None,
 +        })
 +    } else {
 +        None
 +    }
 +}
 +
 +/// `ExprKind::Binary` but more narrowly typed
 +#[derive(Debug, Clone, Copy)]
 +struct BinaryOp<'tcx> {
 +    op: BinOpKind,
 +    left: &'tcx Expr<'tcx>,
 +    right: &'tcx Expr<'tcx>,
 +}
 +
 +impl<'tcx> BinaryOp<'tcx> {
 +    fn new(e: &'tcx Expr<'tcx>) -> Option<BinaryOp<'tcx>> {
 +        match &e.kind {
 +            ExprKind::Binary(op, left, right) => Some(BinaryOp {
 +                op: op.node,
 +                left,
 +                right,
 +            }),
 +            _ => None,
 +        }
 +    }
 +
 +    fn flip(&self) -> Self {
 +        Self {
 +            op: match self.op {
 +                BinOpKind::Le => BinOpKind::Ge,
 +                BinOpKind::Lt => BinOpKind::Gt,
 +                BinOpKind::Ge => BinOpKind::Le,
 +                BinOpKind::Gt => BinOpKind::Lt,
 +                other => other,
 +            },
 +            left: self.right,
 +            right: self.left,
 +        }
 +    }
 +}
 +
 +/// The clamp meta pattern is a pattern shared between many (but not all) patterns.
 +/// In summary, this pattern consists of two if statements that meet many criteria,
 +/// - binary operators that are one of [`>`, `<`, `>=`, `<=`].
 +/// - Both binary statements must have a shared argument
 +///     - Which can appear on the left or right side of either statement
 +///     - The binary operators must define a finite range for the shared argument. To put this in
 +///       the terms of Rust `std` library, the following ranges are acceptable
 +///         - `Range`
 +///         - `RangeInclusive`
 +///       And all other range types are not accepted. For the purposes of `clamp` it's irrelevant
 +///       whether the range is inclusive or not, the output is the same.
 +/// - The result of each if statement must be equal to the argument unique to that if statement. The
 +///   result can not be the shared argument in either case.
 +fn is_clamp_meta_pattern<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    first_bin: &BinaryOp<'tcx>,
 +    second_bin: &BinaryOp<'tcx>,
 +    first_expr: &'tcx Expr<'tcx>,
 +    second_expr: &'tcx Expr<'tcx>,
 +    // This parameters is exclusively for the match pattern.
 +    // It exists because the variable bindings used in that pattern
 +    // refer to the variable bound in the match arm, not the variable
 +    // bound outside of it. Fortunately due to context we know this has to
 +    // be the input variable, not the min or max.
 +    input_hir_ids: Option<(HirId, HirId)>,
 +) -> Option<InputMinMax<'tcx>> {
 +    fn check<'tcx>(
 +        cx: &LateContext<'tcx>,
 +        first_bin: &BinaryOp<'tcx>,
 +        second_bin: &BinaryOp<'tcx>,
 +        first_expr: &'tcx Expr<'tcx>,
 +        second_expr: &'tcx Expr<'tcx>,
 +        input_hir_ids: Option<(HirId, HirId)>,
 +        is_float: bool,
 +    ) -> Option<InputMinMax<'tcx>> {
 +        match (&first_bin.op, &second_bin.op) {
 +            (BinOpKind::Ge | BinOpKind::Gt, BinOpKind::Le | BinOpKind::Lt) => {
 +                let (min, max) = (second_expr, first_expr);
 +                let refers_to_input = match input_hir_ids {
 +                    Some((first_hir_id, second_hir_id)) => {
 +                        path_to_local_id(peel_blocks(first_bin.left), first_hir_id)
 +                            && path_to_local_id(peel_blocks(second_bin.left), second_hir_id)
 +                    },
 +                    None => eq_expr_value(cx, first_bin.left, second_bin.left),
 +                };
 +                (refers_to_input
 +                    && eq_expr_value(cx, first_bin.right, first_expr)
 +                    && eq_expr_value(cx, second_bin.right, second_expr))
 +                .then_some(InputMinMax {
 +                    input: first_bin.left,
 +                    min,
 +                    max,
 +                    is_float,
 +                })
 +            },
 +            _ => None,
 +        }
 +    }
 +    // First filter out any expressions with side effects
 +    let exprs = [
 +        first_bin.left,
 +        first_bin.right,
 +        second_bin.left,
 +        second_bin.right,
 +        first_expr,
 +        second_expr,
 +    ];
 +    let clampability = TypeClampability::is_clampable(cx, cx.typeck_results().expr_ty(first_expr))?;
 +    let is_float = clampability.is_float();
 +    if exprs.iter().any(|e| peel_blocks(e).can_have_side_effects()) {
 +        return None;
 +    }
 +    if !(is_ord_op(first_bin.op) && is_ord_op(second_bin.op)) {
 +        return None;
 +    }
 +    let cases = [
 +        (*first_bin, *second_bin),
 +        (first_bin.flip(), second_bin.flip()),
 +        (first_bin.flip(), *second_bin),
 +        (*first_bin, second_bin.flip()),
 +    ];
 +
 +    cases.into_iter().find_map(|(first, second)| {
 +        check(cx, &first, &second, first_expr, second_expr, input_hir_ids, is_float).or_else(|| {
 +            check(
 +                cx,
 +                &second,
 +                &first,
 +                second_expr,
 +                first_expr,
 +                input_hir_ids.map(|(l, r)| (r, l)),
 +                is_float,
 +            )
 +        })
 +    })
 +}
 +
 +fn block_stmt_with_last<'tcx>(block: &'tcx Block<'tcx>) -> impl Iterator<Item = MaybeBorrowedStmtKind<'tcx>> {
 +    block
 +        .stmts
 +        .iter()
 +        .map(|s| MaybeBorrowedStmtKind::Borrowed(&s.kind))
 +        .chain(
 +            block
 +                .expr
 +                .as_ref()
 +                .map(|e| MaybeBorrowedStmtKind::Owned(StmtKind::Expr(e))),
 +        )
 +}
 +
 +fn is_ord_op(op: BinOpKind) -> bool {
 +    matches!(op, BinOpKind::Ge | BinOpKind::Gt | BinOpKind::Le | BinOpKind::Lt)
 +}
 +
 +/// Really similar to Cow, but doesn't have a `Clone` requirement.
 +#[derive(Debug)]
 +enum MaybeBorrowedStmtKind<'a> {
 +    Borrowed(&'a StmtKind<'a>),
 +    Owned(StmtKind<'a>),
 +}
 +
 +impl<'a> Clone for MaybeBorrowedStmtKind<'a> {
 +    fn clone(&self) -> Self {
 +        match self {
 +            Self::Borrowed(t) => Self::Borrowed(t),
 +            Self::Owned(StmtKind::Expr(e)) => Self::Owned(StmtKind::Expr(e)),
 +            Self::Owned(_) => unreachable!("Owned should only ever contain a StmtKind::Expr."),
 +        }
 +    }
 +}
 +
 +impl<'a> Deref for MaybeBorrowedStmtKind<'a> {
 +    type Target = StmtKind<'a>;
 +
 +    fn deref(&self) -> &Self::Target {
 +        match self {
 +            Self::Borrowed(t) => t,
 +            Self::Owned(t) => t,
 +        }
 +    }
 +}
index fd14d868df348e7e856c08e8b59bf2f4c9ee58d8,0000000000000000000000000000000000000000..33a052c41a38ac26b4dc37fd963e82a99cb676bf
mode 100644,000000..100644
--- /dev/null
@@@ -1,145 -1,0 +1,158 @@@
-         if let Some(binding_span) = find_pat_binding(outer_pat, binding_id);
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::higher::IfLetOrMatch;
++use clippy_utils::source::snippet;
 +use clippy_utils::visitors::is_local_used;
 +use clippy_utils::{
 +    is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq,
 +};
 +use if_chain::if_chain;
 +use rustc_errors::MultiSpan;
 +use rustc_hir::LangItem::OptionNone;
 +use rustc_hir::{Arm, Expr, Guard, HirId, Let, Pat, PatKind};
 +use rustc_lint::LateContext;
 +use rustc_span::Span;
 +
 +use super::COLLAPSIBLE_MATCH;
 +
 +pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
 +    if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
 +        for arm in arms {
 +            check_arm(cx, true, arm.pat, arm.body, arm.guard.as_ref(), Some(els_arm.body));
 +        }
 +    }
 +}
 +
 +pub(super) fn check_if_let<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    pat: &'tcx Pat<'_>,
 +    body: &'tcx Expr<'_>,
 +    else_expr: Option<&'tcx Expr<'_>>,
 +) {
 +    check_arm(cx, false, pat, body, None, else_expr);
 +}
 +
 +fn check_arm<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    outer_is_match: bool,
 +    outer_pat: &'tcx Pat<'tcx>,
 +    outer_then_body: &'tcx Expr<'tcx>,
 +    outer_guard: Option<&'tcx Guard<'tcx>>,
 +    outer_else_body: Option<&'tcx Expr<'tcx>>,
 +) {
 +    let inner_expr = peel_blocks_with_stmt(outer_then_body);
 +    if_chain! {
 +        if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr);
 +        if let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner {
 +            IfLetOrMatch::IfLet(scrutinee, pat, _, els) => Some((scrutinee, pat, els)),
 +            IfLetOrMatch::Match(scrutinee, arms, ..) => if_chain! {
 +                // if there are more than two arms, collapsing would be non-trivial
 +                if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none());
 +                // one of the arms must be "wild-like"
 +                if let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a));
 +                then {
 +                    let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]);
 +                    Some((scrutinee, then.pat, Some(els.body)))
 +                } else {
 +                    None
 +                }
 +            },
 +        };
 +        if outer_pat.span.ctxt() == inner_scrutinee.span.ctxt();
 +        // match expression must be a local binding
 +        // match <local> { .. }
 +        if let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee));
 +        if !pat_contains_or(inner_then_pat);
 +        // the binding must come from the pattern of the containing match arm
 +        // ..<local>.. => match <local> { .. }
-                     help_span.push_span_label(inner_then_pat.span, "with this pattern");
++        if let (Some(binding_span), is_innermost_parent_pat_struct)
++            = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id);
 +        // the "else" branches must be equal
 +        if match (outer_else_body, inner_else_body) {
 +            (None, None) => true,
 +            (None, Some(e)) | (Some(e), None) => is_unit_expr(e),
 +            (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b),
 +        };
 +        // the binding must not be used in the if guard
 +        if outer_guard.map_or(
 +            true,
 +            |(Guard::If(e) | Guard::IfLet(Let { init: e, .. }))| !is_local_used(cx, *e, binding_id)
 +        );
 +        // ...or anywhere in the inner expression
 +        if match inner {
 +            IfLetOrMatch::IfLet(_, _, body, els) => {
 +                !is_local_used(cx, body, binding_id) && els.map_or(true, |e| !is_local_used(cx, e, binding_id))
 +            },
 +            IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| is_local_used(cx, arm, binding_id)),
 +        };
 +        then {
 +            let msg = format!(
 +                "this `{}` can be collapsed into the outer `{}`",
 +                if matches!(inner, IfLetOrMatch::Match(..)) { "match" } else { "if let" },
 +                if outer_is_match { "match" } else { "if let" },
 +            );
++            // collapsing patterns need an explicit field name in struct pattern matching
++            // ex: Struct {x: Some(1)}
++            let replace_msg = if is_innermost_parent_pat_struct {
++                format!(", prefixed by {}:", snippet(cx, binding_span, "their field name"))
++            } else {
++                String::new()
++            };
 +            span_lint_and_then(
 +                cx,
 +                COLLAPSIBLE_MATCH,
 +                inner_expr.span,
 +                &msg,
 +                |diag| {
 +                    let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]);
 +                    help_span.push_span_label(binding_span, "replace this binding");
- fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option<Span> {
++                    help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}"));
 +                    diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern");
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +/// A "wild-like" arm has a wild (`_`) or `None` pattern and no guard. Such arms can be "collapsed"
 +/// into a single wild arm without any significant loss in semantics or readability.
 +fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +    if arm.guard.is_some() {
 +        return false;
 +    }
 +    match arm.pat.kind {
 +        PatKind::Binding(..) | PatKind::Wild => true,
 +        PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone),
 +        _ => false,
 +    }
 +}
 +
-         _ => true,
++fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: HirId) -> (Option<Span>, bool) {
 +    let mut span = None;
++    let mut is_innermost_parent_pat_struct = false;
 +    pat.walk_short(|p| match &p.kind {
 +        // ignore OR patterns
 +        PatKind::Or(_) => false,
 +        PatKind::Binding(_bm, _, _ident, _) => {
 +            let found = p.hir_id == hir_id;
 +            if found {
 +                span = Some(p.span);
 +            }
 +            !found
 +        },
-     span
++        _ => {
++            is_innermost_parent_pat_struct = matches!(p.kind, PatKind::Struct(..));
++            true
++        },
 +    });
++    (span, is_innermost_parent_pat_struct)
 +}
 +
 +fn pat_contains_or(pat: &Pat<'_>) -> bool {
 +    let mut result = false;
 +    pat.walk(|p| {
 +        let is_or = matches!(p.kind, PatKind::Or(_));
 +        result |= is_or;
 +        !is_or
 +    });
 +    result
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..66ba1f6f9c55007771d75d415ff517bea32bdbe5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,153 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::ty::is_type_diagnostic_item;
++use clippy_utils::visitors::contains_unsafe_block;
++use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id};
++
++use rustc_hir::LangItem::OptionSome;
++use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind};
++use rustc_lint::LateContext;
++use rustc_span::{sym, SyntaxContext};
++
++use super::manual_utils::{check_with, SomeExpr};
++use super::MANUAL_FILTER;
++
++// Function called on the <expr> of `[&+]Some((ref | ref mut) x) => <expr>`
++// Need to check if it's of the form `<expr>=if <cond> {<then_expr>} else {<else_expr>}`
++// AND that only one `then/else_expr` resolves to `Some(x)` while the other resolves to `None`
++// return the `cond` expression if so.
++fn get_cond_expr<'tcx>(
++    cx: &LateContext<'tcx>,
++    pat: &Pat<'_>,
++    expr: &'tcx Expr<'_>,
++    ctxt: SyntaxContext,
++) -> Option<SomeExpr<'tcx>> {
++    if_chain! {
++        if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr);
++        if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind;
++        if let PatKind::Binding(_,target, ..) = pat.kind;
++        if let (then_visitor, else_visitor)
++            = (is_some_expr(cx, target, ctxt, then_expr),
++                is_some_expr(cx, target, ctxt, else_expr));
++        if then_visitor != else_visitor; // check that one expr resolves to `Some(x)`, the other to `None`
++        then {
++            return Some(SomeExpr {
++                    expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()),
++                    needs_unsafe_block: contains_unsafe_block(cx, expr),
++                    needs_negated: !then_visitor // if the `then_expr` resolves to `None`, need to negate the cond
++                })
++            }
++    };
++    None
++}
++
++fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
++    // we don't want to use `peel_blocks` here because we don't care if the block is unsafe, it's
++    // checked by `contains_unsafe_block`
++    if let ExprKind::Block(block, None) = expr.kind {
++        if block.stmts.is_empty() {
++            return block.expr;
++        }
++    };
++    None
++}
++
++fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> {
++    peels_blocks_incl_unsafe_opt(expr).unwrap_or(expr)
++}
++
++// function called for each <expr> expression:
++// Some(x) => if <cond> {
++//    <expr>
++// } else {
++//    <expr>
++// }
++// Returns true if <expr> resolves to `Some(x)`, `false` otherwise
++fn is_some_expr<'tcx>(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &'tcx Expr<'_>) -> bool {
++    if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) {
++        // there can be not statements in the block as they would be removed when switching to `.filter`
++        if let ExprKind::Call(callee, [arg]) = inner_expr.kind {
++            return ctxt == expr.span.ctxt()
++                && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome)
++                && path_to_local_id(arg, target);
++        }
++    };
++    false
++}
++
++// given the closure: `|<pattern>| <expr>`
++// returns `|&<pattern>| <expr>`
++fn add_ampersand_if_copy(body_str: String, has_copy_trait: bool) -> String {
++    if has_copy_trait {
++        let mut with_ampersand = body_str;
++        with_ampersand.insert(1, '&');
++        with_ampersand
++    } else {
++        body_str
++    }
++}
++
++pub(super) fn check_match<'tcx>(
++    cx: &LateContext<'tcx>,
++    scrutinee: &'tcx Expr<'_>,
++    arms: &'tcx [Arm<'_>],
++    expr: &'tcx Expr<'_>,
++) {
++    let ty = cx.typeck_results().expr_ty(expr);
++    if is_type_diagnostic_item(cx, ty, sym::Option)
++    && let [first_arm, second_arm] = arms
++    && first_arm.guard.is_none()
++    && second_arm.guard.is_none()
++         {
++            check(cx, expr, scrutinee, first_arm.pat, first_arm.body, Some(second_arm.pat), second_arm.body);
++        }
++}
++
++pub(super) fn check_if_let<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    let_pat: &'tcx Pat<'_>,
++    let_expr: &'tcx Expr<'_>,
++    then_expr: &'tcx Expr<'_>,
++    else_expr: &'tcx Expr<'_>,
++) {
++    check(cx, expr, let_expr, let_pat, then_expr, None, else_expr);
++}
++
++fn check<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    scrutinee: &'tcx Expr<'_>,
++    then_pat: &'tcx Pat<'_>,
++    then_body: &'tcx Expr<'_>,
++    else_pat: Option<&'tcx Pat<'_>>,
++    else_body: &'tcx Expr<'_>,
++) {
++    if let Some(sugg_info) = check_with(
++        cx,
++        expr,
++        scrutinee,
++        then_pat,
++        then_body,
++        else_pat,
++        else_body,
++        get_cond_expr,
++    ) {
++        let body_str = add_ampersand_if_copy(sugg_info.body_str, sugg_info.scrutinee_impl_copy);
++        span_lint_and_sugg(
++            cx,
++            MANUAL_FILTER,
++            expr.span,
++            "manual implementation of `Option::filter`",
++            "try this",
++            if sugg_info.needs_brackets {
++                format!(
++                    "{{ {}{}.filter({body_str}) }}",
++                    sugg_info.scrutinee_str, sugg_info.as_ref_str
++                )
++            } else {
++                format!("{}{}.filter({body_str})", sugg_info.scrutinee_str, sugg_info.as_ref_str)
++            },
++            sugg_info.app,
++        );
++    }
++}
index 76f5e1c941c7a270e37f0647455c8db60550588c,0000000000000000000000000000000000000000..aaba239677fffafa163e7e76805b30be9d037d75
mode 100644,000000..100644
--- /dev/null
@@@ -1,306 -1,0 +1,116 @@@
- use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
++use super::manual_utils::{check_with, SomeExpr};
++use super::MANUAL_MAP;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
- use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
- use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function};
- use clippy_utils::{
-     can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id,
-     peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
- };
- use rustc_ast::util::parser::PREC_POSTFIX;
- use rustc_errors::Applicability;
- use rustc_hir::LangItem::{OptionNone, OptionSome};
- use rustc_hir::{
-     def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path,
-     QPath, UnsafeSource,
- };
- use rustc_lint::LateContext;
- use rustc_span::{sym, SyntaxContext};
 +
- use super::MANUAL_MAP;
++use clippy_utils::{is_res_lang_ctor, path_res};
++
++use rustc_hir::LangItem::OptionSome;
++use rustc_hir::{Arm, Block, BlockCheckMode, Expr, ExprKind, Pat, UnsafeSource};
++use rustc_lint::LateContext;
++use rustc_span::SyntaxContext;
 +
 +pub(super) fn check_match<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    scrutinee: &'tcx Expr<'_>,
 +    arms: &'tcx [Arm<'_>],
 +) {
 +    if let [arm1, arm2] = arms
 +        && arm1.guard.is_none()
 +        && arm2.guard.is_none()
 +    {
 +        check(cx, expr, scrutinee, arm1.pat, arm1.body, Some(arm2.pat), arm2.body);
 +    }
 +}
 +
 +pub(super) fn check_if_let<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    let_pat: &'tcx Pat<'_>,
 +    let_expr: &'tcx Expr<'_>,
 +    then_expr: &'tcx Expr<'_>,
 +    else_expr: &'tcx Expr<'_>,
 +) {
 +    check(cx, expr, let_expr, let_pat, then_expr, None, else_expr);
 +}
 +
- #[expect(clippy::too_many_lines)]
 +fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    scrutinee: &'tcx Expr<'_>,
 +    then_pat: &'tcx Pat<'_>,
 +    then_body: &'tcx Expr<'_>,
 +    else_pat: Option<&'tcx Pat<'_>>,
 +    else_body: &'tcx Expr<'_>,
 +) {
-     let (scrutinee_ty, ty_ref_count, ty_mutability) =
-         peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee));
-     if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option)
-         && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option))
-     {
-         return;
-     }
-     let expr_ctxt = expr.span.ctxt();
-     let (some_expr, some_pat, pat_ref_count, is_wild_none) = match (
-         try_parse_pattern(cx, then_pat, expr_ctxt),
-         else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)),
++    if let Some(sugg_info) = check_with(
++        cx,
++        expr,
++        scrutinee,
++        then_pat,
++        then_body,
++        else_pat,
++        else_body,
++        get_some_expr,
 +    ) {
-         (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
-             (else_body, pattern, ref_count, true)
-         },
-         (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
-             (else_body, pattern, ref_count, false)
-         },
-         (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => {
-             (then_body, pattern, ref_count, true)
-         },
-         (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => {
-             (then_body, pattern, ref_count, false)
-         },
-         _ => return,
-     };
-     // Top level or patterns aren't allowed in closures.
-     if matches!(some_pat.kind, PatKind::Or(_)) {
-         return;
-     }
-     let some_expr = match get_some_expr(cx, some_expr, false, expr_ctxt) {
-         Some(expr) => expr,
-         None => return,
-     };
-     // These two lints will go back and forth with each other.
-     if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit
-         && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
-     {
-         return;
-     }
-     // `map` won't perform any adjustments.
-     if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() {
-         return;
-     }
-     // Determine which binding mode to use.
-     let explicit_ref = some_pat.contains_explicit_ref_binding();
-     let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then_some(ty_mutability));
-     let as_ref_str = match binding_ref {
-         Some(Mutability::Mut) => ".as_mut()",
-         Some(Mutability::Not) => ".as_ref()",
-         None => "",
-     };
-     match can_move_expr_to_closure(cx, some_expr.expr) {
-         Some(captures) => {
-             // Check if captures the closure will need conflict with borrows made in the scrutinee.
-             // TODO: check all the references made in the scrutinee expression. This will require interacting
-             // with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
-             if let Some(binding_ref_mutability) = binding_ref {
-                 let e = peel_hir_expr_while(scrutinee, |e| match e.kind {
-                     ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e),
-                     _ => None,
-                 });
-                 if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind {
-                     match captures.get(l) {
-                         Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return,
-                         Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => {
-                             return;
-                         },
-                         Some(CaptureKind::Ref(Mutability::Not)) | None => (),
-                     }
-                 }
-             }
-         },
-         None => return,
-     };
-     let mut app = Applicability::MachineApplicable;
-     // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or
-     // it's being passed by value.
-     let scrutinee = peel_hir_expr_refs(scrutinee).0;
-     let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
-     let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX {
-         format!("({scrutinee_str})")
-     } else {
-         scrutinee_str.into()
-     };
-     let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
-         if_chain! {
-             if !some_expr.needs_unsafe_block;
-             if let Some(func) = can_pass_as_func(cx, id, some_expr.expr);
-             if func.span.ctxt() == some_expr.expr.span.ctxt();
-             then {
-                 snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
++        span_lint_and_sugg(
++            cx,
++            MANUAL_MAP,
++            expr.span,
++            "manual implementation of `Option::map`",
++            "try this",
++            if sugg_info.needs_brackets {
++                format!(
++                    "{{ {}{}.map({}) }}",
++                    sugg_info.scrutinee_str, sugg_info.as_ref_str, sugg_info.body_str
++                )
 +            } else {
-                 if path_to_local_id(some_expr.expr, id)
-                     && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id)
-                     && binding_ref.is_some()
-                 {
-                     return;
-                 }
-                 // `ref` and `ref mut` annotations were handled earlier.
-                 let annotation = if matches!(annotation, BindingAnnotation::MUT) {
-                     "mut "
-                 } else {
-                     ""
-                 };
-                 let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0;
-                 if some_expr.needs_unsafe_block {
-                     format!("|{annotation}{some_binding}| unsafe {{ {expr_snip} }}")
-                 } else {
-                     format!("|{annotation}{some_binding}| {expr_snip}")
-                 }
-             }
-         }
-     } else if !is_wild_none && explicit_ref.is_none() {
-         // TODO: handle explicit reference annotations.
-         let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0;
-         let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0;
-         if some_expr.needs_unsafe_block {
-             format!("|{pat_snip}| unsafe {{ {expr_snip} }}")
-         } else {
-             format!("|{pat_snip}| {expr_snip}")
-         }
-     } else {
-         // Refutable bindings and mixed reference annotations can't be handled by `map`.
-         return;
-     };
-     span_lint_and_sugg(
-         cx,
-         MANUAL_MAP,
-         expr.span,
-         "manual implementation of `Option::map`",
-         "try this",
-         if else_pat.is_none() && is_else_clause(cx.tcx, expr) {
-             format!("{{ {scrutinee_str}{as_ref_str}.map({body_str}) }}")
-         } else {
-             format!("{scrutinee_str}{as_ref_str}.map({body_str})")
-         },
-         app,
-     );
- }
- // Checks whether the expression could be passed as a function, or whether a closure is needed.
- // Returns the function to be passed to `map` if it exists.
- fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
-     match expr.kind {
-         ExprKind::Call(func, [arg])
-             if path_to_local_id(arg, binding)
-                 && cx.typeck_results().expr_adjustments(arg).is_empty()
-                 && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) =>
-         {
-             Some(func)
-         },
-         _ => None,
-     }
- }
- enum OptionPat<'a> {
-     Wild,
-     None,
-     Some {
-         // The pattern contained in the `Some` tuple.
-         pattern: &'a Pat<'a>,
-         // The number of references before the `Some` tuple.
-         // e.g. `&&Some(_)` has a ref count of 2.
-         ref_count: usize,
-     },
- }
- struct SomeExpr<'tcx> {
-     expr: &'tcx Expr<'tcx>,
-     needs_unsafe_block: bool,
- }
- // Try to parse into a recognized `Option` pattern.
- // i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
- fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
-     fn f<'tcx>(
-         cx: &LateContext<'tcx>,
-         pat: &'tcx Pat<'_>,
-         ref_count: usize,
-         ctxt: SyntaxContext,
-     ) -> Option<OptionPat<'tcx>> {
-         match pat.kind {
-             PatKind::Wild => Some(OptionPat::Wild),
-             PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
-             PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => {
-                 Some(OptionPat::None)
-             },
-             PatKind::TupleStruct(ref qpath, [pattern], _)
-                 if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt =>
-             {
-                 Some(OptionPat::Some { pattern, ref_count })
++                format!(
++                    "{}{}.map({})",
++                    sugg_info.scrutinee_str, sugg_info.as_ref_str, sugg_info.body_str
++                )
 +            },
-             _ => None,
-         }
++            sugg_info.app,
++        );
 +    }
-     f(cx, pat, 0, ctxt)
 +}
 +
 +// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
 +fn get_some_expr<'tcx>(
 +    cx: &LateContext<'tcx>,
++    _: &'tcx Pat<'_>,
 +    expr: &'tcx Expr<'_>,
-     needs_unsafe_block: bool,
 +    ctxt: SyntaxContext,
 +) -> Option<SomeExpr<'tcx>> {
-     // TODO: Allow more complex expressions.
-     match expr.kind {
-         ExprKind::Call(callee, [arg])
-             if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) =>
-         {
-             Some(SomeExpr {
-                 expr: arg,
-                 needs_unsafe_block,
-             })
-         },
-         ExprKind::Block(
-             Block {
-                 stmts: [],
-                 expr: Some(expr),
-                 rules,
-                 ..
++    fn get_some_expr_internal<'tcx>(
++        cx: &LateContext<'tcx>,
++        expr: &'tcx Expr<'_>,
++        needs_unsafe_block: bool,
++        ctxt: SyntaxContext,
++    ) -> Option<SomeExpr<'tcx>> {
++        // TODO: Allow more complex expressions.
++        match expr.kind {
++            ExprKind::Call(callee, [arg])
++                if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) =>
++            {
++                Some(SomeExpr::new_no_negated(arg, needs_unsafe_block))
 +            },
-             _,
-         ) => get_some_expr(
-             cx,
-             expr,
-             needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
-             ctxt,
-         ),
-         _ => None,
++            ExprKind::Block(
++                Block {
++                    stmts: [],
++                    expr: Some(expr),
++                    rules,
++                    ..
++                },
++                _,
++            ) => get_some_expr_internal(
++                cx,
++                expr,
++                needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
++                ctxt,
++            ),
++            _ => None,
++        }
 +    }
- }
- // Checks for the `None` value.
- fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-     is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone)
++    get_some_expr_internal(cx, expr, false, ctxt)
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5b7644a5383231a988574b5bcffb438838979eb1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,277 @@@
++use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
++use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
++use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function};
++use clippy_utils::{
++    can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id,
++    peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, sugg::Sugg, CaptureKind,
++};
++use rustc_ast::util::parser::PREC_POSTFIX;
++use rustc_errors::Applicability;
++use rustc_hir::LangItem::{OptionNone, OptionSome};
++use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath};
++use rustc_lint::LateContext;
++use rustc_span::{sym, SyntaxContext};
++
++#[expect(clippy::too_many_arguments)]
++#[expect(clippy::too_many_lines)]
++pub(super) fn check_with<'tcx, F>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    scrutinee: &'tcx Expr<'_>,
++    then_pat: &'tcx Pat<'_>,
++    then_body: &'tcx Expr<'_>,
++    else_pat: Option<&'tcx Pat<'_>>,
++    else_body: &'tcx Expr<'_>,
++    get_some_expr_fn: F,
++) -> Option<SuggInfo<'tcx>>
++where
++    F: Fn(&LateContext<'tcx>, &'tcx Pat<'_>, &'tcx Expr<'_>, SyntaxContext) -> Option<SomeExpr<'tcx>>,
++{
++    let (scrutinee_ty, ty_ref_count, ty_mutability) =
++        peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee));
++    if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option)
++        && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option))
++    {
++        return None;
++    }
++
++    let expr_ctxt = expr.span.ctxt();
++    let (some_expr, some_pat, pat_ref_count, is_wild_none) = match (
++        try_parse_pattern(cx, then_pat, expr_ctxt),
++        else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)),
++    ) {
++        (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
++            (else_body, pattern, ref_count, true)
++        },
++        (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
++            (else_body, pattern, ref_count, false)
++        },
++        (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => {
++            (then_body, pattern, ref_count, true)
++        },
++        (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => {
++            (then_body, pattern, ref_count, false)
++        },
++        _ => return None,
++    };
++
++    // Top level or patterns aren't allowed in closures.
++    if matches!(some_pat.kind, PatKind::Or(_)) {
++        return None;
++    }
++
++    let Some(some_expr) = get_some_expr_fn(cx, some_pat, some_expr, expr_ctxt) else {
++        return None;
++    };
++
++    // These two lints will go back and forth with each other.
++    if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit
++        && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
++    {
++        return None;
++    }
++
++    // `map` won't perform any adjustments.
++    if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() {
++        return None;
++    }
++
++    // Determine which binding mode to use.
++    let explicit_ref = some_pat.contains_explicit_ref_binding();
++    let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then_some(ty_mutability));
++
++    let as_ref_str = match binding_ref {
++        Some(Mutability::Mut) => ".as_mut()",
++        Some(Mutability::Not) => ".as_ref()",
++        None => "",
++    };
++
++    match can_move_expr_to_closure(cx, some_expr.expr) {
++        Some(captures) => {
++            // Check if captures the closure will need conflict with borrows made in the scrutinee.
++            // TODO: check all the references made in the scrutinee expression. This will require interacting
++            // with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
++            if let Some(binding_ref_mutability) = binding_ref {
++                let e = peel_hir_expr_while(scrutinee, |e| match e.kind {
++                    ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e),
++                    _ => None,
++                });
++                if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind {
++                    match captures.get(l) {
++                        Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None,
++                        Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => {
++                            return None;
++                        },
++                        Some(CaptureKind::Ref(Mutability::Not)) | None => (),
++                    }
++                }
++            }
++        },
++        None => return None,
++    };
++
++    let mut app = Applicability::MachineApplicable;
++
++    // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or
++    // it's being passed by value.
++    let scrutinee = peel_hir_expr_refs(scrutinee).0;
++    let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
++    let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX {
++        format!("({scrutinee_str})")
++    } else {
++        scrutinee_str.into()
++    };
++
++    let closure_expr_snip = some_expr.to_snippet_with_context(cx, expr_ctxt, &mut app);
++    let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
++        if_chain! {
++            if !some_expr.needs_unsafe_block;
++            if let Some(func) = can_pass_as_func(cx, id, some_expr.expr);
++            if func.span.ctxt() == some_expr.expr.span.ctxt();
++            then {
++                snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
++            } else {
++                if path_to_local_id(some_expr.expr, id)
++                    && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id)
++                    && binding_ref.is_some()
++                {
++                    return None;
++                }
++
++                // `ref` and `ref mut` annotations were handled earlier.
++                let annotation = if matches!(annotation, BindingAnnotation::MUT) {
++                    "mut "
++                } else {
++                    ""
++                };
++
++                if some_expr.needs_unsafe_block {
++                    format!("|{annotation}{some_binding}| unsafe {{ {closure_expr_snip} }}")
++                } else {
++                    format!("|{annotation}{some_binding}| {closure_expr_snip}")
++                }
++            }
++        }
++    } else if !is_wild_none && explicit_ref.is_none() {
++        // TODO: handle explicit reference annotations.
++        let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0;
++        if some_expr.needs_unsafe_block {
++            format!("|{pat_snip}| unsafe {{ {closure_expr_snip} }}")
++        } else {
++            format!("|{pat_snip}| {closure_expr_snip}")
++        }
++    } else {
++        // Refutable bindings and mixed reference annotations can't be handled by `map`.
++        return None;
++    };
++
++    // relies on the fact that Option<T>: Copy where T: copy
++    let scrutinee_impl_copy = is_copy(cx, scrutinee_ty);
++
++    Some(SuggInfo {
++        needs_brackets: else_pat.is_none() && is_else_clause(cx.tcx, expr),
++        scrutinee_impl_copy,
++        scrutinee_str,
++        as_ref_str,
++        body_str,
++        app,
++    })
++}
++
++pub struct SuggInfo<'a> {
++    pub needs_brackets: bool,
++    pub scrutinee_impl_copy: bool,
++    pub scrutinee_str: String,
++    pub as_ref_str: &'a str,
++    pub body_str: String,
++    pub app: Applicability,
++}
++
++// Checks whether the expression could be passed as a function, or whether a closure is needed.
++// Returns the function to be passed to `map` if it exists.
++fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
++    match expr.kind {
++        ExprKind::Call(func, [arg])
++            if path_to_local_id(arg, binding)
++                && cx.typeck_results().expr_adjustments(arg).is_empty()
++                && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) =>
++        {
++            Some(func)
++        },
++        _ => None,
++    }
++}
++
++#[derive(Debug)]
++pub(super) enum OptionPat<'a> {
++    Wild,
++    None,
++    Some {
++        // The pattern contained in the `Some` tuple.
++        pattern: &'a Pat<'a>,
++        // The number of references before the `Some` tuple.
++        // e.g. `&&Some(_)` has a ref count of 2.
++        ref_count: usize,
++    },
++}
++
++pub(super) struct SomeExpr<'tcx> {
++    pub expr: &'tcx Expr<'tcx>,
++    pub needs_unsafe_block: bool,
++    pub needs_negated: bool, // for `manual_filter` lint
++}
++
++impl<'tcx> SomeExpr<'tcx> {
++    pub fn new_no_negated(expr: &'tcx Expr<'tcx>, needs_unsafe_block: bool) -> Self {
++        Self {
++            expr,
++            needs_unsafe_block,
++            needs_negated: false,
++        }
++    }
++
++    pub fn to_snippet_with_context(
++        &self,
++        cx: &LateContext<'tcx>,
++        ctxt: SyntaxContext,
++        app: &mut Applicability,
++    ) -> Sugg<'tcx> {
++        let sugg = Sugg::hir_with_context(cx, self.expr, ctxt, "..", app);
++        if self.needs_negated { !sugg } else { sugg }
++    }
++}
++
++// Try to parse into a recognized `Option` pattern.
++// i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
++pub(super) fn try_parse_pattern<'tcx>(
++    cx: &LateContext<'tcx>,
++    pat: &'tcx Pat<'_>,
++    ctxt: SyntaxContext,
++) -> Option<OptionPat<'tcx>> {
++    fn f<'tcx>(
++        cx: &LateContext<'tcx>,
++        pat: &'tcx Pat<'_>,
++        ref_count: usize,
++        ctxt: SyntaxContext,
++    ) -> Option<OptionPat<'tcx>> {
++        match pat.kind {
++            PatKind::Wild => Some(OptionPat::Wild),
++            PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
++            PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => {
++                Some(OptionPat::None)
++            },
++            PatKind::TupleStruct(ref qpath, [pattern], _)
++                if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt =>
++            {
++                Some(OptionPat::Some { pattern, ref_count })
++            },
++            _ => None,
++        }
++    }
++    f(cx, pat, 0, ctxt)
++}
++
++// Checks for the `None` value.
++fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
++    is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone)
++}
index 37049f83577513605c6ea63000e4662101898138,0000000000000000000000000000000000000000..168c1e4d2e60d4d3da9e94412c674d8539a299a5
mode 100644,000000..100644
--- /dev/null
@@@ -1,413 -1,0 +1,411 @@@
-     #[expect(clippy::too_many_lines)]
 +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, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +use rustc_span::Symbol;
 +
 +use super::MATCH_SAME_ARMS;
 +
 +#[expect(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 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_some(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_some(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) {
 +                    HirIdMapEntry::Vacant(entry) => entry,
 +                    // check if using the same bindings as before
 +                    HirIdMapEntry::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", "", 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())
 +}
 +
 +#[expect(clippy::similar_names)]
 +impl<'a> NormalizedPat<'a> {
-                 let adt = match cx.typeck_results().pat_ty(pat).ty_adt_def() {
-                     Some(x) => x,
-                     None => return Self::Wild,
 +    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 Some(adt) = cx.typeck_results().pat_ty(pat).ty_adt_def() else {
++                    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.as_opt_usize() {
 +                    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.as_opt_usize() {
 +                    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 minimum 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 68682cedf1de434bcce702c5c5abb13e4f97a53f,0000000000000000000000000000000000000000..1bf8d4e96ad49a8056a9208686c5cfd5f9acc3b1
mode 100644,000000..100644
--- /dev/null
@@@ -1,208 -1,0 +1,221 @@@
-                 let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0));
-                 let sugg = format!(
-                     "{};\n{indent}{snippet_body}",
-                     snippet_with_applicability(cx, ex.span, "..", &mut applicability)
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::macros::HirNode;
 +use clippy_utils::source::{indent_of, snippet, snippet_block, snippet_with_applicability};
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::{get_parent_expr, is_refutable, peel_blocks};
 +use rustc_errors::Applicability;
 +use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind};
 +use rustc_lint::LateContext;
 +use rustc_span::Span;
 +
 +use super::MATCH_SINGLE_BINDING;
 +
 +enum AssignmentExpr {
 +    Assign { span: Span, match_span: Span },
 +    Local { span: Span, pat_span: Span },
 +}
 +
 +#[expect(clippy::too_many_lines)]
 +pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'a>) {
 +    if expr.span.from_expansion() || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
 +        return;
 +    }
 +
 +    let matched_vars = ex.span;
 +    let bind_names = arms[0].pat.span;
 +    let match_body = peel_blocks(arms[0].body);
 +    let mut snippet_body = if match_body.span.from_expansion() {
 +        Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string()
 +    } else {
 +        snippet_block(cx, match_body.span, "..", Some(expr.span)).to_string()
 +    };
 +
 +    // Do we need to add ';' to suggestion ?
 +    match match_body.kind {
 +        ExprKind::Block(block, _) => {
 +            // macro + expr_ty(body) == ()
 +            if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() {
 +                snippet_body.push(';');
 +            }
 +        },
 +        _ => {
 +            // expr_ty(body) == ()
 +            if cx.typeck_results().expr_ty(match_body).is_unit() {
 +                snippet_body.push(';');
 +            }
 +        },
 +    }
 +
 +    let mut applicability = Applicability::MaybeIncorrect;
 +    match arms[0].pat.kind {
 +        PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => {
 +            let (target_span, sugg) = match opt_parent_assign_span(cx, ex) {
 +                Some(AssignmentExpr::Assign { span, match_span }) => {
 +                    let sugg = sugg_with_curlies(
 +                        cx,
 +                        (ex, expr),
 +                        (bind_names, matched_vars),
 +                        &snippet_body,
 +                        &mut applicability,
 +                        Some(span),
++                        true,
 +                    );
 +
 +                    span_lint_and_sugg(
 +                        cx,
 +                        MATCH_SINGLE_BINDING,
 +                        span.to(match_span),
 +                        "this assignment could be simplified",
 +                        "consider removing the `match` expression",
 +                        sugg,
 +                        applicability,
 +                    );
 +
 +                    return;
 +                },
 +                Some(AssignmentExpr::Local { span, pat_span }) => (
 +                    span,
 +                    format!(
 +                        "let {} = {};\n{}let {} = {snippet_body};",
 +                        snippet_with_applicability(cx, bind_names, "..", &mut applicability),
 +                        snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
 +                        " ".repeat(indent_of(cx, expr.span).unwrap_or(0)),
 +                        snippet_with_applicability(cx, pat_span, "..", &mut applicability)
 +                    ),
 +                ),
 +                None => {
 +                    let sugg = sugg_with_curlies(
 +                        cx,
 +                        (ex, expr),
 +                        (bind_names, matched_vars),
 +                        &snippet_body,
 +                        &mut applicability,
 +                        None,
++                        true,
 +                    );
 +                    (expr.span, sugg)
 +                },
 +            };
 +
 +            span_lint_and_sugg(
 +                cx,
 +                MATCH_SINGLE_BINDING,
 +                target_span,
 +                "this match could be written as a `let` statement",
 +                "consider using a `let` statement",
 +                sugg,
 +                applicability,
 +            );
 +        },
 +        PatKind::Wild => {
 +            if ex.can_have_side_effects() {
-     format!(
-         "{cbrace_start}let {} = {};\n{indent}{assignment_str}{snippet_body}{cbrace_end}",
-         snippet_with_applicability(cx, bind_names, "..", applicability),
-         snippet_with_applicability(cx, matched_vars, "..", applicability)
-     )
++                let sugg = sugg_with_curlies(
++                    cx,
++                    (ex, expr),
++                    (bind_names, matched_vars),
++                    &snippet_body,
++                    &mut applicability,
++                    None,
++                    false,
 +                );
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    MATCH_SINGLE_BINDING,
 +                    expr.span,
 +                    "this match could be replaced by its scrutinee and body",
 +                    "consider using the scrutinee and body instead",
 +                    sugg,
 +                    applicability,
 +                );
 +            } else {
 +                span_lint_and_sugg(
 +                    cx,
 +                    MATCH_SINGLE_BINDING,
 +                    expr.span,
 +                    "this match could be replaced by its body itself",
 +                    "consider using the match body instead",
 +                    snippet_body,
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        },
 +        _ => (),
 +    }
 +}
 +
 +/// Returns true if the `ex` match expression is in a local (`let`) or assign expression
 +fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<AssignmentExpr> {
 +    let map = &cx.tcx.hir();
 +
 +    if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id)) {
 +        return match map.find(map.get_parent_node(parent_arm_expr.hir_id)) {
 +            Some(Node::Local(parent_let_expr)) => Some(AssignmentExpr::Local {
 +                span: parent_let_expr.span,
 +                pat_span: parent_let_expr.pat.span(),
 +            }),
 +            Some(Node::Expr(Expr {
 +                kind: ExprKind::Assign(parent_assign_expr, match_expr, _),
 +                ..
 +            })) => Some(AssignmentExpr::Assign {
 +                span: parent_assign_expr.span,
 +                match_span: match_expr.span,
 +            }),
 +            _ => None,
 +        };
 +    }
 +
 +    None
 +}
 +
 +fn sugg_with_curlies<'a>(
 +    cx: &LateContext<'a>,
 +    (ex, match_expr): (&Expr<'a>, &Expr<'a>),
 +    (bind_names, matched_vars): (Span, Span),
 +    snippet_body: &str,
 +    applicability: &mut Applicability,
 +    assignment: Option<Span>,
++    needs_var_binding: bool,
 +) -> String {
 +    let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0));
 +
 +    let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new());
 +    if let Some(parent_expr) = get_parent_expr(cx, match_expr) {
 +        if let ExprKind::Closure { .. } = parent_expr.kind {
 +            cbrace_end = format!("\n{indent}}}");
 +            // Fix body indent due to the closure
 +            indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
 +            cbrace_start = format!("{{\n{indent}");
 +        }
 +    }
 +
 +    // If the parent is already an arm, and the body is another match statement,
 +    // we need curly braces around suggestion
 +    let parent_node_id = cx.tcx.hir().get_parent_node(match_expr.hir_id);
 +    if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) {
 +        if let ExprKind::Match(..) = arm.body.kind {
 +            cbrace_end = format!("\n{indent}}}");
 +            // Fix body indent due to the match
 +            indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
 +            cbrace_start = format!("{{\n{indent}");
 +        }
 +    }
 +
 +    let assignment_str = assignment.map_or_else(String::new, |span| {
 +        let mut s = snippet(cx, span, "..").to_string();
 +        s.push_str(" = ");
 +        s
 +    });
 +
++    let scrutinee = if needs_var_binding {
++        format!(
++            "let {} = {}",
++            snippet_with_applicability(cx, bind_names, "..", applicability),
++            snippet_with_applicability(cx, matched_vars, "..", applicability)
++        )
++    } else {
++        snippet_with_applicability(cx, matched_vars, "..", applicability).to_string()
++    };
++
++    format!("{cbrace_start}{scrutinee};\n{indent}{assignment_str}{snippet_body}{cbrace_end}")
 +}
index e6b183fc05f25df01fabded1ce6bb49835880cf9,0000000000000000000000000000000000000000..7d8171ead89e10787dee90e48d11a7e33b527a6a
mode 100644,000000..100644
--- /dev/null
@@@ -1,1134 -1,0 +1,1174 @@@
 +mod collapsible_match;
 +mod infallible_destructuring_match;
++mod manual_filter;
 +mod manual_map;
 +mod manual_unwrap_or;
++mod manual_utils;
 +mod match_as_ref;
 +mod match_bool;
 +mod match_like_matches;
 +mod match_on_vec_items;
 +mod match_ref_pats;
 +mod match_same_arms;
 +mod match_single_binding;
 +mod match_str_case_mismatch;
 +mod match_wild_enum;
 +mod match_wild_err_arm;
 +mod needless_match;
 +mod overlapping_arms;
 +mod redundant_pattern_match;
 +mod rest_pat_in_fully_bound_struct;
 +mod significant_drop_in_scrutinee;
 +mod single_match;
 +mod try_err;
 +mod wild_in_or_pats;
 +
 +use clippy_utils::source::{snippet_opt, walk_span_to_context};
 +use clippy_utils::{higher, in_constant, is_span_match, meets_msrv, msrvs};
 +use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
 +use rustc_lexer::{tokenize, TokenKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{Span, SpanData, SyntaxContext};
 +
 +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");
 +    /// match x {
 +    ///     Some(ref foo) => bar(foo),
 +    ///     _ => (),
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # fn bar(stool: &str) {}
 +    /// # let x = Some("abc");
 +    /// 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
 +    /// match x {
 +    ///     &A(ref y) => foo(y),
 +    ///     &B => bar(),
 +    ///     _ => frob(&x),
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// 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;
 +    ///
 +    /// let r: Option<&()> = match x {
 +    ///     None => None,
 +    ///     Some(ref v) => Some(v),
 +    /// };
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x: Option<()> = None;
 +    ///
 +    /// 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);
 +    /// match x {
 +    ///     Foo::A(_) => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # enum Foo { A(usize), B(usize) }
 +    /// # let x = Foo::B(1);
 +    /// 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;
 +    /// match x {
 +    ///     Foo::A => {},
 +    ///     Foo::B => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # enum Foo { A, B, C }
 +    /// # let x = Foo::B;
 +    /// 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
 +    /// # let s = "foo";
 +    /// match s {
 +    ///     "a" => {},
 +    ///     "bar" | _ => {},
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let s = "foo";
 +    /// match s {
 +    ///     "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;
 +    /// match (a, b) {
 +    ///     (c, d) => {
 +    ///         // useless match
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let a = 1;
 +    /// # let b = 2;
 +    /// 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 };
 +    ///
 +    /// match a {
 +    ///     A { a: 5, .. } => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # struct A { a: i32 }
 +    /// # let a = A { a: 5 };
 +    /// 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);
 +    ///
 +    /// let a = match x {
 +    ///     Some(0) => true,
 +    ///     _ => false,
 +    /// };
 +    ///
 +    /// let a = if let Some(0) = x {
 +    ///     true
 +    /// } else {
 +    ///     false
 +    /// };
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = Some(5);
 +    /// let a = matches!(x, Some(0));
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub MATCH_LIKE_MATCHES_MACRO,
 +    style,
 +    "a match that could be written with the matches! macro"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `match` with identical arm bodies.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a copy & paste error. If arm bodies
 +    /// are the same on purpose, you can factor them
 +    /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
 +    ///
 +    /// ### Known problems
 +    /// False positive possible with order dependent `match`
 +    /// (see issue
 +    /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// match foo {
 +    ///     Bar => bar(),
 +    ///     Quz => quz(),
 +    ///     Baz => bar(), // <= oops
 +    /// }
 +    /// ```
 +    ///
 +    /// This should probably be
 +    /// ```rust,ignore
 +    /// match foo {
 +    ///     Bar => bar(),
 +    ///     Quz => quz(),
 +    ///     Baz => baz(), // <= fixed
 +    /// }
 +    /// ```
 +    ///
 +    /// or if the original code was not a typo:
 +    /// ```rust,ignore
 +    /// match foo {
 +    ///     Bar | Baz => bar(), // <= shows the intent better
 +    ///     Quz => quz(),
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_SAME_ARMS,
 +    pedantic,
 +    "`match` with identical arm bodies"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary `match` or match-like `if let` returns for `Option` and `Result`
 +    /// when function signatures are the same.
 +    ///
 +    /// ### Why is this bad?
 +    /// This `match` block does nothing and might not be what the coder intended.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// fn foo() -> Result<(), i32> {
 +    ///     match result {
 +    ///         Ok(val) => Ok(val),
 +    ///         Err(err) => Err(err),
 +    ///     }
 +    /// }
 +    ///
 +    /// fn bar() -> Option<i32> {
 +    ///     if let Some(val) = option {
 +    ///         Some(val)
 +    ///     } else {
 +    ///         None
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be replaced as
 +    ///
 +    /// ```rust,ignore
 +    /// fn foo() -> Result<(), i32> {
 +    ///     result
 +    /// }
 +    ///
 +    /// fn bar() -> Option<i32> {
 +    ///     option
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub NEEDLESS_MATCH,
 +    complexity,
 +    "`match` or match-like `if let` that are unnecessary"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together
 +    /// without adding any branches.
 +    ///
 +    /// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only
 +    /// cases where merging would most likely make the code more readable.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is unnecessarily verbose and complex.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn func(opt: Option<Result<u64, String>>) {
 +    ///     let n = match opt {
 +    ///         Some(n) => match n {
 +    ///             Ok(n) => n,
 +    ///             _ => return,
 +    ///         }
 +    ///         None => return,
 +    ///     };
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn func(opt: Option<Result<u64, String>>) {
 +    ///     let n = match opt {
 +    ///         Some(Ok(n)) => n,
 +    ///         _ => return,
 +    ///     };
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.50.0"]
 +    pub COLLAPSIBLE_MATCH,
 +    style,
 +    "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together."
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Concise code helps focusing on behavior instead of boilerplate.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let foo: Option<i32> = None;
 +    /// match foo {
 +    ///     Some(v) => v,
 +    ///     None => 1,
 +    /// };
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let foo: Option<i32> = None;
 +    /// foo.unwrap_or(1);
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub MANUAL_UNWRAP_OR,
 +    complexity,
 +    "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `match vec[idx]` or `match vec[n..m]`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This can panic at runtime.
 +    ///
 +    /// ### Example
 +    /// ```rust, no_run
 +    /// let arr = vec![0, 1, 2, 3];
 +    /// let idx = 1;
 +    ///
 +    /// match arr[idx] {
 +    ///     0 => println!("{}", 0),
 +    ///     1 => println!("{}", 3),
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust, no_run
 +    /// let arr = vec![0, 1, 2, 3];
 +    /// let idx = 1;
 +    ///
 +    /// match arr.get(idx) {
 +    ///     Some(0) => println!("{}", 0),
 +    ///     Some(1) => println!("{}", 3),
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub MATCH_ON_VEC_ITEMS,
 +    pedantic,
 +    "matching on vector elements can panic"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `match` expressions modifying the case of a string with non-compliant arms
 +    ///
 +    /// ### Why is this bad?
 +    /// The arm is unreachable, which is likely a mistake
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let text = "Foo";
 +    /// match &*text.to_ascii_lowercase() {
 +    ///     "foo" => {},
 +    ///     "Bar" => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let text = "Foo";
 +    /// match &*text.to_ascii_lowercase() {
 +    ///     "foo" => {},
 +    ///     "bar" => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub MATCH_STR_CASE_MISMATCH,
 +    correctness,
 +    "creation of a case altering match expression with non-compliant arms"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Check for temporaries returned from function calls in a match scrutinee that have the
 +    /// `clippy::has_significant_drop` attribute.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have
 +    /// an important side-effect, such as unlocking a mutex, making it important for users to be
 +    /// able to accurately understand their lifetimes. When a temporary is returned in a function
 +    /// call in a match scrutinee, its lifetime lasts until the end of the match block, which may
 +    /// be surprising.
 +    ///
 +    /// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a
 +    /// function call that returns a `MutexGuard` and then tries to lock again in one of the match
 +    /// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of
 +    /// the match block and thus will not unlock.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// # use std::sync::Mutex;
 +    /// # struct State {}
 +    /// # impl State {
 +    /// #     fn foo(&self) -> bool {
 +    /// #         true
 +    /// #     }
 +    /// #     fn bar(&self) {}
 +    /// # }
 +    /// let mutex = Mutex::new(State {});
 +    ///
 +    /// match mutex.lock().unwrap().foo() {
 +    ///     true => {
 +    ///         mutex.lock().unwrap().bar(); // Deadlock!
 +    ///     }
 +    ///     false => {}
 +    /// };
 +    ///
 +    /// println!("All done!");
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::sync::Mutex;
 +    /// # struct State {}
 +    /// # impl State {
 +    /// #     fn foo(&self) -> bool {
 +    /// #         true
 +    /// #     }
 +    /// #     fn bar(&self) {}
 +    /// # }
 +    /// let mutex = Mutex::new(State {});
 +    ///
 +    /// let is_foo = mutex.lock().unwrap().foo();
 +    /// match is_foo {
 +    ///     true => {
 +    ///         mutex.lock().unwrap().bar();
 +    ///     }
 +    ///     false => {}
 +    /// };
 +    ///
 +    /// println!("All done!");
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub SIGNIFICANT_DROP_IN_SCRUTINEE,
 +    nursery,
 +    "warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `Err(x)?`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `?` operator is designed to allow calls that
 +    /// can fail to be easily chained. For example, `foo()?.bar()` or
 +    /// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will
 +    /// always return), it is more clear to write `return Err(x)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo(fail: bool) -> Result<i32, String> {
 +    ///     if fail {
 +    ///       Err("failed")?;
 +    ///     }
 +    ///     Ok(0)
 +    /// }
 +    /// ```
 +    /// Could be written:
 +    ///
 +    /// ```rust
 +    /// fn foo(fail: bool) -> Result<i32, String> {
 +    ///     if fail {
 +    ///       return Err("failed".into());
 +    ///     }
 +    ///     Ok(0)
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.38.0"]
 +    pub TRY_ERR,
 +    restriction,
 +    "return errors explicitly rather than hiding them behind a `?`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `match` which could be implemented using `map`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the `map` method is clearer and more concise.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// match Some(0) {
 +    ///     Some(x) => Some(x + 1),
 +    ///     None => None,
 +    /// };
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// Some(0).map(|x| x + 1);
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub MANUAL_MAP,
 +    style,
 +    "reimplementation of `map`"
 +}
 +
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for usages of `match` which could be implemented using `filter`
++    ///
++    /// ### Why is this bad?
++    /// Using the `filter` method is clearer and more concise.
++    ///
++    /// ### Example
++    /// ```rust
++    /// match Some(0) {
++    ///     Some(x) => if x % 2 == 0 {
++    ///                     Some(x)
++    ///                } else {
++    ///                     None
++    ///                 },
++    ///     None => None,
++    /// };
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// Some(0).filter(|&x| x % 2 == 0);
++    /// ```
++    #[clippy::version = "1.66.0"]
++    pub MANUAL_FILTER,
++    complexity,
++    "reimplentation of `filter`"
++}
++
 +#[derive(Default)]
 +pub struct Matches {
 +    msrv: Option<RustcVersion>,
 +    infallible_destructuring_match_linted: bool,
 +}
 +
 +impl Matches {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self {
 +            msrv,
 +            ..Matches::default()
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(Matches => [
 +    SINGLE_MATCH,
 +    MATCH_REF_PATS,
 +    MATCH_BOOL,
 +    SINGLE_MATCH_ELSE,
 +    MATCH_OVERLAPPING_ARM,
 +    MATCH_WILD_ERR_ARM,
 +    MATCH_AS_REF,
 +    WILDCARD_ENUM_MATCH_ARM,
 +    MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +    WILDCARD_IN_OR_PATTERNS,
 +    MATCH_SINGLE_BINDING,
 +    INFALLIBLE_DESTRUCTURING_MATCH,
 +    REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +    REDUNDANT_PATTERN_MATCHING,
 +    MATCH_LIKE_MATCHES_MACRO,
 +    MATCH_SAME_ARMS,
 +    NEEDLESS_MATCH,
 +    COLLAPSIBLE_MATCH,
 +    MANUAL_UNWRAP_OR,
 +    MATCH_ON_VEC_ITEMS,
 +    MATCH_STR_CASE_MISMATCH,
 +    SIGNIFICANT_DROP_IN_SCRUTINEE,
 +    TRY_ERR,
 +    MANUAL_MAP,
++    MANUAL_FILTER,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Matches {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
 +        let from_expansion = expr.span.from_expansion();
 +
 +        if let ExprKind::Match(ex, arms, source) = expr.kind {
 +            if source == MatchSource::Normal && !is_span_match(cx, expr.span) {
 +                return;
 +            }
 +            if matches!(source, MatchSource::Normal | MatchSource::ForLoopDesugar) {
 +                significant_drop_in_scrutinee::check(cx, expr, ex, arms, source);
 +            }
 +
 +            collapsible_match::check_match(cx, arms);
 +            if !from_expansion {
 +                // These don't depend on a relationship between multiple arms
 +                match_wild_err_arm::check(cx, ex, arms);
 +                wild_in_or_pats::check(cx, arms);
 +            }
 +
 +            if source == MatchSource::TryDesugar {
 +                try_err::check(cx, expr, ex);
 +            }
 +
 +            if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) {
 +                if source == MatchSource::Normal {
 +                    if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO)
 +                        && match_like_matches::check_match(cx, expr, ex, arms))
 +                    {
 +                        match_same_arms::check(cx, arms);
 +                    }
 +
 +                    redundant_pattern_match::check_match(cx, expr, ex, arms);
 +                    single_match::check(cx, ex, arms, expr);
 +                    match_bool::check(cx, ex, arms, expr);
 +                    overlapping_arms::check(cx, ex, arms);
 +                    match_wild_enum::check(cx, ex, arms);
 +                    match_as_ref::check(cx, ex, arms, expr);
 +                    needless_match::check_match(cx, ex, arms, expr);
 +                    match_on_vec_items::check(cx, ex);
 +                    match_str_case_mismatch::check(cx, ex, arms);
 +
 +                    if !in_constant(cx, expr.hir_id) {
 +                        manual_unwrap_or::check(cx, expr, ex, arms);
 +                        manual_map::check_match(cx, expr, ex, arms);
++                        manual_filter::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);
 +            }
 +        } else if let Some(if_let) = higher::IfLet::hir(cx, expr) {
 +            collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else);
 +            if !from_expansion {
 +                if let Some(else_expr) = if_let.if_else {
 +                    if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) {
 +                        match_like_matches::check_if_let(
 +                            cx,
 +                            expr,
 +                            if_let.let_pat,
 +                            if_let.let_expr,
 +                            if_let.if_then,
 +                            else_expr,
 +                        );
 +                    }
 +                    if !in_constant(cx, expr.hir_id) {
 +                        manual_map::check_if_let(cx, expr, if_let.let_pat, if_let.let_expr, if_let.if_then, else_expr);
++                        manual_filter::check_if_let(
++                            cx,
++                            expr,
++                            if_let.let_pat,
++                            if_let.let_expr,
++                            if_let.if_then,
++                            else_expr,
++                        );
 +                    }
 +                }
 +                redundant_pattern_match::check_if_let(
 +                    cx,
 +                    expr,
 +                    if_let.let_pat,
 +                    if_let.let_expr,
 +                    if_let.if_else.is_some(),
 +                );
 +                needless_match::check_if_let(cx, expr, &if_let);
 +            }
 +        } else if !from_expansion {
 +            redundant_pattern_match::check(cx, expr);
 +        }
 +    }
 +
 +    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
 +        self.infallible_destructuring_match_linted |=
 +            local.els.is_none() && 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_some((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_some(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 as usize;
 +        (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 d496107ffd6b85a76a903cf912663ca1fd62369b,0000000000000000000000000000000000000000..e5a15b2e1a1d2b454a8a5f488f6dfa1f7aa654fb
mode 100644,000000..100644
--- /dev/null
@@@ -1,244 -1,0 +1,243 @@@
- use clippy_utils::ty::{implements_trait, match_type, peel_mid_ty_refs};
- use clippy_utils::{
-     is_lint_allowed, is_unit_expr, is_wild, paths, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs,
- };
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::{expr_block, snippet};
-     let candidates = [&paths::COW, &paths::OPTION, &paths::RESULT];
++use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_refs};
++use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs};
 +use core::cmp::max;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, Ty};
++use rustc_span::sym;
 +
 +use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE};
 +
 +#[rustfmt::skip]
 +pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
 +    if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
 +        if expr.span.from_expansion() {
 +            // Don't lint match expressions present in
 +            // macro_rules! block
 +            return;
 +        }
 +        if let PatKind::Or(..) = arms[0].pat.kind {
 +            // don't lint for or patterns for now, this makes
 +            // the lint noisy in unnecessary situations
 +            return;
 +        }
 +        let els = arms[1].body;
 +        let els = if is_unit_expr(peel_blocks(els)) {
 +            None
 +        } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
 +            if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
 +                // single statement/expr "else" block, don't lint
 +                return;
 +            }
 +            // block with 2+ statements or 1 expr and 1+ statement
 +            Some(els)
 +        } else {
 +            // not a block, don't lint
 +            return;
 +        };
 +
 +        let ty = cx.typeck_results().expr_ty(ex);
 +        if *ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) {
 +            check_single_pattern(cx, ex, arms, expr, els);
 +            check_opt_like(cx, ex, arms, expr, ty, els);
 +        }
 +    }
 +}
 +
 +fn check_single_pattern(
 +    cx: &LateContext<'_>,
 +    ex: &Expr<'_>,
 +    arms: &[Arm<'_>],
 +    expr: &Expr<'_>,
 +    els: Option<&Expr<'_>>,
 +) {
 +    if is_wild(arms[1].pat) {
 +        report_single_pattern(cx, ex, arms, expr, els);
 +    }
 +}
 +
 +fn report_single_pattern(
 +    cx: &LateContext<'_>,
 +    ex: &Expr<'_>,
 +    arms: &[Arm<'_>],
 +    expr: &Expr<'_>,
 +    els: Option<&Expr<'_>>,
 +) {
 +    let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
 +    let els_str = els.map_or(String::new(), |els| {
 +        format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
 +    });
 +
 +    let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
 +    let (msg, sugg) = if_chain! {
 +        if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
 +        let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
 +        if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait();
 +        if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait();
 +        if ty.is_integral() || ty.is_char() || ty.is_str()
 +            || (implements_trait(cx, ty, spe_trait_id, &[])
 +                && implements_trait(cx, ty, pe_trait_id, &[ty.into()]));
 +        then {
 +            // scrutinee derives PartialEq and the pattern is a constant.
 +            let pat_ref_count = match pat.kind {
 +                // string literals are already a reference.
 +                PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1,
 +                _ => pat_ref_count,
 +            };
 +            // References are only implicitly added to the pattern, so no overflow here.
 +            // e.g. will work: match &Some(_) { Some(_) => () }
 +            // will not: match Some(_) { &Some(_) => () }
 +            let ref_count_diff = ty_ref_count - pat_ref_count;
 +
 +            // Try to remove address of expressions first.
 +            let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff);
 +            let ref_count_diff = ref_count_diff - removed;
 +
 +            let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`";
 +            let sugg = format!(
 +                "if {} == {}{} {}{els_str}",
 +                snippet(cx, ex.span, ".."),
 +                // PartialEq for different reference counts may not exist.
 +                "&".repeat(ref_count_diff),
 +                snippet(cx, arms[0].pat.span, ".."),
 +                expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
 +            );
 +            (msg, sugg)
 +        } else {
 +            let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
 +            let sugg = format!(
 +                "if let {} = {} {}{els_str}",
 +                snippet(cx, arms[0].pat.span, ".."),
 +                snippet(cx, ex.span, ".."),
 +                expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
 +            );
 +            (msg, sugg)
 +        }
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        lint,
 +        expr.span,
 +        msg,
 +        "try this",
 +        sugg,
 +        Applicability::HasPlaceholders,
 +    );
 +}
 +
 +fn check_opt_like<'a>(
 +    cx: &LateContext<'a>,
 +    ex: &Expr<'_>,
 +    arms: &[Arm<'_>],
 +    expr: &Expr<'_>,
 +    ty: Ty<'a>,
 +    els: Option<&Expr<'_>>,
 +) {
 +    // We don't want to lint if the second arm contains an enum which could
 +    // have more variants in the future.
 +    if form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat) {
 +        report_single_pattern(cx, ex, arms, expr, els);
 +    }
 +}
 +
 +/// Returns `true` if all of the types in the pattern are enums which we know
 +/// won't be expanded in the future
 +fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> bool {
 +    let mut paths_and_types = Vec::new();
 +    collect_pat_paths(&mut paths_and_types, cx, pat, ty);
 +    paths_and_types.iter().all(|ty| in_candidate_enum(cx, *ty))
 +}
 +
 +/// Returns `true` if the given type is an enum we know won't be expanded in the future
 +fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool {
 +    // list of candidate `Enum`s we know will never get any more members
-         if match_type(cx, ty, candidate_ty) {
++    let candidates = [sym::Cow, sym::Option, sym::Result];
 +
 +    for candidate_ty in candidates {
++        if is_type_diagnostic_item(cx, ty, candidate_ty) {
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +/// Collects types from the given pattern
 +fn collect_pat_paths<'a>(acc: &mut Vec<Ty<'a>>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) {
 +    match pat.kind {
 +        PatKind::Tuple(inner, _) => inner.iter().for_each(|p| {
 +            let p_ty = cx.typeck_results().pat_ty(p);
 +            collect_pat_paths(acc, cx, p, p_ty);
 +        }),
 +        PatKind::TupleStruct(..) | PatKind::Binding(BindingAnnotation::NONE, .., None) | PatKind::Path(_) => {
 +            acc.push(ty);
 +        },
 +        _ => {},
 +    }
 +}
 +
 +/// Returns true if the given arm of pattern matching contains wildcard patterns.
 +fn contains_only_wilds(pat: &Pat<'_>) -> bool {
 +    match pat.kind {
 +        PatKind::Wild => true,
 +        PatKind::Tuple(inner, _) | PatKind::TupleStruct(_, inner, ..) => inner.iter().all(contains_only_wilds),
 +        _ => false,
 +    }
 +}
 +
 +/// Returns true if the given patterns forms only exhaustive matches that don't contain enum
 +/// patterns without a wildcard.
 +fn form_exhaustive_matches<'a>(cx: &LateContext<'a>, ty: Ty<'a>, left: &Pat<'_>, right: &Pat<'_>) -> bool {
 +    match (&left.kind, &right.kind) {
 +        (PatKind::Wild, _) | (_, PatKind::Wild) => true,
 +        (PatKind::Tuple(left_in, left_pos), PatKind::Tuple(right_in, right_pos)) => {
 +            // We don't actually know the position and the presence of the `..` (dotdot) operator
 +            // in the arms, so we need to evaluate the correct offsets here in order to iterate in
 +            // both arms at the same time.
 +            let left_pos = left_pos.as_opt_usize();
 +            let right_pos = right_pos.as_opt_usize();
 +            let len = max(
 +                left_in.len() + usize::from(left_pos.is_some()),
 +                right_in.len() + usize::from(right_pos.is_some()),
 +            );
 +            let mut left_pos = left_pos.unwrap_or(usize::MAX);
 +            let mut right_pos = right_pos.unwrap_or(usize::MAX);
 +            let mut left_dot_space = 0;
 +            let mut right_dot_space = 0;
 +            for i in 0..len {
 +                let mut found_dotdot = false;
 +                if i == left_pos {
 +                    left_dot_space += 1;
 +                    if left_dot_space < len - left_in.len() {
 +                        left_pos += 1;
 +                    }
 +                    found_dotdot = true;
 +                }
 +                if i == right_pos {
 +                    right_dot_space += 1;
 +                    if right_dot_space < len - right_in.len() {
 +                        right_pos += 1;
 +                    }
 +                    found_dotdot = true;
 +                }
 +                if found_dotdot {
 +                    continue;
 +                }
 +                if !contains_only_wilds(&left_in[i - left_dot_space])
 +                    && !contains_only_wilds(&right_in[i - right_dot_space])
 +                {
 +                    return false;
 +                }
 +            }
 +            true
 +        },
 +        (PatKind::TupleStruct(..), PatKind::Path(_)) => pat_in_candidate_enum(cx, ty, right),
 +        (PatKind::TupleStruct(..), PatKind::TupleStruct(_, inner, _)) => {
 +            pat_in_candidate_enum(cx, ty, right) && inner.iter().all(contains_only_wilds)
 +        },
 +        _ => false,
 +    }
 +}
index be56b63506a4be475be73426c827852b1241c455,0000000000000000000000000000000000000000..8adf9e37059209d24d940d8c193263afda17bcae
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,54 @@@
-         let mutbl = match self_ref_ty.kind() {
-             ty::Ref(_, _, mutbl) => mutbl,
-             _ => unreachable!(),
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::is_trait_method;
 +use clippy_utils::ty::has_iter_method;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::{sym, Symbol};
 +
 +use super::INTO_ITER_ON_REF;
 +
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    expr: &hir::Expr<'_>,
 +    method_span: Span,
 +    method_name: Symbol,
 +    receiver: &hir::Expr<'_>,
 +) {
 +    let self_ty = cx.typeck_results().expr_ty_adjusted(receiver);
 +    if_chain! {
 +        if let ty::Ref(..) = self_ty.kind();
 +        if method_name == sym::into_iter;
 +        if is_trait_method(cx, expr, sym::IntoIterator);
 +        if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty);
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                INTO_ITER_ON_REF,
 +                method_span,
 +                &format!(
 +                    "this `.into_iter()` call is equivalent to `.{method_name}()` and will not consume the `{kind}`",
 +                ),
 +                "call directly",
 +                method_name.to_string(),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn ty_has_iter_method(cx: &LateContext<'_>, self_ref_ty: Ty<'_>) -> Option<(Symbol, &'static str)> {
 +    has_iter_method(cx, self_ref_ty).map(|ty_name| {
++        let ty::Ref(_, _, mutbl) = self_ref_ty.kind() else {
++            unreachable!()
 +        };
 +        let method_name = match mutbl {
 +            hir::Mutability::Not => "iter",
 +            hir::Mutability::Mut => "iter_mut",
 +        };
 +        (ty_name, method_name)
 +    })
 +}
index ec694cf6882e5c28a5ad490dee73401fccdb5da7,0000000000000000000000000000000000000000..b80541b86479a804227b1152718881ad7d2daf58
mode 100644,000000..100644
--- /dev/null
@@@ -1,163 -1,0 +1,157 @@@
-     let mm = if let Some(mm) = is_min_or_max(cx, unwrap_arg) {
-         mm
-     } else {
-         return;
-     };
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{match_def_path, path_def_id};
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::layout::LayoutOf;
 +
 +pub fn check(
 +    cx: &LateContext<'_>,
 +    expr: &hir::Expr<'_>,
 +    arith_lhs: &hir::Expr<'_>,
 +    arith_rhs: &hir::Expr<'_>,
 +    unwrap_arg: &hir::Expr<'_>,
 +    arith: &str,
 +) {
 +    let ty = cx.typeck_results().expr_ty(arith_lhs);
 +    if !ty.is_integral() {
 +        return;
 +    }
 +
-         let sign = if let Some(sign) = lit_sign(arith_rhs) {
-             sign
-         } else {
++    let Some(mm) = is_min_or_max(cx, unwrap_arg) else { return };
 +
 +    if ty.is_signed() {
 +        use self::{
 +            MinMax::{Max, Min},
 +            Sign::{Neg, Pos},
 +        };
 +
++        let Some(sign) = lit_sign(arith_rhs) else {
 +            return;
 +        };
 +
 +        match (arith, sign, mm) {
 +            ("add", Pos, Max) | ("add", Neg, Min) | ("sub", Neg, Max) | ("sub", Pos, Min) => (),
 +            // "mul" is omitted because lhs can be negative.
 +            _ => return,
 +        }
 +    } else {
 +        match (mm, arith) {
 +            (MinMax::Max, "add" | "mul") | (MinMax::Min, "sub") => (),
 +            _ => return,
 +        }
 +    }
 +
 +    let mut applicability = Applicability::MachineApplicable;
 +    span_lint_and_sugg(
 +        cx,
 +        super::MANUAL_SATURATING_ARITHMETIC,
 +        expr.span,
 +        "manual saturating arithmetic",
 +        &format!("try using `saturating_{arith}`"),
 +        format!(
 +            "{}.saturating_{arith}({})",
 +            snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
 +            snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
 +        ),
 +        applicability,
 +    );
 +}
 +
 +#[derive(PartialEq, Eq)]
 +enum MinMax {
 +    Min,
 +    Max,
 +}
 +
 +fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option<MinMax> {
 +    // `T::max_value()` `T::min_value()` inherent methods
 +    if_chain! {
 +        if let hir::ExprKind::Call(func, args) = &expr.kind;
 +        if args.is_empty();
 +        if let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind;
 +        then {
 +            match segment.ident.as_str() {
 +                "max_value" => return Some(MinMax::Max),
 +                "min_value" => return Some(MinMax::Min),
 +                _ => {}
 +            }
 +        }
 +    }
 +
 +    let ty = cx.typeck_results().expr_ty(expr);
 +    let ty_str = ty.to_string();
 +
 +    // `std::T::MAX` `std::T::MIN` constants
 +    if let Some(id) = path_def_id(cx, expr) {
 +        if match_def_path(cx, id, &["core", &ty_str, "MAX"]) {
 +            return Some(MinMax::Max);
 +        }
 +
 +        if match_def_path(cx, id, &["core", &ty_str, "MIN"]) {
 +            return Some(MinMax::Min);
 +        }
 +    }
 +
 +    // Literals
 +    let bits = cx.layout_of(ty).unwrap().size.bits();
 +    let (minval, maxval): (u128, u128) = if ty.is_signed() {
 +        let minval = 1 << (bits - 1);
 +        let mut maxval = !(1 << (bits - 1));
 +        if bits != 128 {
 +            maxval &= (1 << bits) - 1;
 +        }
 +        (minval, maxval)
 +    } else {
 +        (0, if bits == 128 { !0 } else { (1 << bits) - 1 })
 +    };
 +
 +    let check_lit = |expr: &hir::Expr<'_>, check_min: bool| {
 +        if let hir::ExprKind::Lit(lit) = &expr.kind {
 +            if let ast::LitKind::Int(value, _) = lit.node {
 +                if value == maxval {
 +                    return Some(MinMax::Max);
 +                }
 +
 +                if check_min && value == minval {
 +                    return Some(MinMax::Min);
 +                }
 +            }
 +        }
 +
 +        None
 +    };
 +
 +    if let r @ Some(_) = check_lit(expr, !ty.is_signed()) {
 +        return r;
 +    }
 +
 +    if ty.is_signed() {
 +        if let hir::ExprKind::Unary(hir::UnOp::Neg, val) = &expr.kind {
 +            return check_lit(val, true);
 +        }
 +    }
 +
 +    None
 +}
 +
 +#[derive(PartialEq, Eq)]
 +enum Sign {
 +    Pos,
 +    Neg,
 +}
 +
 +fn lit_sign(expr: &hir::Expr<'_>) -> Option<Sign> {
 +    if let hir::ExprKind::Unary(hir::UnOp::Neg, inner) = &expr.kind {
 +        if let hir::ExprKind::Lit(..) = &inner.kind {
 +            return Some(Sign::Neg);
 +        }
 +    } else if let hir::ExprKind::Lit(..) = &expr.kind {
 +        return Some(Sign::Pos);
 +    }
 +
 +    None
 +}
index cfcf9596c50d3f32638475a2f06277916257195e,0000000000000000000000000000000000000000..fb92779be2a7a54b9e58dd83bacb4d5416d40016
mode 100644,000000..100644
--- /dev/null
@@@ -1,3920 -1,0 +1,3921 @@@
- use clippy_utils::{
-     contains_return, get_trait_def_id, is_trait_method, iter_input_pats, meets_msrv, msrvs, paths, return_ty,
- };
 +mod bind_instead_of_map;
 +mod bytecount;
 +mod bytes_count_to_len;
 +mod bytes_nth;
 +mod case_sensitive_file_extension_comparisons;
 +mod chars_cmp;
 +mod chars_cmp_with_unwrap;
 +mod chars_last_cmp;
 +mod chars_last_cmp_with_unwrap;
 +mod chars_next_cmp;
 +mod chars_next_cmp_with_unwrap;
 +mod clone_on_copy;
 +mod clone_on_ref_ptr;
 +mod cloned_instead_of_copied;
 +mod collapsible_str_replace;
 +mod err_expect;
 +mod expect_fun_call;
 +mod expect_used;
 +mod extend_with_drain;
 +mod filetype_is_file;
 +mod filter_map;
 +mod filter_map_identity;
 +mod filter_map_next;
 +mod filter_next;
 +mod flat_map_identity;
 +mod flat_map_option;
 +mod from_iter_instead_of_collect;
 +mod get_first;
 +mod get_last_with_len;
 +mod get_unwrap;
 +mod implicit_clone;
 +mod inefficient_to_string;
 +mod inspect_for_each;
 +mod into_iter_on_ref;
 +mod is_digit_ascii_radix;
 +mod iter_cloned_collect;
 +mod iter_count;
 +mod iter_kv_map;
 +mod iter_next_slice;
 +mod iter_nth;
 +mod iter_nth_zero;
 +mod iter_on_single_or_empty_collections;
 +mod iter_overeager_cloned;
 +mod iter_skip_next;
 +mod iter_with_drain;
 +mod iterator_step_by_zero;
 +mod manual_ok_or;
 +mod manual_saturating_arithmetic;
 +mod manual_str_repeat;
 +mod map_clone;
 +mod map_collect_result_unit;
 +mod map_err_ignore;
 +mod map_flatten;
 +mod map_identity;
 +mod map_unwrap_or;
 +mod mut_mutex_lock;
 +mod needless_option_as_deref;
 +mod needless_option_take;
 +mod no_effect_replace;
 +mod obfuscated_if_else;
 +mod ok_expect;
 +mod open_options;
 +mod option_as_ref_deref;
 +mod option_map_or_none;
 +mod option_map_unwrap_or;
 +mod or_fun_call;
 +mod or_then_unwrap;
 +mod path_buf_push_overwrite;
 +mod range_zip_with_len;
 +mod repeat_once;
 +mod search_is_some;
 +mod single_char_add_str;
 +mod single_char_insert_string;
 +mod single_char_pattern;
 +mod single_char_push_string;
 +mod skip_while_next;
 +mod stable_sort_primitive;
 +mod str_splitn;
 +mod string_extend_chars;
 +mod suspicious_map;
 +mod suspicious_splitn;
 +mod suspicious_to_owned;
 +mod uninit_assumed_init;
 +mod unit_hash;
 +mod unnecessary_filter_map;
 +mod unnecessary_fold;
 +mod unnecessary_iter_cloned;
 +mod unnecessary_join;
 +mod unnecessary_lazy_eval;
 +mod unnecessary_sort_by;
 +mod unnecessary_to_owned;
 +mod unwrap_or_else_default;
 +mod unwrap_used;
 +mod useless_asref;
 +mod utils;
 +mod vec_resize_to_zero;
 +mod verbose_file_reads;
 +mod wrong_self_convention;
 +mod zst_offset;
 +
 +use bind_instead_of_map::BindInsteadOfMap;
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 +use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item};
-                 let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
++use clippy_utils::{contains_return, is_trait_method, iter_input_pats, meets_msrv, msrvs, 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_hir_analysis::hir_ty_to_ty;
 +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};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `cloned()` on an `Iterator` or `Option` where
 +    /// `copied()` could be used instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// `copied()` is better because it guarantees that the type being cloned
 +    /// implements `Copy`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// [1, 2, 3].iter().cloned();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// [1, 2, 3].iter().copied();
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub CLONED_INSTEAD_OF_COPIED,
 +    pedantic,
 +    "used `cloned` where `copied` could be used instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for consecutive calls to `str::replace` (2 or more)
 +    /// that can be collapsed into a single call.
 +    ///
 +    /// ### Why is this bad?
 +    /// Consecutive `str::replace` calls scan the string multiple times
 +    /// with repetitive code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let hello = "hesuo worpd"
 +    ///     .replace('s', "l")
 +    ///     .replace("u", "l")
 +    ///     .replace('p', "l");
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l");
 +    /// ```
 +    #[clippy::version = "1.64.0"]
 +    pub COLLAPSIBLE_STR_REPLACE,
 +    perf,
 +    "collapse consecutive calls to str::replace (2 or more) into a single call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's often inefficient to clone all elements of an iterator, when eventually, only some
 +    /// of them will be consumed.
 +    ///
 +    /// ### Known Problems
 +    /// This `lint` removes the side of effect of cloning items in the iterator.
 +    /// A code that relies on that side-effect could fail.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let vec = vec!["string".to_string()];
 +    /// vec.iter().cloned().take(10);
 +    /// vec.iter().cloned().last();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec!["string".to_string()];
 +    /// vec.iter().take(10).cloned();
 +    /// vec.iter().last().cloned();
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub ITER_OVEREAGER_CLONED,
 +    perf,
 +    "using `cloned()` early with `Iterator::iter()` can lead to some performance inefficiencies"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
 +    /// used instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// When applicable, `filter_map()` is more clear since it shows that
 +    /// `Option` is used to produce 0 or 1 items.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub FLAT_MAP_OPTION,
 +    pedantic,
 +    "used `flat_map` where `filter_map` could be used instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.unwrap()` or `.unwrap_err()` calls on `Result`s and `.unwrap()` call on `Option`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is better to handle the `None` or `Err` case,
 +    /// or at least call `.expect(_)` with a more helpful message. Still, for a lot of
 +    /// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is
 +    /// `Allow` by default.
 +    ///
 +    /// `result.unwrap()` will let the thread panic on `Err` values.
 +    /// Normally, you want to implement more sophisticated error handling,
 +    /// and propagate errors upwards with `?` operator.
 +    ///
 +    /// Even if you want to panic on errors, not all `Error`s implement good
 +    /// messages on display. Therefore, it may be beneficial to look at the places
 +    /// where they may get displayed. Activate this lint to do just that.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option.unwrap();
 +    /// result.unwrap();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option.expect("more helpful message");
 +    /// result.expect("more helpful message");
 +    /// ```
 +    ///
 +    /// If [expect_used](#expect_used) is enabled, instead:
 +    /// ```rust,ignore
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option?;
 +    ///
 +    /// // or
 +    ///
 +    /// result?;
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub UNWRAP_USED,
 +    restriction,
 +    "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// Usually it is better to handle the `None` or `Err` case.
 +    /// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why
 +    /// this lint is `Allow` by default.
 +    ///
 +    /// `result.expect()` will let the thread panic on `Err`
 +    /// values. Normally, you want to implement more sophisticated error handling,
 +    /// and propagate errors upwards with `?` operator.
 +    ///
 +    /// ### Examples
 +    /// ```rust,ignore
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option.expect("one");
 +    /// result.expect("one");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option?;
 +    ///
 +    /// // or
 +    ///
 +    /// result?;
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub EXPECT_USED,
 +    restriction,
 +    "using `.expect()` on `Result` or `Option`, which might be better handled"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for methods that should live in a trait
 +    /// implementation of a `std` trait (see [llogiq's blog
 +    /// post](http://llogiq.github.io/2015/07/30/traits.html) for further
 +    /// information) instead of an inherent implementation.
 +    ///
 +    /// ### Why is this bad?
 +    /// Implementing the traits improve ergonomics for users of
 +    /// the code, often with very little cost. Also people seeing a `mul(...)`
 +    /// method
 +    /// may expect `*` to work equally, so you should have good reason to disappoint
 +    /// them.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct X;
 +    /// impl X {
 +    ///     fn add(&self, other: &X) -> X {
 +    ///         // ..
 +    /// # X
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SHOULD_IMPLEMENT_TRAIT,
 +    style,
 +    "defining a method that should be implementing a std trait"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for methods with certain name prefixes and which
 +    /// doesn't match how self is taken. The actual rules are:
 +    ///
 +    /// |Prefix |Postfix     |`self` taken                   | `self` type  |
 +    /// |-------|------------|-------------------------------|--------------|
 +    /// |`as_`  | none       |`&self` or `&mut self`         | any          |
 +    /// |`from_`| none       | none                          | any          |
 +    /// |`into_`| none       |`self`                         | any          |
 +    /// |`is_`  | none       |`&mut self` or `&self` or none | any          |
 +    /// |`to_`  | `_mut`     |`&mut self`                    | any          |
 +    /// |`to_`  | not `_mut` |`self`                         | `Copy`       |
 +    /// |`to_`  | not `_mut` |`&self`                        | not `Copy`   |
 +    ///
 +    /// Note: Clippy doesn't trigger methods with `to_` prefix in:
 +    /// - Traits definition.
 +    /// Clippy can not tell if a type that implements a trait is `Copy` or not.
 +    /// - Traits implementation, when `&self` is taken.
 +    /// The method signature is controlled by the trait and often `&self` is required for all types that implement the trait
 +    /// (see e.g. the `std::string::ToString` trait).
 +    ///
 +    /// Clippy allows `Pin<&Self>` and `Pin<&mut Self>` if `&self` and `&mut self` is required.
 +    ///
 +    /// Please find more info here:
 +    /// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
 +    ///
 +    /// ### Why is this bad?
 +    /// Consistency breeds readability. If you follow the
 +    /// conventions, your users won't be surprised that they, e.g., need to supply a
 +    /// mutable reference to a `as_..` function.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct X;
 +    /// impl X {
 +    ///     fn as_str(self) -> &'static str {
 +    ///         // ..
 +    /// # ""
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRONG_SELF_CONVENTION,
 +    style,
 +    "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `ok().expect(..)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Because you usually call `expect()` on the `Result`
 +    /// directly to get a better error message.
 +    ///
 +    /// ### Known problems
 +    /// The error type needs to implement `Debug`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = Ok::<_, ()>(());
 +    /// x.ok().expect("why did I do this again?");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = Ok::<_, ()>(());
 +    /// x.expect("why did I do this again?");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OK_EXPECT,
 +    style,
 +    "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.err().expect()` calls on the `Result` type.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.expect_err()` can be called directly to avoid the extra type conversion from `err()`.
 +    ///
 +    /// ### Example
 +    /// ```should_panic
 +    /// let x: Result<u32, &str> = Ok(10);
 +    /// x.err().expect("Testing err().expect()");
 +    /// ```
 +    /// Use instead:
 +    /// ```should_panic
 +    /// let x: Result<u32, &str> = Ok(10);
 +    /// x.expect_err("Testing expect_err");
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub ERR_EXPECT,
 +    style,
 +    r#"using `.err().expect("")` when `.expect_err("")` can be used"#
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and
 +    /// `Result` values.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, these can be written as `_.unwrap_or_default`, which is
 +    /// simpler and more concise.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let x = Some(1);
 +    /// x.unwrap_or_else(Default::default);
 +    /// x.unwrap_or_else(u32::default);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = Some(1);
 +    /// x.unwrap_or_default();
 +    /// ```
 +    #[clippy::version = "1.56.0"]
 +    pub UNWRAP_OR_ELSE_DEFAULT,
 +    style,
 +    "using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or
 +    /// `result.map(_).unwrap_or_else(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, these can be written more concisely (resp.) as
 +    /// `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.
 +    ///
 +    /// ### Known problems
 +    /// The order of the arguments is not in execution order
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// # fn some_function(foo: ()) -> usize { 1 }
 +    /// option.map(|a| a + 1).unwrap_or(0);
 +    /// result.map(|a| a + 1).unwrap_or_else(some_function);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// # fn some_function(foo: ()) -> usize { 1 }
 +    /// option.map_or(0, |a| a + 1);
 +    /// result.map_or_else(some_function, |a| a + 1);
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub MAP_UNWRAP_OR,
 +    pedantic,
 +    "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map_or(None, _)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.and_then(_)`.
 +    ///
 +    /// ### Known problems
 +    /// The order of the arguments is not in execution order.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    /// opt.map_or(None, |a| Some(a + 1));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    /// opt.and_then(|a| Some(a + 1));
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OPTION_MAP_OR_NONE,
 +    style,
 +    "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map_or(None, Some)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.ok()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let r: Result<u32, &str> = Ok(1);
 +    /// assert_eq!(Some(1), r.map_or(None, Some));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let r: Result<u32, &str> = Ok(1);
 +    /// assert_eq!(Some(1), r.ok());
 +    /// ```
 +    #[clippy::version = "1.44.0"]
 +    pub RESULT_MAP_OR_INTO_OPTION,
 +    style,
 +    "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or
 +    /// `_.or_else(|x| Err(y))`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.map(|x| y)` or `_.map_err(|x| y)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn opt() -> Option<&'static str> { Some("42") }
 +    /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
 +    /// let _ = opt().and_then(|s| Some(s.len()));
 +    /// let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });
 +    /// let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });
 +    /// ```
 +    ///
 +    /// The correct use would be:
 +    ///
 +    /// ```rust
 +    /// # fn opt() -> Option<&'static str> { Some("42") }
 +    /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
 +    /// let _ = opt().map(|s| s.len());
 +    /// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });
 +    /// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub BIND_INSTEAD_OF_MAP,
 +    complexity,
 +    "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter(_).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().filter(|x| **x == 0).next();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().find(|x| **x == 0);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FILTER_NEXT,
 +    complexity,
 +    "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.skip_while(condition).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find(!condition)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().skip_while(|x| **x == 0).next();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().find(|x| **x != 0);
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub SKIP_WHILE_NEXT,
 +    complexity,
 +    "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map(_).flatten(_)` on `Iterator` and `Option`
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.flat_map(_)` for `Iterator` or `_.and_then(_)` for `Option`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec = vec![vec![1]];
 +    /// let opt = Some(5);
 +    ///
 +    /// vec.iter().map(|x| x.iter()).flatten();
 +    /// opt.map(|x| Some(x * 2)).flatten();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![vec![1]];
 +    /// # let opt = Some(5);
 +    /// vec.iter().flat_map(|x| x.iter());
 +    /// opt.and_then(|x| Some(x * 2));
 +    /// ```
 +    #[clippy::version = "1.31.0"]
 +    pub MAP_FLATTEN,
 +    complexity,
 +    "using combinations of `flatten` and `map` which can usually be written as a single method call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter(_).map(_)` that can be written more simply
 +    /// as `filter_map(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code in the `filter` and `map` operations is poor style and
 +    /// less performant.
 +    ///
 +     /// ### Example
 +    /// ```rust
 +    /// # #![allow(unused)]
 +    /// (0_i32..10)
 +    ///     .filter(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// (0_i32..10).filter_map(|n| n.checked_add(1));
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub MANUAL_FILTER_MAP,
 +    complexity,
 +    "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.find(_).map(_)` that can be written more simply
 +    /// as `find_map(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code in the `find` and `map` operations is poor style and
 +    /// less performant.
 +    ///
 +     /// ### Example
 +    /// ```rust
 +    /// (0_i32..10)
 +    ///     .find(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// (0_i32..10).find_map(|n| n.checked_add(1));
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub MANUAL_FIND_MAP,
 +    complexity,
 +    "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter_map(_).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find_map(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    ///  (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();
 +    /// ```
 +    /// Can be written as
 +    ///
 +    /// ```rust
 +    ///  (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
 +    /// ```
 +    #[clippy::version = "1.36.0"]
 +    pub FILTER_MAP_NEXT,
 +    pedantic,
 +    "using combination of `filter_map` and `next` which can usually be written as a single method call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `flat_map(|x| x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely by using `flatten`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iter = vec![vec![0]].into_iter();
 +    /// iter.flat_map(|x| x);
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// # let iter = vec![vec![0]].into_iter();
 +    /// iter.flatten();
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub FLAT_MAP_IDENTITY,
 +    complexity,
 +    "call to `flat_map` where `flatten` is sufficient"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for an iterator or string search (such as `find()`,
 +    /// `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as:
 +    /// * `_.any(_)`, or `_.contains(_)` for `is_some()`,
 +    /// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #![allow(unused)]
 +    /// let vec = vec![1];
 +    /// vec.iter().find(|x| **x == 0).is_some();
 +    ///
 +    /// "hello world".find("world").is_none();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let vec = vec![1];
 +    /// vec.iter().any(|x| *x == 0);
 +    ///
 +    /// # #[allow(unused)]
 +    /// !"hello world".contains("world");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SEARCH_IS_SOME,
 +    complexity,
 +    "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.chars().next()` on a `str` to check
 +    /// if it starts with a given char.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.starts_with(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let name = "foo";
 +    /// if name.chars().next() == Some('_') {};
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let name = "foo";
 +    /// if name.starts_with('_') {};
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CHARS_NEXT_CMP,
 +    style,
 +    "using `.chars().next()` to check if a string starts with a char"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,
 +    /// `.or_insert(foo(..))` etc., and suggests to use `.or_else(|| foo(..))`,
 +    /// `.unwrap_or_else(|| foo(..))`, `.unwrap_or_default()` or `.or_default()`
 +    /// etc. instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// The function will always be called and potentially
 +    /// allocate an object acting as the default.
 +    ///
 +    /// ### Known problems
 +    /// If the function has side-effects, not calling it will
 +    /// change the semantic of the program, but you shouldn't rely on that anyway.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or(String::new());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or_else(String::new);
 +    ///
 +    /// // or
 +    ///
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or_default();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OR_FUN_CALL,
 +    perf,
 +    "using any `*or` method with a function call, which suggests `*or_else`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.or(…).unwrap()` calls to Options and Results.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `.unwrap_or(…)` instead for clarity.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let fallback = "fallback";
 +    /// // Result
 +    /// # type Error = &'static str;
 +    /// # let result: Result<&str, Error> = Err("error");
 +    /// let value = result.or::<Error>(Ok(fallback)).unwrap();
 +    ///
 +    /// // Option
 +    /// # let option: Option<&str> = None;
 +    /// let value = option.or(Some(fallback)).unwrap();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let fallback = "fallback";
 +    /// // Result
 +    /// # let result: Result<&str, &str> = Err("error");
 +    /// let value = result.unwrap_or(fallback);
 +    ///
 +    /// // Option
 +    /// # let option: Option<&str> = None;
 +    /// let value = option.unwrap_or(fallback);
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub OR_THEN_UNWRAP,
 +    complexity,
 +    "checks for `.or(…).unwrap()` calls to Options and Results."
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,
 +    /// etc., and suggests to use `unwrap_or_else` instead
 +    ///
 +    /// ### Why is this bad?
 +    /// The function will always be called.
 +    ///
 +    /// ### Known problems
 +    /// If the function has side-effects, not calling it will
 +    /// change the semantics of the program, but you shouldn't rely on that anyway.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # let err_msg = "I'm a teapot";
 +    /// foo.expect(&format!("Err {}: {}", err_code, err_msg));
 +    ///
 +    /// // or
 +    ///
 +    /// # let foo = Some(String::new());
 +    /// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # let err_msg = "I'm a teapot";
 +    /// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXPECT_FUN_CALL,
 +    perf,
 +    "using any `expect` method with a function call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on a `Copy` type.
 +    ///
 +    /// ### Why is this bad?
 +    /// The only reason `Copy` types implement `Clone` is for
 +    /// generics, not for using the `clone` method on a concrete type.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// 42u64.clone();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CLONE_ON_COPY,
 +    complexity,
 +    "using `clone` on a `Copy` type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on a ref-counted pointer,
 +    /// (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified
 +    /// function syntax instead (e.g., `Rc::clone(foo)`).
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling '.clone()' on an Rc, Arc, or Weak
 +    /// can obscure the fact that only the pointer is being cloned, not the underlying
 +    /// data.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// let x = Rc::new(1);
 +    ///
 +    /// x.clone();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// # let x = Rc::new(1);
 +    /// Rc::clone(&x);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CLONE_ON_REF_PTR,
 +    restriction,
 +    "using 'clone' on a ref-counted pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on an `&&T`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Cloning an `&&T` copies the inner `&T`, instead of
 +    /// cloning the underlying `T`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn main() {
 +    ///     let x = vec![1];
 +    ///     let y = &&x;
 +    ///     let z = y.clone();
 +    ///     println!("{:p} {:p}", *y, z); // prints out the same pointer
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CLONE_DOUBLE_REF,
 +    correctness,
 +    "using `clone` on `&&T`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.to_string()` on an `&&T` where
 +    /// `T` implements `ToString` directly (like `&&str` or `&&String`).
 +    ///
 +    /// ### Why is this bad?
 +    /// This bypasses the specialized implementation of
 +    /// `ToString` and instead goes through the more expensive string formatting
 +    /// facilities.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Generic implementation for `T: Display` is used (slow)
 +    /// ["foo", "bar"].iter().map(|s| s.to_string());
 +    ///
 +    /// // OK, the specialized impl is used
 +    /// ["foo", "bar"].iter().map(|&s| s.to_string());
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub INEFFICIENT_TO_STRING,
 +    pedantic,
 +    "using `to_string` on `&&T` where `T: ToString`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `new` not returning a type that contains `Self`.
 +    ///
 +    /// ### Why is this bad?
 +    /// As a convention, `new` methods are used to make a new
 +    /// instance of a type.
 +    ///
 +    /// ### Example
 +    /// In an impl block:
 +    /// ```rust
 +    /// # struct Foo;
 +    /// # struct NotAFoo;
 +    /// impl Foo {
 +    ///     fn new() -> NotAFoo {
 +    /// # NotAFoo
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # struct Foo;
 +    /// struct Bar(Foo);
 +    /// impl Foo {
 +    ///     // Bad. The type name must contain `Self`
 +    ///     fn new() -> Bar {
 +    /// # Bar(Foo)
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # struct Foo;
 +    /// # struct FooError;
 +    /// impl Foo {
 +    ///     // Good. Return type contains `Self`
 +    ///     fn new() -> Result<Foo, FooError> {
 +    /// # Ok(Foo)
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Or in a trait definition:
 +    /// ```rust
 +    /// pub trait Trait {
 +    ///     // Bad. The type name must contain `Self`
 +    ///     fn new();
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// pub trait Trait {
 +    ///     // Good. Return type contains `Self`
 +    ///     fn new() -> Self;
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEW_RET_NO_SELF,
 +    style,
 +    "not returning type containing `Self` in a `new` method"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for string methods that receive a single-character
 +    /// `str` as an argument, e.g., `_.split("x")`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Performing these methods using a `char` is faster than
 +    /// using a `str`.
 +    ///
 +    /// ### Known problems
 +    /// Does not catch multi-byte unicode characters.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// _.split("x");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// _.split('x');
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SINGLE_CHAR_PATTERN,
 +    perf,
 +    "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calling `.step_by(0)` on iterators which panics.
 +    ///
 +    /// ### Why is this bad?
 +    /// This very much looks like an oversight. Use `panic!()` instead if you
 +    /// actually intend to panic.
 +    ///
 +    /// ### Example
 +    /// ```rust,should_panic
 +    /// for x in (0..100).step_by(0) {
 +    ///     //..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITERATOR_STEP_BY_ZERO,
 +    correctness,
 +    "using `Iterator::step_by(0)`, which will panic at runtime"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for indirect collection of populated `Option`
 +    ///
 +    /// ### Why is this bad?
 +    /// `Option` is like a collection of 0-1 things, so `flatten`
 +    /// automatically does this without suspicious-looking `unwrap` calls.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = std::iter::empty::<Option<i32>>().filter(Option::is_some).map(Option::unwrap);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let _ = std::iter::empty::<Option<i32>>().flatten();
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub OPTION_FILTER_MAP,
 +    complexity,
 +    "filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `iter.nth(0)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `iter.next()` is equivalent to
 +    /// `iter.nth(0)`, as they both consume the next element,
 +    ///  but is more readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// # let mut s = HashSet::new();
 +    /// # s.insert(1);
 +    /// let x = s.iter().nth(0);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// # let mut s = HashSet::new();
 +    /// # s.insert(1);
 +    /// let x = s.iter().next();
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub ITER_NTH_ZERO,
 +    style,
 +    "replace `iter.nth(0)` with `iter.next()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.iter().nth()` (and the related
 +    /// `.iter_mut().nth()`) on standard library types with *O*(1) element access.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.get()` and `.get_mut()` are more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().nth(3);
 +    /// let bad_slice = &some_vec[..].iter().nth(3);
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.get(3);
 +    /// let bad_slice = &some_vec[..].get(3);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITER_NTH,
 +    perf,
 +    "using `.iter().nth()` on a standard library type with O(1) element access"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.skip(x).next()` on iterators.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.nth(x)` is cleaner
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().skip(3).next();
 +    /// let bad_slice = &some_vec[..].iter().skip(3).next();
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().nth(3);
 +    /// let bad_slice = &some_vec[..].iter().nth(3);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITER_SKIP_NEXT,
 +    style,
 +    "using `.skip(x).next()` on an iterator"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.drain(..)` on `Vec` and `VecDeque` for iteration.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.into_iter()` is simpler with better performance.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// let mut foo = vec![0, 1, 2, 3];
 +    /// let bar: HashSet<usize> = foo.drain(..).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// let foo = vec![0, 1, 2, 3];
 +    /// let bar: HashSet<usize> = foo.into_iter().collect();
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub ITER_WITH_DRAIN,
 +    nursery,
 +    "replace `.drain(..)` with `.into_iter()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for using `x.get(x.len() - 1)` instead of
 +    /// `x.last()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `x.last()` is easier to read and has the same
 +    /// result.
 +    ///
 +    /// Note that using `x[x.len() - 1]` is semantically different from
 +    /// `x.last()`.  Indexing into the array will panic on out-of-bounds
 +    /// accesses, while `x.get()` and `x.last()` will return `None`.
 +    ///
 +    /// There is another lint (get_unwrap) that covers the case of using
 +    /// `x.get(index).unwrap()` instead of `x[index]`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = vec![2, 3, 5];
 +    /// let last_element = x.get(x.len() - 1);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = vec![2, 3, 5];
 +    /// let last_element = x.last();
 +    /// ```
 +    #[clippy::version = "1.37.0"]
 +    pub GET_LAST_WITH_LEN,
 +    complexity,
 +    "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.get().unwrap()` (or
 +    /// `.get_mut().unwrap`) on a standard library type which implements `Index`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the Index trait (`[]`) is more clear and more
 +    /// concise.
 +    ///
 +    /// ### Known problems
 +    /// Not a replacement for error handling: Using either
 +    /// `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`
 +    /// if the value being accessed is `None`. If the use of `.get().unwrap()` is a
 +    /// temporary placeholder for dealing with the `Option` type, then this does
 +    /// not mitigate the need for error handling. If there is a chance that `.get()`
 +    /// will be `None` in your program, then it is advisable that the `None` case
 +    /// is handled in a future refactor instead of using `.unwrap()` or the Index
 +    /// trait.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut some_vec = vec![0, 1, 2, 3];
 +    /// let last = some_vec.get(3).unwrap();
 +    /// *some_vec.get_mut(0).unwrap() = 1;
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let mut some_vec = vec![0, 1, 2, 3];
 +    /// let last = some_vec[3];
 +    /// some_vec[0] = 1;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub GET_UNWRAP,
 +    restriction,
 +    "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for occurrences where one vector gets extended instead of append
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `append` instead of `extend` is more concise and faster
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut a = vec![1, 2, 3];
 +    /// let mut b = vec![4, 5, 6];
 +    ///
 +    /// a.extend(b.drain(..));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let mut a = vec![1, 2, 3];
 +    /// let mut b = vec![4, 5, 6];
 +    ///
 +    /// a.append(&mut b);
 +    /// ```
 +    #[clippy::version = "1.55.0"]
 +    pub EXTEND_WITH_DRAIN,
 +    perf,
 +    "using vec.append(&mut vec) to move the full range of a vector to another"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.extend(s.chars())` where s is a
 +    /// `&str` or `String`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.push_str(s)` is clearer
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let abc = "abc";
 +    /// let def = String::from("def");
 +    /// let mut s = String::new();
 +    /// s.extend(abc.chars());
 +    /// s.extend(def.chars());
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let abc = "abc";
 +    /// let def = String::from("def");
 +    /// let mut s = String::new();
 +    /// s.push_str(abc);
 +    /// s.push_str(&def);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub STRING_EXTEND_CHARS,
 +    style,
 +    "using `x.extend(s.chars())` where s is a `&str` or `String`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.cloned().collect()` on slice to
 +    /// create a `Vec`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.to_vec()` is clearer
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let s = [1, 2, 3, 4, 5];
 +    /// let s2: Vec<isize> = s[..].iter().cloned().collect();
 +    /// ```
 +    /// The better use would be:
 +    /// ```rust
 +    /// let s = [1, 2, 3, 4, 5];
 +    /// let s2: Vec<isize> = s.to_vec();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITER_CLONED_COLLECT,
 +    style,
 +    "using `.cloned().collect()` on slice to create a `Vec`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.chars().last()` or
 +    /// `_.chars().next_back()` on a `str` to check if it ends with a given char.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.ends_with(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let name = "_";
 +    /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-');
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let name = "_";
 +    /// name.ends_with('_') || name.ends_with('-');
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CHARS_LAST_CMP,
 +    style,
 +    "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.as_ref()` or `.as_mut()` where the
 +    /// types before and after the call are the same.
 +    ///
 +    /// ### Why is this bad?
 +    /// The call is unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn do_stuff(x: &[i32]) {}
 +    /// let x: &[i32] = &[1, 2, 3, 4, 5];
 +    /// do_stuff(x.as_ref());
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// # fn do_stuff(x: &[i32]) {}
 +    /// let x: &[i32] = &[1, 2, 3, 4, 5];
 +    /// do_stuff(x);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USELESS_ASREF,
 +    complexity,
 +    "using `as_ref` where the types before and after the call are the same"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for using `fold` when a more succinct alternative exists.
 +    /// Specifically, this checks for `fold`s which could be replaced by `any`, `all`,
 +    /// `sum` or `product`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// (0..3).fold(false, |acc, x| acc || x > 2);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// (0..3).any(|x| x > 2);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNNECESSARY_FOLD,
 +    style,
 +    "using `fold` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `filter_map` calls that could be replaced by `filter` or `map`.
 +    /// More specifically it checks if the closure provided is only performing one of the
 +    /// filter or map operations and suggests the appropriate option.
 +    ///
 +    /// ### Why is this bad?
 +    /// Complexity. The intent is also clearer if only a single
 +    /// operation is being performed.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });
 +    ///
 +    /// // As there is no transformation of the argument this could be written as:
 +    /// let _ = (0..3).filter(|&x| x > 2);
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// let _ = (0..4).filter_map(|x| Some(x + 1));
 +    ///
 +    /// // As there is no conditional check on the argument this could be written as:
 +    /// let _ = (0..4).map(|x| x + 1);
 +    /// ```
 +    #[clippy::version = "1.31.0"]
 +    pub UNNECESSARY_FILTER_MAP,
 +    complexity,
 +    "using `filter_map` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `find_map` calls that could be replaced by `find` or `map`. More
 +    /// specifically it checks if the closure provided is only performing one of the
 +    /// find or map operations and suggests the appropriate option.
 +    ///
 +    /// ### Why is this bad?
 +    /// Complexity. The intent is also clearer if only a single
 +    /// operation is being performed.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).find_map(|x| if x > 2 { Some(x) } else { None });
 +    ///
 +    /// // As there is no transformation of the argument this could be written as:
 +    /// let _ = (0..3).find(|&x| x > 2);
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// let _ = (0..4).find_map(|x| Some(x + 1));
 +    ///
 +    /// // As there is no conditional check on the argument this could be written as:
 +    /// let _ = (0..4).map(|x| x + 1).next();
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub UNNECESSARY_FIND_MAP,
 +    complexity,
 +    "using `find_map` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `into_iter` calls on references which should be replaced by `iter`
 +    /// or `iter_mut`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. Calling `into_iter` on a reference will not move out its
 +    /// content into the resulting iterator, which is confusing. It is better just call `iter` or
 +    /// `iter_mut` directly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![3, 4, 5];
 +    /// (&vec).into_iter();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![3, 4, 5];
 +    /// (&vec).iter();
 +    /// ```
 +    #[clippy::version = "1.32.0"]
 +    pub INTO_ITER_ON_REF,
 +    style,
 +    "using `.into_iter()` on a reference"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `map` followed by a `count`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It looks suspicious. Maybe `map` was confused with `filter`.
 +    /// If the `map` call is intentional, this should be rewritten
 +    /// using `inspect`. Or, if you intend to drive the iterator to
 +    /// completion, you can just use `for_each` instead.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).map(|x| x + 2).count();
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub SUSPICIOUS_MAP,
 +    suspicious,
 +    "suspicious usage of map"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `MaybeUninit::uninit().assume_init()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// For most types, this is undefined behavior.
 +    ///
 +    /// ### Known problems
 +    /// For now, we accept empty tuples and tuples / arrays
 +    /// of `MaybeUninit`. There may be other types that allow uninitialized
 +    /// data, but those are not yet rigorously defined.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Beware the UB
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
 +    /// ```
 +    ///
 +    /// Note that the following is OK:
 +    ///
 +    /// ```rust
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: [MaybeUninit<bool>; 5] = unsafe {
 +    ///     MaybeUninit::uninit().assume_init()
 +    /// };
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub UNINIT_ASSUMED_INIT,
 +    correctness,
 +    "`MaybeUninit::uninit().assume_init()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be written simply with `saturating_add/sub` methods.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.checked_add(y).unwrap_or(u32::MAX);
 +    /// let sub = x.checked_sub(y).unwrap_or(u32::MIN);
 +    /// ```
 +    ///
 +    /// can be written using dedicated methods for saturating addition/subtraction as:
 +    ///
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.saturating_add(y);
 +    /// let sub = x.saturating_sub(y);
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub MANUAL_SATURATING_ARITHMETIC,
 +    style,
 +    "`.checked_add/sub(x).unwrap_or(MAX/MIN)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
 +    /// zero-sized types
 +    ///
 +    /// ### Why is this bad?
 +    /// This is a no-op, and likely unintended
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe { (&() as *const ()).offset(1) };
 +    /// ```
 +    #[clippy::version = "1.41.0"]
 +    pub ZST_OFFSET,
 +    correctness,
 +    "Check for offset calculations on raw pointers to zero-sized types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `FileType::is_file()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// When people testing a file type with `FileType::is_file`
 +    /// they are testing whether a path is something they can get bytes from. But
 +    /// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover
 +    /// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if filetype.is_file() {
 +    ///     // read file
 +    /// }
 +    /// # Ok::<_, std::io::Error>(())
 +    /// # };
 +    /// ```
 +    ///
 +    /// should be written as:
 +    ///
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if !filetype.is_dir() {
 +    ///     // read file
 +    /// }
 +    /// # Ok::<_, std::io::Error>(())
 +    /// # };
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub FILETYPE_IS_FILE,
 +    restriction,
 +    "`FileType::is_file` is not recommended to test for readable file type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.as_deref()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some("".to_string());
 +    /// opt.as_ref().map(String::as_str)
 +    /// # ;
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// # let opt = Some("".to_string());
 +    /// opt.as_deref()
 +    /// # ;
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub OPTION_AS_REF_DEREF,
 +    complexity,
 +    "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `iter().next()` on a Slice or an Array
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be shortened into `.get()`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let a = [1, 2, 3];
 +    /// # let b = vec![1, 2, 3];
 +    /// a[2..].iter().next();
 +    /// b.iter().next();
 +    /// ```
 +    /// should be written as:
 +    /// ```rust
 +    /// # let a = [1, 2, 3];
 +    /// # let b = vec![1, 2, 3];
 +    /// a.get(2);
 +    /// b.get(0);
 +    /// ```
 +    #[clippy::version = "1.46.0"]
 +    pub ITER_NEXT_SLICE,
 +    style,
 +    "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns when using `push_str`/`insert_str` with a single-character string literal
 +    /// where `push`/`insert` with a `char` would work fine.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's less clear that we are pushing a single character.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let mut string = String::new();
 +    /// string.insert_str(0, "R");
 +    /// string.push_str("R");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let mut string = String::new();
 +    /// string.insert(0, 'R');
 +    /// string.push('R');
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub SINGLE_CHAR_ADD_STR,
 +    style,
 +    "`push_str()` or `insert_str()` used with a single-character string literal as parameter"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// As the counterpart to `or_fun_call`, this lint looks for unnecessary
 +    /// lazily evaluated closures on `Option` and `Result`.
 +    ///
 +    /// This lint suggests changing the following functions, when eager evaluation results in
 +    /// simpler code:
 +    ///  - `unwrap_or_else` to `unwrap_or`
 +    ///  - `and_then` to `and`
 +    ///  - `or_else` to `or`
 +    ///  - `get_or_insert_with` to `get_or_insert`
 +    ///  - `ok_or_else` to `ok_or`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using eager evaluation is shorter and simpler in some cases.
 +    ///
 +    /// ### Known problems
 +    /// It is possible, but not recommended for `Deref` and `Index` to have
 +    /// side effects. Eagerly evaluating them can change the semantics of the program.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // example code where clippy issues a warning
 +    /// let opt: Option<u32> = None;
 +    ///
 +    /// opt.unwrap_or_else(|| 42);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let opt: Option<u32> = None;
 +    ///
 +    /// opt.unwrap_or(42);
 +    /// ```
 +    #[clippy::version = "1.48.0"]
 +    pub UNNECESSARY_LAZY_EVALUATIONS,
 +    style,
 +    "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map(_).collect::<Result<(), _>()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `try_for_each` instead is more readable and idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// (0..3).try_for_each(|t| Err(t));
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub MAP_COLLECT_RESULT_UNIT,
 +    style,
 +    "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `from_iter()` function calls on types that implement the `FromIterator`
 +    /// trait.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is recommended style to use collect. See
 +    /// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let five_fives = std::iter::repeat(5).take(5);
 +    ///
 +    /// let v = Vec::from_iter(five_fives);
 +    ///
 +    /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let five_fives = std::iter::repeat(5).take(5);
 +    ///
 +    /// let v: Vec<i32> = five_fives.collect();
 +    ///
 +    /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub FROM_ITER_INSTEAD_OF_COLLECT,
 +    pedantic,
 +    "use `.collect()` instead of `::from_iter()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `inspect().for_each()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is the same as performing the computation
 +    /// inside `inspect` at the beginning of the closure in `for_each`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// [1,2,3,4,5].iter()
 +    /// .inspect(|&x| println!("inspect the number: {}", x))
 +    /// .for_each(|&x| {
 +    ///     assert!(x >= 0);
 +    /// });
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// [1,2,3,4,5].iter()
 +    /// .for_each(|&x| {
 +    ///     println!("inspect the number: {}", x);
 +    ///     assert!(x >= 0);
 +    /// });
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub INSPECT_FOR_EACH,
 +    complexity,
 +    "using `.inspect().for_each()`, which can be replaced with `.for_each()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `filter_map(|x| x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely by using `flatten`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iter = vec![Some(1)].into_iter();
 +    /// iter.filter_map(|x| x);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let iter = vec![Some(1)].into_iter();
 +    /// iter.flatten();
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub FILTER_MAP_IDENTITY,
 +    complexity,
 +    "call to `filter_map` where `flatten` is sufficient"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for instances of `map(f)` where `f` is the identity function.
 +    ///
 +    /// ### Why is this bad?
 +    /// It can be written more concisely without the call to `map`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = [1, 2, 3];
 +    /// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = [1, 2, 3];
 +    /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub MAP_IDENTITY,
 +    complexity,
 +    "using iterator.map(|x| x)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.bytes().nth()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.as_bytes().get()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// "Hello".bytes().nth(3);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// "Hello".as_bytes().get(3);
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub BYTES_NTH,
 +    style,
 +    "replace `.bytes().nth()` with `.as_bytes().get()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.
 +    ///
 +    /// ### Why is this bad?
 +    /// These methods do the same thing as `_.clone()` but may be confusing as
 +    /// to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = vec![1, 2, 3];
 +    /// let b = a.to_vec();
 +    /// let c = a.to_owned();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = vec![1, 2, 3];
 +    /// let b = a.clone();
 +    /// let c = a.clone();
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub IMPLICIT_CLONE,
 +    pedantic,
 +    "implicitly cloning a value by invoking a function on its dereferenced type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.iter().count()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.len()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #![allow(unused)]
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    ///
 +    /// some_vec.iter().count();
 +    /// &some_vec[..].iter().count();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    ///
 +    /// some_vec.len();
 +    /// &some_vec[..].len();
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub ITER_COUNT,
 +    complexity,
 +    "replace `.iter().count()` with `.len()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the usage of `_.to_owned()`, on a `Cow<'_, _>`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `to_owned()` on a `Cow` creates a clone of the `Cow`
 +    /// itself, without taking ownership of the `Cow` contents (i.e.
 +    /// it's equivalent to calling `Cow::clone`).
 +    /// The similarly named `into_owned` method, on the other hand,
 +    /// clones the `Cow` contents, effectively turning any `Cow::Borrowed`
 +    /// into a `Cow::Owned`.
 +    ///
 +    /// Given the potential ambiguity, consider replacing `to_owned`
 +    /// with `clone` for better readability or, if getting a `Cow::Owned`
 +    /// was the original intent, using `into_owned` instead.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::borrow::Cow;
 +    /// let s = "Hello world!";
 +    /// let cow = Cow::Borrowed(s);
 +    ///
 +    /// let data = cow.to_owned();
 +    /// assert!(matches!(data, Cow::Borrowed(_)))
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::borrow::Cow;
 +    /// let s = "Hello world!";
 +    /// let cow = Cow::Borrowed(s);
 +    ///
 +    /// let data = cow.clone();
 +    /// assert!(matches!(data, Cow::Borrowed(_)))
 +    /// ```
 +    /// or
 +    /// ```rust
 +    /// # use std::borrow::Cow;
 +    /// let s = "Hello world!";
 +    /// let cow = Cow::Borrowed(s);
 +    ///
 +    /// let data = cow.into_owned();
 +    /// assert!(matches!(data, String))
 +    /// ```
 +    #[clippy::version = "1.65.0"]
 +    pub SUSPICIOUS_TO_OWNED,
 +    suspicious,
 +    "calls to `to_owned` on a `Cow<'_, _>` might not do what they are expected"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to [`splitn`]
 +    /// (https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and
 +    /// related functions with either zero or one splits.
 +    ///
 +    /// ### Why is this bad?
 +    /// These calls don't actually split the value and are
 +    /// likely to be intended as a different number.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let s = "";
 +    /// for x in s.splitn(1, ":") {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let s = "";
 +    /// for x in s.splitn(2, ":") {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.54.0"]
 +    pub SUSPICIOUS_SPLITN,
 +    correctness,
 +    "checks for `.splitn(0, ..)` and `.splitn(1, ..)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for manual implementations of `str::repeat`
 +    ///
 +    /// ### Why is this bad?
 +    /// These are both harder to read, as well as less performant.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: String = std::iter::repeat('x').take(10).collect();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x: String = "x".repeat(10);
 +    /// ```
 +    #[clippy::version = "1.54.0"]
 +    pub MANUAL_STR_REPEAT,
 +    perf,
 +    "manual implementation of `str::repeat`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `str::splitn(2, _)`
 +    ///
 +    /// ### Why is this bad?
 +    /// `split_once` is both clearer in intent and slightly more efficient.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let s = "key=value=add";
 +    /// let (key, value) = s.splitn(2, '=').next_tuple()?;
 +    /// let value = s.splitn(2, '=').nth(1)?;
 +    ///
 +    /// let mut parts = s.splitn(2, '=');
 +    /// let key = parts.next()?;
 +    /// let value = parts.next()?;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// let s = "key=value=add";
 +    /// let (key, value) = s.split_once('=')?;
 +    /// let value = s.split_once('=')?.1;
 +    ///
 +    /// let (key, value) = s.split_once('=')?;
 +    /// ```
 +    ///
 +    /// ### Limitations
 +    /// The multiple statement variant currently only detects `iter.next()?`/`iter.next().unwrap()`
 +    /// in two separate `let` statements that immediately follow the `splitn()`
 +    #[clippy::version = "1.57.0"]
 +    pub MANUAL_SPLIT_ONCE,
 +    complexity,
 +    "replace `.splitn(2, pat)` with `.split_once(pat)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `str::splitn` (or `str::rsplitn`) where using `str::split` would be the same.
 +    /// ### Why is this bad?
 +    /// The function `split` is simpler and there is no performance difference in these cases, considering
 +    /// that both functions return a lazy iterator.
 +    /// ### Example
 +    /// ```rust
 +    /// let str = "key=value=add";
 +    /// let _ = str.splitn(3, '=').next().unwrap();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let str = "key=value=add";
 +    /// let _ = str.split('=').next().unwrap();
 +    /// ```
 +    #[clippy::version = "1.59.0"]
 +    pub NEEDLESS_SPLITN,
 +    complexity,
 +    "usages of `str::splitn` that can be replaced with `str::split`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary calls to [`ToOwned::to_owned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned)
 +    /// and other `to_owned`-like functions.
 +    ///
 +    /// ### Why is this bad?
 +    /// The unnecessary calls result in useless allocations.
 +    ///
 +    /// ### Known problems
 +    /// `unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an
 +    /// owned copy of a resource and the resource is later used mutably. See
 +    /// [#8148](https://github.com/rust-lang/rust-clippy/issues/8148).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let path = std::path::Path::new("x");
 +    /// foo(&path.to_string_lossy().to_string());
 +    /// fn foo(s: &str) {}
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let path = std::path::Path::new("x");
 +    /// foo(&path.to_string_lossy());
 +    /// fn foo(s: &str) {}
 +    /// ```
 +    #[clippy::version = "1.59.0"]
 +    pub UNNECESSARY_TO_OWNED,
 +    perf,
 +    "unnecessary calls to `to_owned`-like functions"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.collect::<Vec<String>>().join("")` on iterators.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.collect::<String>()` is more concise and might be more performant
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vector = vec!["hello",  "world"];
 +    /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
 +    /// println!("{}", output);
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let vector = vec!["hello",  "world"];
 +    /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
 +    /// println!("{}", output);
 +    /// ```
 +    /// ### Known problems
 +    /// While `.collect::<String>()` is sometimes more performant, there are cases where
 +    /// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")`
 +    /// will prevent loop unrolling and will result in a negative performance impact.
 +    ///
 +    /// Additionally, differences have been observed between aarch64 and x86_64 assembly output,
 +    /// with aarch64 tending to producing faster assembly in more cases when using `.collect::<String>()`
 +    #[clippy::version = "1.61.0"]
 +    pub UNNECESSARY_JOIN,
 +    pedantic,
 +    "using `.collect::<Vec<String>>().join(\"\")` on an iterator"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for no-op uses of `Option::{as_deref, as_deref_mut}`,
 +    /// for example, `Option<&T>::as_deref()` returns the same type.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code and improving readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = Some(&1);
 +    /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = Some(&1);
 +    /// let b = a;
 +    /// ```
 +    #[clippy::version = "1.57.0"]
 +    pub NEEDLESS_OPTION_AS_DEREF,
 +    complexity,
 +    "no-op use of `deref` or `deref_mut` method to `Option`."
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Finds usages of [`char::is_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that
 +    /// can be replaced with [`is_ascii_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or
 +    /// [`is_ascii_hexdigit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit).
 +    ///
 +    /// ### Why is this bad?
 +    /// `is_digit(..)` is slower and requires specifying the radix.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let c: char = '6';
 +    /// c.is_digit(10);
 +    /// c.is_digit(16);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let c: char = '6';
 +    /// c.is_ascii_digit();
 +    /// c.is_ascii_hexdigit();
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub IS_DIGIT_ASCII_RADIX,
 +    style,
 +    "use of `char::is_digit(..)` with literal radix of 10 or 16"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calling `take` function after `as_ref`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code. `take` writes `None` to its argument.
 +    /// In this case the modification is useless as it's a temporary that cannot be read from afterwards.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = Some(3);
 +    /// x.as_ref().take();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = Some(3);
 +    /// x.as_ref();
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub NEEDLESS_OPTION_TAKE,
 +    complexity,
 +    "using `.as_ref().take()` on a temporary value"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `replace` statements which have no effect.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's either a mistake or confusing.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// "1234".replace("12", "12");
 +    /// "1234".replacen("12", "12", 1);
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub NO_EFFECT_REPLACE,
 +    suspicious,
 +    "replace with no effect"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `.then_some(..).unwrap_or(..)`
 +    ///
 +    /// ### Why is this bad?
 +    /// This can be written more clearly with `if .. else ..`
 +    ///
 +    /// ### Limitations
 +    /// This lint currently only looks for usages of
 +    /// `.then_some(..).unwrap_or(..)`, but will be expanded
 +    /// to account for similar patterns.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = true;
 +    /// x.then_some("a").unwrap_or("b");
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = true;
 +    /// if x { "a" } else { "b" };
 +    /// ```
 +    #[clippy::version = "1.64.0"]
 +    pub OBFUSCATED_IF_ELSE,
 +    style,
 +    "use of `.then_some(..).unwrap_or(..)` can be written \
 +    more clearly with `if .. else ..`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Checks for calls to `iter`, `iter_mut` or `into_iter` on collections containing a single item
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// It is simpler to use the once function from the standard library:
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust
 +    /// let a = [123].iter();
 +    /// let b = Some(123).into_iter();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::iter;
 +    /// let a = iter::once(&123);
 +    /// let b = iter::once(123);
 +    /// ```
 +    ///
 +    /// ### Known problems
 +    ///
 +    /// The type of the resulting iterator might become incompatible with its usage
 +    #[clippy::version = "1.64.0"]
 +    pub ITER_ON_SINGLE_ITEMS,
 +    nursery,
 +    "Iterator for array of length 1"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Checks for calls to `iter`, `iter_mut` or `into_iter` on empty collections
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// It is simpler to use the empty function from the standard library:
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust
 +    /// use std::{slice, option};
 +    /// let a: slice::Iter<i32> = [].iter();
 +    /// let f: option::IntoIter<i32> = None.into_iter();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::iter;
 +    /// let a: iter::Empty<i32> = iter::empty();
 +    /// let b: iter::Empty<i32> = iter::empty();
 +    /// ```
 +    ///
 +    /// ### Known problems
 +    ///
 +    /// The type of the resulting iterator might become incompatible with its usage
 +    #[clippy::version = "1.64.0"]
 +    pub ITER_ON_EMPTY_COLLECTIONS,
 +    nursery,
 +    "Iterator for empty array"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for naive byte counts
 +    ///
 +    /// ### Why is this bad?
 +    /// The [`bytecount`](https://crates.io/crates/bytecount)
 +    /// crate has methods to count your bytes faster, especially for large slices.
 +    ///
 +    /// ### Known problems
 +    /// If you have predominantly small slices, the
 +    /// `bytecount::count(..)` method may actually be slower. However, if you can
 +    /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
 +    /// faster in those cases.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1_u8];
 +    /// let count = vec.iter().filter(|x| **x == 0u8).count();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// # let vec = vec![1_u8];
 +    /// let count = bytecount::count(&vec, 0u8);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NAIVE_BYTECOUNT,
 +    pedantic,
 +    "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// It checks for `str::bytes().count()` and suggests replacing it with
 +    /// `str::len()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `str::bytes().count()` is longer and may not be as performant as using
 +    /// `str::len()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// "hello".bytes().count();
 +    /// String::from("hello").bytes().count();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// "hello".len();
 +    /// String::from("hello").len();
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub BYTES_COUNT_TO_LEN,
 +    complexity,
 +    "Using `bytes().count()` when `len()` performs the same functionality"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `ends_with` with possible file extensions
 +    /// and suggests to use a case-insensitive approach instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// `ends_with` is case-sensitive and may not detect files with a valid extension.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn is_rust_file(filename: &str) -> bool {
 +    ///     filename.ends_with(".rs")
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn is_rust_file(filename: &str) -> bool {
 +    ///     let filename = std::path::Path::new(filename);
 +    ///     filename.extension()
 +    ///         .map_or(false, |ext| ext.eq_ignore_ascii_case("rs"))
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
 +    pedantic,
 +    "Checks for calls to ends_with with case-sensitive file extensions"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for using `x.get(0)` instead of
 +    /// `x.first()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `x.first()` is easier to read and has the same
 +    /// result.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = vec![2, 3, 5];
 +    /// let first_element = x.get(0);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = vec![2, 3, 5];
 +    /// let first_element = x.first();
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub GET_FIRST,
 +    style,
 +    "Using `x.get(0)` when `x.first()` is simpler"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Finds patterns that reimplement `Option::ok_or`.
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// Concise code helps focusing on behavior instead of boilerplate.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// let foo: Option<i32> = None;
 +    /// foo.map_or(Err("error"), |v| Ok(v));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let foo: Option<i32> = None;
 +    /// foo.ok_or("error");
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub MANUAL_OK_OR,
 +    pedantic,
 +    "finds patterns that can be encoded more concisely with `Option::ok_or`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `map(|x| x.clone())` or
 +    /// dereferencing closures for `Copy` types, on `Iterator` or `Option`,
 +    /// and suggests `cloned()` or `copied()` instead
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = vec![42, 43];
 +    /// let y = x.iter();
 +    /// let z = y.map(|i| *i);
 +    /// ```
 +    ///
 +    /// The correct use would be:
 +    ///
 +    /// ```rust
 +    /// let x = vec![42, 43];
 +    /// let y = x.iter();
 +    /// let z = y.cloned();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MAP_CLONE,
 +    style,
 +    "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for instances of `map_err(|_| Some::Enum)`
 +    ///
 +    /// ### Why is this bad?
 +    /// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
 +    ///
 +    /// ### Example
 +    /// Before:
 +    /// ```rust
 +    /// use std::fmt;
 +    ///
 +    /// #[derive(Debug)]
 +    /// enum Error {
 +    ///     Indivisible,
 +    ///     Remainder(u8),
 +    /// }
 +    ///
 +    /// impl fmt::Display for Error {
 +    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +    ///         match self {
 +    ///             Error::Indivisible => write!(f, "could not divide input by three"),
 +    ///             Error::Remainder(remainder) => write!(
 +    ///                 f,
 +    ///                 "input is not divisible by three, remainder = {}",
 +    ///                 remainder
 +    ///             ),
 +    ///         }
 +    ///     }
 +    /// }
 +    ///
 +    /// impl std::error::Error for Error {}
 +    ///
 +    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
 +    ///     input
 +    ///         .parse::<i32>()
 +    ///         .map_err(|_| Error::Indivisible)
 +    ///         .map(|v| v % 3)
 +    ///         .and_then(|remainder| {
 +    ///             if remainder == 0 {
 +    ///                 Ok(())
 +    ///             } else {
 +    ///                 Err(Error::Remainder(remainder as u8))
 +    ///             }
 +    ///         })
 +    /// }
 +    ///  ```
 +    ///
 +    ///  After:
 +    ///  ```rust
 +    /// use std::{fmt, num::ParseIntError};
 +    ///
 +    /// #[derive(Debug)]
 +    /// enum Error {
 +    ///     Indivisible(ParseIntError),
 +    ///     Remainder(u8),
 +    /// }
 +    ///
 +    /// impl fmt::Display for Error {
 +    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +    ///         match self {
 +    ///             Error::Indivisible(_) => write!(f, "could not divide input by three"),
 +    ///             Error::Remainder(remainder) => write!(
 +    ///                 f,
 +    ///                 "input is not divisible by three, remainder = {}",
 +    ///                 remainder
 +    ///             ),
 +    ///         }
 +    ///     }
 +    /// }
 +    ///
 +    /// impl std::error::Error for Error {
 +    ///     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
 +    ///         match self {
 +    ///             Error::Indivisible(source) => Some(source),
 +    ///             _ => None,
 +    ///         }
 +    ///     }
 +    /// }
 +    ///
 +    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
 +    ///     input
 +    ///         .parse::<i32>()
 +    ///         .map_err(Error::Indivisible)
 +    ///         .map(|v| v % 3)
 +    ///         .and_then(|remainder| {
 +    ///             if remainder == 0 {
 +    ///                 Ok(())
 +    ///             } else {
 +    ///                 Err(Error::Remainder(remainder as u8))
 +    ///             }
 +    ///         })
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.48.0"]
 +    pub MAP_ERR_IGNORE,
 +    restriction,
 +    "`map_err` should not ignore the original error"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `&mut Mutex::lock` calls
 +    ///
 +    /// ### Why is this bad?
 +    /// `Mutex::lock` is less efficient than
 +    /// calling `Mutex::get_mut`. In addition you also have a statically
 +    /// guarantee that the mutex isn't locked, instead of just a runtime
 +    /// guarantee.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::sync::{Arc, Mutex};
 +    ///
 +    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
 +    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
 +    ///
 +    /// let mut value = value_mutex.lock().unwrap();
 +    /// *value += 1;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::sync::{Arc, Mutex};
 +    ///
 +    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
 +    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
 +    ///
 +    /// let value = value_mutex.get_mut().unwrap();
 +    /// *value += 1;
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub MUT_MUTEX_LOCK,
 +    style,
 +    "`&mut Mutex::lock` does unnecessary locking"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for duplicate open options as well as combinations
 +    /// that make no sense.
 +    ///
 +    /// ### Why is this bad?
 +    /// In the best case, the code will be harder to read than
 +    /// necessary. I don't know the worst case.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::fs::OpenOptions;
 +    ///
 +    /// OpenOptions::new().read(true).truncate(true);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NONSENSICAL_OPEN_OPTIONS,
 +    correctness,
 +    "nonsensical combination of options for opening a file"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
 +    /// calls on `PathBuf` that can cause overwrites.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `push` with a root path at the start can overwrite the
 +    /// previous defined path.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::path::PathBuf;
 +    ///
 +    /// let mut x = PathBuf::from("/foo");
 +    /// x.push("/bar");
 +    /// assert_eq!(x, PathBuf::from("/bar"));
 +    /// ```
 +    /// Could be written:
 +    ///
 +    /// ```rust
 +    /// use std::path::PathBuf;
 +    ///
 +    /// let mut x = PathBuf::from("/foo");
 +    /// x.push("bar");
 +    /// assert_eq!(x, PathBuf::from("/foo/bar"));
 +    /// ```
 +    #[clippy::version = "1.36.0"]
 +    pub PATH_BUF_PUSH_OVERWRITE,
 +    nursery,
 +    "calling `push` with file system root on `PathBuf` can overwrite it"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for zipping a collection with the range of
 +    /// `0.._.len()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The code is better expressed with `.enumerate()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = vec![1];
 +    /// let _ = x.iter().zip(0..x.len());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = vec![1];
 +    /// let _ = x.iter().enumerate();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub RANGE_ZIP_WITH_LEN,
 +    complexity,
 +    "zipping iterator with a range when `enumerate()` would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.repeat(1)` and suggest the following method for each types.
 +    /// - `.to_string()` for `str`
 +    /// - `.clone()` for `String`
 +    /// - `.to_vec()` for `slice`
 +    ///
 +    /// The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if
 +    /// they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306))
 +    ///
 +    /// ### Why is this bad?
 +    /// For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning
 +    /// the string is the intention behind this, `clone()` should be used.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn main() {
 +    ///     let x = String::from("hello world").repeat(1);
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn main() {
 +    ///     let x = String::from("hello world").clone();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub REPEAT_ONCE,
 +    complexity,
 +    "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// When sorting primitive values (integers, bools, chars, as well
 +    /// as arrays, slices, and tuples of such items), it is typically better to
 +    /// use an unstable sort than a stable sort.
 +    ///
 +    /// ### Why is this bad?
 +    /// Typically, using a stable sort consumes more memory and cpu cycles.
 +    /// Because values which compare equal are identical, preserving their
 +    /// relative order (the guarantee that a stable sort provides) means
 +    /// nothing, while the extra costs still apply.
 +    ///
 +    /// ### Known problems
 +    ///
 +    /// As pointed out in
 +    /// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241),
 +    /// a stable sort can instead be significantly faster for certain scenarios
 +    /// (eg. when a sorted vector is extended with new data and resorted).
 +    ///
 +    /// For more information and benchmarking results, please refer to the
 +    /// issue linked above.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut vec = vec![2, 1, 3];
 +    /// vec.sort();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let mut vec = vec![2, 1, 3];
 +    /// vec.sort_unstable();
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub STABLE_SORT_PRIMITIVE,
 +    pedantic,
 +    "use of sort() when sort_unstable() is equivalent"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects `().hash(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::hash::Hash;
 +    /// # use std::collections::hash_map::DefaultHasher;
 +    /// # enum Foo { Empty, WithValue(u8) }
 +    /// # use Foo::*;
 +    /// # let mut state = DefaultHasher::new();
 +    /// # let my_enum = Foo::Empty;
 +    /// match my_enum {
 +    ///       Empty => ().hash(&mut state),
 +    ///       WithValue(x) => x.hash(&mut state),
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::hash::Hash;
 +    /// # use std::collections::hash_map::DefaultHasher;
 +    /// # enum Foo { Empty, WithValue(u8) }
 +    /// # use Foo::*;
 +    /// # let mut state = DefaultHasher::new();
 +    /// # let my_enum = Foo::Empty;
 +    /// match my_enum {
 +    ///       Empty => 0_u8.hash(&mut state),
 +    ///       WithValue(x) => x.hash(&mut state),
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub UNIT_HASH,
 +    correctness,
 +    "hashing a unit value, which does nothing"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects uses of `Vec::sort_by` passing in a closure
 +    /// which compares the two arguments, either directly or indirectly.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
 +    /// possible) than to use `Vec::sort_by` and a more complicated
 +    /// closure.
 +    ///
 +    /// ### Known problems
 +    /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
 +    /// imported by a use statement, then it will need to be added manually.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct A;
 +    /// # impl A { fn foo(&self) {} }
 +    /// # let mut vec: Vec<A> = Vec::new();
 +    /// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # struct A;
 +    /// # impl A { fn foo(&self) {} }
 +    /// # let mut vec: Vec<A> = Vec::new();
 +    /// vec.sort_by_key(|a| a.foo());
 +    /// ```
 +    #[clippy::version = "1.46.0"]
 +    pub UNNECESSARY_SORT_BY,
 +    complexity,
 +    "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Finds occurrences of `Vec::resize(0, an_int)`
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably an argument inversion mistake.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// vec!(1, 2, 3, 4, 5).resize(0, 5)
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// vec!(1, 2, 3, 4, 5).clear()
 +    /// ```
 +    #[clippy::version = "1.46.0"]
 +    pub VEC_RESIZE_TO_ZERO,
 +    correctness,
 +    "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of File::read_to_end and File::read_to_string.
 +    ///
 +    /// ### Why is this bad?
 +    /// `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
 +    /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
 +    ///
 +    /// ### Example
 +    /// ```rust,no_run
 +    /// # use std::io::Read;
 +    /// # use std::fs::File;
 +    /// let mut f = File::open("foo.txt").unwrap();
 +    /// let mut bytes = Vec::new();
 +    /// f.read_to_end(&mut bytes).unwrap();
 +    /// ```
 +    /// Can be written more concisely as
 +    /// ```rust,no_run
 +    /// # use std::fs;
 +    /// let mut bytes = fs::read("foo.txt").unwrap();
 +    /// ```
 +    #[clippy::version = "1.44.0"]
 +    pub VERBOSE_FILE_READS,
 +    restriction,
 +    "use of `File::read_to_end` or `File::read_to_string`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Checks for iterating a map (`HashMap` or `BTreeMap`) and
 +    /// ignoring either the keys or values.
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// Readability. There are `keys` and `values` methods that
 +    /// can be used to express that we only need the keys or the values.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```
 +    /// # use std::collections::HashMap;
 +    /// let map: HashMap<u32, u32> = HashMap::new();
 +    /// let values = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```
 +    /// # use std::collections::HashMap;
 +    /// let map: HashMap<u32, u32> = HashMap::new();
 +    /// let values = map.values().collect::<Vec<_>>();
 +    /// ```
 +    #[clippy::version = "1.65.0"]
 +    pub ITER_KV_MAP,
 +    complexity,
 +    "iterating on map using `iter` when `keys` or `values` would do"
 +}
 +
 +pub struct Methods {
 +    avoid_breaking_exported_api: bool,
 +    msrv: Option<RustcVersion>,
 +    allow_expect_in_tests: bool,
 +    allow_unwrap_in_tests: bool,
 +}
 +
 +impl Methods {
 +    #[must_use]
 +    pub fn new(
 +        avoid_breaking_exported_api: bool,
 +        msrv: Option<RustcVersion>,
 +        allow_expect_in_tests: bool,
 +        allow_unwrap_in_tests: bool,
 +    ) -> Self {
 +        Self {
 +            avoid_breaking_exported_api,
 +            msrv,
 +            allow_expect_in_tests,
 +            allow_unwrap_in_tests,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(Methods => [
 +    UNWRAP_USED,
 +    EXPECT_USED,
 +    SHOULD_IMPLEMENT_TRAIT,
 +    WRONG_SELF_CONVENTION,
 +    OK_EXPECT,
 +    UNWRAP_OR_ELSE_DEFAULT,
 +    MAP_UNWRAP_OR,
 +    RESULT_MAP_OR_INTO_OPTION,
 +    OPTION_MAP_OR_NONE,
 +    BIND_INSTEAD_OF_MAP,
 +    OR_FUN_CALL,
 +    OR_THEN_UNWRAP,
 +    EXPECT_FUN_CALL,
 +    CHARS_NEXT_CMP,
 +    CHARS_LAST_CMP,
 +    CLONE_ON_COPY,
 +    CLONE_ON_REF_PTR,
 +    CLONE_DOUBLE_REF,
 +    COLLAPSIBLE_STR_REPLACE,
 +    ITER_OVEREAGER_CLONED,
 +    CLONED_INSTEAD_OF_COPIED,
 +    FLAT_MAP_OPTION,
 +    INEFFICIENT_TO_STRING,
 +    NEW_RET_NO_SELF,
 +    SINGLE_CHAR_PATTERN,
 +    SINGLE_CHAR_ADD_STR,
 +    SEARCH_IS_SOME,
 +    FILTER_NEXT,
 +    SKIP_WHILE_NEXT,
 +    FILTER_MAP_IDENTITY,
 +    MAP_IDENTITY,
 +    MANUAL_FILTER_MAP,
 +    MANUAL_FIND_MAP,
 +    OPTION_FILTER_MAP,
 +    FILTER_MAP_NEXT,
 +    FLAT_MAP_IDENTITY,
 +    MAP_FLATTEN,
 +    ITERATOR_STEP_BY_ZERO,
 +    ITER_NEXT_SLICE,
 +    ITER_COUNT,
 +    ITER_NTH,
 +    ITER_NTH_ZERO,
 +    BYTES_NTH,
 +    ITER_SKIP_NEXT,
 +    GET_UNWRAP,
 +    GET_LAST_WITH_LEN,
 +    STRING_EXTEND_CHARS,
 +    ITER_CLONED_COLLECT,
 +    ITER_WITH_DRAIN,
 +    USELESS_ASREF,
 +    UNNECESSARY_FOLD,
 +    UNNECESSARY_FILTER_MAP,
 +    UNNECESSARY_FIND_MAP,
 +    INTO_ITER_ON_REF,
 +    SUSPICIOUS_MAP,
 +    UNINIT_ASSUMED_INIT,
 +    MANUAL_SATURATING_ARITHMETIC,
 +    ZST_OFFSET,
 +    FILETYPE_IS_FILE,
 +    OPTION_AS_REF_DEREF,
 +    UNNECESSARY_LAZY_EVALUATIONS,
 +    MAP_COLLECT_RESULT_UNIT,
 +    FROM_ITER_INSTEAD_OF_COLLECT,
 +    INSPECT_FOR_EACH,
 +    IMPLICIT_CLONE,
 +    SUSPICIOUS_TO_OWNED,
 +    SUSPICIOUS_SPLITN,
 +    MANUAL_STR_REPEAT,
 +    EXTEND_WITH_DRAIN,
 +    MANUAL_SPLIT_ONCE,
 +    NEEDLESS_SPLITN,
 +    UNNECESSARY_TO_OWNED,
 +    UNNECESSARY_JOIN,
 +    ERR_EXPECT,
 +    NEEDLESS_OPTION_AS_DEREF,
 +    IS_DIGIT_ASCII_RADIX,
 +    NEEDLESS_OPTION_TAKE,
 +    NO_EFFECT_REPLACE,
 +    OBFUSCATED_IF_ELSE,
 +    ITER_ON_SINGLE_ITEMS,
 +    ITER_ON_EMPTY_COLLECTIONS,
 +    NAIVE_BYTECOUNT,
 +    BYTES_COUNT_TO_LEN,
 +    CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
 +    GET_FIRST,
 +    MANUAL_OK_OR,
 +    MAP_CLONE,
 +    MAP_ERR_IGNORE,
 +    MUT_MUTEX_LOCK,
 +    NONSENSICAL_OPEN_OPTIONS,
 +    PATH_BUF_PUSH_OVERWRITE,
 +    RANGE_ZIP_WITH_LEN,
 +    REPEAT_ONCE,
 +    STABLE_SORT_PRIMITIVE,
 +    UNIT_HASH,
 +    UNNECESSARY_SORT_BY,
 +    VEC_RESIZE_TO_ZERO,
 +    VERBOSE_FILE_READS,
 +    ITER_KV_MAP,
 +]);
 +
 +/// 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>, &'tcx [hir::Expr<'tcx>], Span)> {
 +    if let ExprKind::MethodCall(path, receiver, args, _) = recv.kind {
 +        if !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() {
 +            let name = path.ident.name.as_str();
 +            return Some((name, receiver, args, path.ident.span));
 +        }
 +    }
 +    None
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for Methods {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        self.check_methods(cx, expr);
 +
 +        match expr.kind {
 +            hir::ExprKind::Call(func, args) => {
 +                from_iter_instead_of_collect::check(cx, expr, args, func);
 +            },
 +            hir::ExprKind::MethodCall(method_call, receiver, args, _) => {
 +                let method_span = method_call.ident.span;
 +                or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args);
 +                expect_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args);
 +                clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args);
 +                clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args);
 +                inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args);
 +                single_char_add_str::check(cx, expr, receiver, args);
 +                into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver);
 +                single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args);
 +                unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv);
 +            },
 +            hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
 +                let mut info = BinaryExprInfo {
 +                    expr,
 +                    chain: lhs,
 +                    other: rhs,
 +                    eq: op.node == hir::BinOpKind::Eq,
 +                };
 +                lint_binary_expr_with_method_call(cx, &mut info);
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    #[allow(clippy::too_many_lines)]
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
 +        if in_external_macro(cx.sess(), impl_item.span) {
 +            return;
 +        }
 +        let name = impl_item.ident.name.as_str();
 +        let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_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 let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind {
 +            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_opt = method_sig.inputs().iter().next().copied();
 +            // if this impl block implements a trait, lint in trait definition instead
 +            if !implements_trait && cx.access_levels.is_exported(impl_item.def_id.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)
 +                        // in case there is no first arg, since we already have checked the number of arguments
 +                        // it's should be always true
 +                        && first_arg_ty_opt.map_or(true, |first_arg_ty| 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.def_id))
 +                    && let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next()
 +                    && let Some(first_arg_ty) = first_arg_ty_opt
 +                {
 +                    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 ret_ty.contains(self_ty) {
 +                return;
 +            }
 +
 +            // if return type is impl trait, check the associated types
 +            if let ty::Opaque(def_id, _) = *ret_ty.kind() {
 +                // one of the associated types must be Self
 +                for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
 +                    if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
 +                        let assoc_ty = match projection_predicate.term.unpack() {
 +                            ty::TermKind::Ty(ty) => ty,
 +                            ty::TermKind::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 assoc_ty.contains(self_ty) {
 +                            return;
 +                        }
 +                    }
 +                }
 +            }
 +
 +            if name == "new" && ret_ty != self_ty {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    impl_item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if in_external_macro(cx.tcx.sess, item.span) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let TraitItemKind::Fn(ref sig, _) = item.kind;
 +            if sig.decl.implicit_self.has_implicit_self();
 +            if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
 +
 +            then {
 +                let first_arg_span = first_arg_ty.span;
 +                let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
-                     true
++                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,
-             let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
++                    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 trait_path = match mutability {
-                 hir::Mutability::Not => &paths::ASREF_TRAIT,
-                 hir::Mutability::Mut => &paths::ASMUT_TRAIT,
++            let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id())
++                .self_ty()
++                .skip_binder();
 +            if !ret_ty.contains(self_ty);
 +
 +            then {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +impl Methods {
 +    #[allow(clippy::too_many_lines)]
 +    fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let Some((name, recv, args, span)) = method_call(expr) {
 +            match (name, args) {
 +                ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
 +                    zst_offset::check(cx, expr, recv);
 +                },
 +                ("and_then", [arg]) => {
 +                    let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg);
 +                    let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg);
 +                    if !biom_option_linted && !biom_result_linted {
 +                        unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
 +                    }
 +                },
 +                ("as_deref" | "as_deref_mut", []) => {
 +                    needless_option_as_deref::check(cx, expr, recv, name);
 +                },
 +                ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
 +                ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
 +                ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
 +                ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv),
 +                ("collect", []) => match method_call(recv) {
 +                    Some((name @ ("cloned" | "copied"), recv2, [], _)) => {
 +                        iter_cloned_collect::check(cx, name, expr, recv2);
 +                    },
 +                    Some(("map", m_recv, [m_arg], _)) => {
 +                        map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv);
 +                    },
 +                    Some(("take", take_self_arg, [take_arg], _)) => {
 +                        if meets_msrv(self.msrv, msrvs::STR_REPEAT) {
 +                            manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
 +                        }
 +                    },
 +                    _ => {},
 +                },
 +                ("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) {
 +                    Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false),
 +                    Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _)) => {
 +                        iter_count::check(cx, expr, recv2, name2);
 +                    },
 +                    Some(("map", _, [arg], _)) => suspicious_map::check(cx, expr, recv, arg),
 +                    Some(("filter", recv2, [arg], _)) => bytecount::check(cx, expr, recv2, arg),
 +                    Some(("bytes", recv2, [], _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
 +                    _ => {},
 +                },
 +                ("drain", [arg]) => {
 +                    iter_with_drain::check(cx, expr, recv, span, arg);
 +                },
 +                ("ends_with", [arg]) => {
 +                    if let ExprKind::MethodCall(.., span) = expr.kind {
 +                        case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg);
 +                    }
 +                },
 +                ("expect", [_]) => match method_call(recv) {
 +                    Some(("ok", recv, [], _)) => ok_expect::check(cx, expr, recv),
 +                    Some(("err", recv, [], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
 +                    _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
 +                },
 +                ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests),
 +                ("extend", [arg]) => {
 +                    string_extend_chars::check(cx, expr, recv, arg);
 +                    extend_with_drain::check(cx, expr, recv, arg);
 +                },
 +                ("filter_map", [arg]) => {
 +                    unnecessary_filter_map::check(cx, expr, arg, name);
 +                    filter_map_identity::check(cx, expr, arg, span);
 +                },
 +                ("find_map", [arg]) => {
 +                    unnecessary_filter_map::check(cx, expr, arg, name);
 +                },
 +                ("flat_map", [arg]) => {
 +                    flat_map_identity::check(cx, expr, arg, span);
 +                    flat_map_option::check(cx, expr, arg, span);
 +                },
 +                ("flatten", []) => match method_call(recv) {
 +                    Some(("map", recv, [map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
 +                    Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true),
 +                    _ => {},
 +                },
 +                ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
 +                ("for_each", [_]) => {
 +                    if let Some(("inspect", _, [_], span2)) = method_call(recv) {
 +                        inspect_for_each::check(cx, expr, span2);
 +                    }
 +                },
 +                ("get", [arg]) => {
 +                    get_first::check(cx, expr, recv, arg);
 +                    get_last_with_len::check(cx, expr, recv, arg);
 +                },
 +                ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
 +                ("hash", [arg]) => {
 +                    unit_hash::check(cx, expr, recv, arg);
 +                },
 +                ("is_file", []) => filetype_is_file::check(cx, expr, recv),
 +                ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv),
 +                ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
 +                ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
 +                ("iter" | "iter_mut" | "into_iter", []) => {
 +                    iter_on_single_or_empty_collections::check(cx, expr, name, recv);
 +                },
 +                ("join", [join_arg]) => {
 +                    if let Some(("collect", _, _, span)) = method_call(recv) {
 +                        unnecessary_join::check(cx, expr, recv, join_arg, span);
 +                    }
 +                },
 +                ("last", []) | ("skip", [_]) => {
 +                    if let Some((name2, recv2, args2, _span2)) = method_call(recv) {
 +                        if let ("cloned", []) = (name2, args2) {
 +                            iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
 +                        }
 +                    }
 +                },
 +                ("lock", []) => {
 +                    mut_mutex_lock::check(cx, expr, recv, span);
 +                },
 +                (name @ ("map" | "map_err"), [m_arg]) => {
 +                    if name == "map" {
 +                        map_clone::check(cx, expr, recv, m_arg, self.msrv);
 +                        if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _)) = method_call(recv) {
 +                            iter_kv_map::check(cx, map_name, expr, recv2, m_arg);
 +                        }
 +                    } else {
 +                        map_err_ignore::check(cx, expr, m_arg);
 +                    }
 +                    if let Some((name, recv2, args, span2)) = method_call(recv) {
 +                        match (name, args) {
 +                            ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv),
 +                            ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv),
 +                            ("filter", [f_arg]) => {
 +                                filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
 +                            },
 +                            ("find", [f_arg]) => {
 +                                filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true);
 +                            },
 +                            _ => {},
 +                        }
 +                    }
 +                    map_identity::check(cx, expr, recv, m_arg, name, span);
 +                },
 +                ("map_or", [def, map]) => {
 +                    option_map_or_none::check(cx, expr, recv, def, map);
 +                    manual_ok_or::check(cx, expr, recv, def, map);
 +                },
 +                ("next", []) => {
 +                    if let Some((name2, recv2, args2, _)) = method_call(recv) {
 +                        match (name2, args2) {
 +                            ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
 +                            ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
 +                            ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv),
 +                            ("iter", []) => iter_next_slice::check(cx, expr, recv2),
 +                            ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
 +                            ("skip_while", [_]) => skip_while_next::check(cx, expr),
 +                            _ => {},
 +                        }
 +                    }
 +                },
 +                ("nth", [n_arg]) => match method_call(recv) {
 +                    Some(("bytes", recv2, [], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
 +                    Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
 +                    Some(("iter", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
 +                    Some(("iter_mut", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
 +                    _ => iter_nth_zero::check(cx, expr, recv, n_arg),
 +                },
 +                ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
 +                ("open", [_]) => {
 +                    open_options::check(cx, expr, recv);
 +                },
 +                ("or_else", [arg]) => {
 +                    if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
 +                        unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
 +                    }
 +                },
 +                ("push", [arg]) => {
 +                    path_buf_push_overwrite::check(cx, expr, arg);
 +                },
 +                ("read_to_end", [_]) => {
 +                    verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_END_MSG);
 +                },
 +                ("read_to_string", [_]) => {
 +                    verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_STRING_MSG);
 +                },
 +                ("repeat", [arg]) => {
 +                    repeat_once::check(cx, expr, recv, arg);
 +                },
 +                (name @ ("replace" | "replacen"), [arg1, arg2] | [arg1, arg2, _]) => {
 +                    no_effect_replace::check(cx, expr, arg1, arg2);
 +
 +                    // Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint
 +                    if name == "replace" && let Some(("replace", ..)) = method_call(recv) {
 +                        collapsible_str_replace::check(cx, expr, arg1, arg2);
 +                    }
 +                },
 +                ("resize", [count_arg, default_arg]) => {
 +                    vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span);
 +                },
 +                ("sort", []) => {
 +                    stable_sort_primitive::check(cx, expr, recv);
 +                },
 +                ("sort_by", [arg]) => {
 +                    unnecessary_sort_by::check(cx, expr, recv, arg, false);
 +                },
 +                ("sort_unstable_by", [arg]) => {
 +                    unnecessary_sort_by::check(cx, expr, recv, arg, true);
 +                },
 +                ("splitn" | "rsplitn", [count_arg, pat_arg]) => {
 +                    if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
 +                        suspicious_splitn::check(cx, name, expr, recv, count);
 +                        str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv);
 +                    }
 +                },
 +                ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
 +                    if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
 +                        suspicious_splitn::check(cx, name, expr, recv, count);
 +                    }
 +                },
 +                ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
 +                ("take", [_arg]) => {
 +                    if let Some((name2, recv2, args2, _span2)) = method_call(recv) {
 +                        if let ("cloned", []) = (name2, args2) {
 +                            iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
 +                        }
 +                    }
 +                },
 +                ("take", []) => needless_option_take::check(cx, expr, recv),
 +                ("then", [arg]) => {
 +                    if !meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
 +                        return;
 +                    }
 +                    unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some");
 +                },
 +                ("to_owned", []) => {
 +                    if !suspicious_to_owned::check(cx, expr, recv) {
 +                        implicit_clone::check(cx, name, expr, recv);
 +                    }
 +                },
 +                ("to_os_string" | "to_path_buf" | "to_vec", []) => {
 +                    implicit_clone::check(cx, name, expr, recv);
 +                },
 +                ("unwrap", []) => {
 +                    match method_call(recv) {
 +                        Some(("get", recv, [get_arg], _)) => {
 +                            get_unwrap::check(cx, expr, recv, get_arg, false);
 +                        },
 +                        Some(("get_mut", recv, [get_arg], _)) => {
 +                            get_unwrap::check(cx, expr, recv, get_arg, true);
 +                        },
 +                        Some(("or", recv, [or_arg], or_span)) => {
 +                            or_then_unwrap::check(cx, expr, recv, or_arg, or_span);
 +                        },
 +                        _ => {},
 +                    }
 +                    unwrap_used::check(cx, expr, recv, false, self.allow_unwrap_in_tests);
 +                },
 +                ("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests),
 +                ("unwrap_or", [u_arg]) => match method_call(recv) {
 +                    Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _)) => {
 +                        manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
 +                    },
 +                    Some(("map", m_recv, [m_arg], span)) => {
 +                        option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
 +                    },
 +                    Some(("then_some", t_recv, [t_arg], _)) => {
 +                        obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg);
 +                    },
 +                    _ => {},
 +                },
 +                ("unwrap_or_else", [u_arg]) => match method_call(recv) {
 +                    Some(("map", recv, [map_arg], _))
 +                        if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
 +                    _ => {
 +                        unwrap_or_else_default::check(cx, expr, recv, u_arg);
 +                        unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
 +                    },
 +                },
 +                ("zip", [arg]) => {
 +                    if let ExprKind::MethodCall(name, iter_recv, [], _) = recv.kind
 +                        && name.ident.name == sym::iter
 +                    {
 +                        range_zip_with_len::check(cx, expr, iter_recv, arg);
 +                    }
 +                },
 +                _ => {},
 +            }
 +        }
 +    }
 +}
 +
 +fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
 +    if let Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span)) = method_call(recv) {
 +        search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
 +    }
 +}
 +
 +/// Used for `lint_binary_expr_with_method_call`.
 +#[derive(Copy, Clone)]
 +struct BinaryExprInfo<'a> {
 +    expr: &'a hir::Expr<'a>,
 +    chain: &'a hir::Expr<'a>,
 +    other: &'a hir::Expr<'a>,
 +    eq: bool,
 +}
 +
 +/// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
 +fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) {
 +    macro_rules! lint_with_both_lhs_and_rhs {
 +        ($func:expr, $cx:expr, $info:ident) => {
 +            if !$func($cx, $info) {
 +                ::std::mem::swap(&mut $info.chain, &mut $info.other);
 +                if $func($cx, $info) {
 +                    return;
 +                }
 +            }
 +        };
 +    }
 +
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info);
 +}
 +
 +const FN_HEADER: hir::FnHeader = hir::FnHeader {
 +    unsafety: hir::Unsafety::Normal,
 +    constness: hir::Constness::NotConst,
 +    asyncness: hir::IsAsync::NotAsync,
 +    abi: rustc_target::spec::abi::Abi::Rust,
 +};
 +
 +struct ShouldImplTraitCase {
 +    trait_name: &'static str,
 +    method_name: &'static str,
 +    param_count: usize,
 +    fn_header: hir::FnHeader,
 +    // implicit self kind expected (none, self, &self, ...)
 +    self_kind: SelfKind,
 +    // checks against the output type
 +    output_type: OutType,
 +    // certain methods with explicit lifetimes can't implement the equivalent trait method
 +    lint_explicit_lifetime: bool,
 +}
 +impl ShouldImplTraitCase {
 +    const fn new(
 +        trait_name: &'static str,
 +        method_name: &'static str,
 +        param_count: usize,
 +        fn_header: hir::FnHeader,
 +        self_kind: SelfKind,
 +        output_type: OutType,
 +        lint_explicit_lifetime: bool,
 +    ) -> ShouldImplTraitCase {
 +        ShouldImplTraitCase {
 +            trait_name,
 +            method_name,
 +            param_count,
 +            fn_header,
 +            self_kind,
 +            output_type,
 +            lint_explicit_lifetime,
 +        }
 +    }
 +
 +    fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool {
 +        self.lint_explicit_lifetime
 +            || !impl_item.generics.params.iter().any(|p| {
 +                matches!(
 +                    p.kind,
 +                    hir::GenericParamKind::Lifetime {
 +                        kind: hir::LifetimeParamKind::Explicit
 +                    }
 +                )
 +            })
 +    }
 +}
 +
 +#[rustfmt::skip]
 +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
 +    ShouldImplTraitCase::new("std::ops::Add", "add",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::convert::AsMut", "as_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::convert::AsRef", "as_ref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::BitAnd", "bitand",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitOr", "bitor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitXor", "bitxor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::borrow::Borrow", "borrow",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::clone::Clone", "clone",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::cmp::Ord", "cmp",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::default::Default", "default",  0,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Deref", "deref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::Div", "div",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Drop", "drop",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Unit, true),
 +    ShouldImplTraitCase::new("std::cmp::PartialEq", "eq",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Bool, true),
 +    ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::str::FromStr", "from_str",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::hash::Hash", "hash",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Unit, true),
 +    ShouldImplTraitCase::new("std::ops::Index", "index",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut",  2,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Mul", "mul",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Neg", "neg",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::iter::Iterator", "next",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Any, false),
 +    ShouldImplTraitCase::new("std::ops::Not", "not",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Rem", "rem",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Shl", "shl",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Shr", "shr",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Sub", "sub",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +];
 +
 +#[derive(Clone, Copy, PartialEq, Eq, Debug)]
 +enum SelfKind {
 +    Value,
 +    Ref,
 +    RefMut,
 +    No, // When we want the first argument type to be different than `Self`
 +}
 +
 +impl SelfKind {
 +    fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +        fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +            if ty == parent_ty {
 +                true
 +            } else if ty.is_box() {
 +                ty.boxed_ty() == parent_ty
 +            } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
 +                if let ty::Adt(_, substs) = ty.kind() {
 +                    substs.types().next().map_or(false, |t| t == parent_ty)
 +                } else {
 +                    false
 +                }
 +            } else {
 +                false
 +            }
 +        }
 +
 +        fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +            if let ty::Ref(_, t, m) = *ty.kind() {
 +                return m == mutability && t == parent_ty;
 +            }
 +
-             let trait_def_id = match get_trait_def_id(cx, trait_path) {
-                 Some(did) => did,
-                 None => return false,
++            let trait_sym = match mutability {
++                hir::Mutability::Not => sym::AsRef,
++                hir::Mutability::Mut => sym::AsMut,
 +            };
 +
++            let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else {
++                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 6fb92d1c663cf6de9ee7a7421656186fad5d7908,0000000000000000000000000000000000000000..742483e6b2e5545274adf08081f1db58953eb8c5
mode 100644,000000..100644
--- /dev/null
@@@ -1,118 -1,0 +1,120 @@@
-     let deref_aliases: [&[&str]; 9] = [
-         &paths::DEREF_TRAIT_METHOD,
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +use rustc_semver::RustcVersion;
 +use rustc_span::sym;
 +
 +use super::OPTION_AS_REF_DEREF;
 +
 +/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &hir::Expr<'_>,
 +    as_ref_recv: &hir::Expr<'_>,
 +    map_arg: &hir::Expr<'_>,
 +    is_mut: bool,
 +    msrv: Option<RustcVersion>,
 +) {
 +    if !meets_msrv(msrv, msrvs::OPTION_AS_DEREF) {
 +        return;
 +    }
 +
 +    let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not);
 +
 +    let option_ty = cx.typeck_results().expr_ty(as_ref_recv);
 +    if !is_type_diagnostic_item(cx, option_ty, sym::Option) {
 +        return;
 +    }
 +
-         hir::ExprKind::Path(ref expr_qpath) => cx
-             .qpath_res(expr_qpath, map_arg.hir_id)
-             .opt_def_id()
-             .map_or(false, |fun_def_id| {
-                 deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path))
-             }),
++    let deref_aliases: [&[&str]; 8] = [
 +        &paths::DEREF_MUT_TRAIT_METHOD,
 +        &paths::CSTRING_AS_C_STR,
 +        &paths::OS_STRING_AS_OS_STR,
 +        &paths::PATH_BUF_AS_PATH,
 +        &paths::STRING_AS_STR,
 +        &paths::STRING_AS_MUT_STR,
 +        &paths::VEC_AS_SLICE,
 +        &paths::VEC_AS_MUT_SLICE,
 +    ];
 +
 +    let is_deref = match map_arg.kind {
-                             deref_aliases.iter().any(|path| match_def_path(cx, method_did, path))
++        hir::ExprKind::Path(ref expr_qpath) => {
++            cx.qpath_res(expr_qpath, map_arg.hir_id)
++                .opt_def_id()
++                .map_or(false, |fun_def_id| {
++                    cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id)
++                        || deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path))
++                })
++        },
 +        hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
 +            let closure_body = cx.tcx.hir().body(body);
 +            let closure_expr = peel_blocks(closure_body.value);
 +
 +            match &closure_expr.kind {
 +                hir::ExprKind::MethodCall(_, receiver, [], _) => {
 +                    if_chain! {
 +                        if path_to_local_id(receiver, closure_body.params[0].pat.hir_id);
 +                        let adj = cx
 +                            .typeck_results()
 +                            .expr_adjustments(receiver)
 +                            .iter()
 +                            .map(|x| &x.kind)
 +                            .collect::<Box<[_]>>();
 +                        if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj;
 +                        then {
 +                            let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap();
++                            cx.tcx.is_diagnostic_item(sym::deref_method, method_did)
++                                || deref_aliases.iter().any(|path| match_def_path(cx, method_did, path))
 +                        } else {
 +                            false
 +                        }
 +                    }
 +                },
 +                hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, inner) if same_mutability(m) => {
 +                    if_chain! {
 +                        if let hir::ExprKind::Unary(hir::UnOp::Deref, inner1) = inner.kind;
 +                        if let hir::ExprKind::Unary(hir::UnOp::Deref, inner2) = inner1.kind;
 +                        then {
 +                            path_to_local_id(inner2, closure_body.params[0].pat.hir_id)
 +                        } else {
 +                            false
 +                        }
 +                    }
 +                },
 +                _ => false,
 +            }
 +        },
 +        _ => false,
 +    };
 +
 +    if is_deref {
 +        let current_method = if is_mut {
 +            format!(".as_mut().map({})", snippet(cx, map_arg.span, ".."))
 +        } else {
 +            format!(".as_ref().map({})", snippet(cx, map_arg.span, ".."))
 +        };
 +        let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" };
 +        let hint = format!("{}.{method_hint}()", snippet(cx, as_ref_recv.span, ".."));
 +        let suggestion = format!("try using {method_hint} instead");
 +
 +        let msg = format!(
 +            "called `{current_method}` on an Option value. This can be done more directly \
 +            by calling `{hint}` instead"
 +        );
 +        span_lint_and_sugg(
 +            cx,
 +            OPTION_AS_REF_DEREF,
 +            expr.span,
 +            &msg,
 +            &suggestion,
 +            hint,
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
index 6a35024d0361240596283a3e116b9e5b39eceb8d,0000000000000000000000000000000000000000..991d3dd538bf86a7ea572c011cd419fb1aa22999
mode 100644,000000..100644
--- /dev/null
@@@ -1,181 -1,0 +1,180 @@@
- use clippy_utils::ty::{implements_trait, match_type};
- use clippy_utils::{contains_return, is_trait_item, last_path_segment, paths};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::eager_or_lazy::switch_to_lazy_eval;
 +use clippy_utils::source::{snippet, snippet_with_macro_callsite};
- use rustc_span::symbol::{kw, sym};
++use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
++use clippy_utils::{contains_return, is_trait_item, last_path_segment};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_span::source_map::Span;
-         const KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [
-             (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
-             (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
-             (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
-             (&paths::RESULT, true, &["or", "unwrap_or"], "else"),
++use rustc_span::symbol::{kw, sym, Symbol};
 +use std::borrow::Cow;
 +
 +use super::OR_FUN_CALL;
 +
 +/// Checks for the `OR_FUN_CALL` lint.
 +#[allow(clippy::too_many_lines)]
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &hir::Expr<'_>,
 +    method_span: Span,
 +    name: &str,
 +    receiver: &'tcx hir::Expr<'_>,
 +    args: &'tcx [hir::Expr<'_>],
 +) {
 +    /// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`,
 +    /// `or_insert(T::new())` or `or_insert(T::default())`.
 +    #[allow(clippy::too_many_arguments)]
 +    fn check_unwrap_or_default(
 +        cx: &LateContext<'_>,
 +        name: &str,
 +        fun: &hir::Expr<'_>,
 +        arg: &hir::Expr<'_>,
 +        or_has_args: bool,
 +        span: Span,
 +        method_span: Span,
 +    ) -> bool {
 +        let is_default_default = || is_trait_item(cx, fun, sym::Default);
 +
 +        let implements_default = |arg, default_trait_id| {
 +            let arg_ty = cx.typeck_results().expr_ty(arg);
 +            implements_trait(cx, arg_ty, default_trait_id, &[])
 +        };
 +
 +        if_chain! {
 +            if !or_has_args;
 +            if let Some(sugg) = match name {
 +                "unwrap_or" => Some("unwrap_or_default"),
 +                "or_insert" => Some("or_default"),
 +                _ => None,
 +            };
 +            if let hir::ExprKind::Path(ref qpath) = fun.kind;
 +            if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default);
 +            let path = last_path_segment(qpath).ident.name;
 +            // needs to target Default::default in particular or be *::new and have a Default impl
 +            // available
 +            if (matches!(path, kw::Default) && is_default_default())
 +                || (matches!(path, sym::new) && implements_default(arg, default_trait_id));
 +
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    OR_FUN_CALL,
 +                    method_span.with_hi(span.hi()),
 +                    &format!("use of `{name}` followed by a call to `{path}`"),
 +                    "try this",
 +                    format!("{sugg}()"),
 +                    Applicability::MachineApplicable,
 +                );
 +
 +                true
 +            } else {
 +                false
 +            }
 +        }
 +    }
 +
 +    /// Checks for `*or(foo())`.
 +    #[allow(clippy::too_many_arguments)]
 +    fn check_general_case<'tcx>(
 +        cx: &LateContext<'tcx>,
 +        name: &str,
 +        method_span: Span,
 +        self_expr: &hir::Expr<'_>,
 +        arg: &'tcx hir::Expr<'_>,
 +        span: Span,
 +        // None if lambda is required
 +        fun_span: Option<Span>,
 +    ) {
 +        // (path, fn_has_argument, methods, suffix)
-                 KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0));
++        const KNOW_TYPES: [(Symbol, bool, &[&str], &str); 4] = [
++            (sym::BTreeEntry, false, &["or_insert"], "with"),
++            (sym::HashMapEntry, false, &["or_insert"], "with"),
++            (sym::Option, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
++            (sym::Result, true, &["or", "unwrap_or"], "else"),
 +        ];
 +
 +        if_chain! {
 +            if KNOW_TYPES.iter().any(|k| k.2.contains(&name));
 +
 +            if switch_to_lazy_eval(cx, arg);
 +            if !contains_return(arg);
 +
 +            let self_ty = cx.typeck_results().expr_ty(self_expr);
 +
 +            if let Some(&(_, fn_has_arguments, poss, suffix)) =
-                                 None => macro_expanded_snipped
++                KNOW_TYPES.iter().find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0));
 +
 +            if poss.contains(&name);
 +
 +            then {
 +                let macro_expanded_snipped;
 +                let sugg: Cow<'_, str> = {
 +                    let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) {
 +                        (false, Some(fun_span)) => (fun_span, false),
 +                        _ => (arg.span, true),
 +                    };
 +                    let snippet = {
 +                        let not_macro_argument_snippet = snippet_with_macro_callsite(cx, snippet_span, "..");
 +                        if not_macro_argument_snippet == "vec![]" {
 +                            macro_expanded_snipped = snippet(cx, snippet_span, "..");
 +                            match macro_expanded_snipped.strip_prefix("$crate::vec::") {
 +                                Some(stripped) => Cow::from(stripped),
-                         }
-                         else {
++                                None => macro_expanded_snipped,
 +                            }
++                        } else {
 +                            not_macro_argument_snippet
 +                        }
 +                    };
 +
 +                    if use_lambda {
 +                        let l_arg = if fn_has_arguments { "_" } else { "" };
 +                        format!("|{l_arg}| {snippet}").into()
 +                    } else {
 +                        snippet
 +                    }
 +                };
 +                let span_replace_word = method_span.with_hi(span.hi());
 +                span_lint_and_sugg(
 +                    cx,
 +                    OR_FUN_CALL,
 +                    span_replace_word,
 +                    &format!("use of `{name}` followed by a function call"),
 +                    "try this",
 +                    format!("{name}_{suffix}({sugg})"),
 +                    Applicability::HasPlaceholders,
 +                );
 +            }
 +        }
 +    }
 +
 +    if let [arg] = args {
 +        let inner_arg = if let hir::ExprKind::Block(
 +            hir::Block {
 +                stmts: [],
 +                expr: Some(expr),
 +                ..
 +            },
 +            _,
 +        ) = arg.kind
 +        {
 +            expr
 +        } else {
 +            arg
 +        };
 +        match inner_arg.kind {
 +            hir::ExprKind::Call(fun, or_args) => {
 +                let or_has_args = !or_args.is_empty();
 +                if !check_unwrap_or_default(cx, name, fun, arg, or_has_args, expr.span, method_span) {
 +                    let fun_span = if or_has_args { None } else { Some(fun.span) };
 +                    check_general_case(cx, name, method_span, receiver, arg, expr.span, fun_span);
 +                }
 +            },
 +            hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
 +                check_general_case(cx, name, method_span, receiver, arg, expr.span, None);
 +            },
 +            _ => (),
 +        }
 +    }
 +}
index ae3594bd36c3ab1ecfb3a1e6022666aa900b7001,0000000000000000000000000000000000000000..1acac59144cee8ce926daf8fdabfe5212918dae5
mode 100644,000000..100644
--- /dev/null
@@@ -1,387 -1,0 +1,385 @@@
-             let (name, args) = if let ExprKind::MethodCall(name, _, [args @ ..], _) = e.kind {
-                 (name, args)
-             } else {
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet_with_context;
 +use clippy_utils::usage::local_used_after_expr;
 +use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
 +use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths};
 +use core::ops::ControlFlow;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind,
 +};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +use rustc_semver::RustcVersion;
 +use rustc_span::{sym, Span, Symbol, SyntaxContext};
 +
 +use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN};
 +
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    method_name: &str,
 +    expr: &Expr<'_>,
 +    self_arg: &Expr<'_>,
 +    pat_arg: &Expr<'_>,
 +    count: u128,
 +    msrv: Option<RustcVersion>,
 +) {
 +    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();
 +    let (msg, reverse) = if method_name == "splitn" {
 +        ("manual implementation of `split_once`", false)
 +    } else {
 +        ("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 => {
 +            if reverse {
 +                format!("{self_snip}.rsplit_once({pat_snip}).map(|(x, y)| (y, x))")
 +            } else {
 +                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::MUT, 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,
 +                "",
 +                app,
 +            );
 +            diag.span_suggestion(
 +                second.span,
 +                &remove_msg,
 +                "",
 +                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::NONE, _, ident, None),
 +            ..
 +        },
 +        init: Some(init_expr),
 +        hir_id: local_hir_id,
 +        ..
 +    }) = stmt.kind
 +    {
 +        let mut path_to_binding = None;
 +        let _: Option<!> = for_each_expr_with_closures(cx, init_expr, |e| {
 +            if path_to_local_id(e, binding) {
 +                path_to_binding = Some(e);
 +            }
 +            ControlFlow::Continue(Descend::from(path_to_binding.is_none()))
 +        });
 +
 +        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 {
 +    Nth(u128),
 +    NextTuple,
 +}
 +
 +#[derive(Debug, PartialEq, Eq)]
 +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>)>,
 +) -> Option<IterUsage> {
 +    let (kind, span) = match iter.next() {
 +        Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
++            let ExprKind::MethodCall(name, _, [args @ ..], _) = e.kind 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) {
 +                ("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 {
 +                                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;
 +                                }
 +                            }
 +                        };
 +                        (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 5cf88bfc8880d7dbb8b20283c1ffedb5658702d9,0000000000000000000000000000000000000000..3566fe9a0bbc953d220abff348e68971bad45125
mode 100644,000000..100644
--- /dev/null
@@@ -1,499 -1,0 +1,499 @@@
-                     let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.hir_id());
 +use super::implicit_clone::is_clone_like;
 +use super::unnecessary_iter_cloned::{self, is_into_iter};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
 +use clippy_utils::visitors::find_all_ret_expressions;
 +use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
 +use clippy_utils::{meets_msrv, msrvs};
 +use rustc_errors::Applicability;
 +use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node};
 +use rustc_hir_typeck::{FnCtxt, Inherited};
 +use rustc_infer::infer::TyCtxtInferExt;
 +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::EarlyBinder;
 +use rustc_middle::ty::{self, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
 +use rustc_semver::RustcVersion;
 +use rustc_span::{sym, Symbol};
 +use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
 +use std::cmp::max;
 +
 +use super::UNNECESSARY_TO_OWNED;
 +
 +pub fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    method_name: Symbol,
 +    receiver: &'tcx Expr<'_>,
 +    args: &'tcx [Expr<'_>],
 +    msrv: Option<RustcVersion>,
 +) {
 +    if_chain! {
 +        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
 +        if args.is_empty();
 +        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, expr, method_name, method_def_id) {
 +                // At this point, we know the call is of a `to_owned`-like function. The functions
 +                // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary
 +                // based on its context, that is, whether it is a referent in an `AddrOf` expression, an
 +                // argument in a `into_iter` call, or an argument in the call of some other function.
 +                if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) {
 +                    return;
 +                }
 +                if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) {
 +                    return;
 +                }
 +                check_other_call_arg(cx, expr, method_name, receiver);
 +            }
 +        }
 +    }
 +}
 +
 +/// Checks whether `expr` is a referent in an `AddrOf` expression and, if so, determines whether its
 +/// call of a `to_owned`-like function is unnecessary.
 +#[allow(clippy::too_many_lines)]
 +fn check_addr_of_expr(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    method_name: Symbol,
 +    method_def_id: DefId,
 +    receiver: &Expr<'_>,
 +) -> bool {
 +    if_chain! {
 +        if let Some(parent) = get_parent_expr(cx, expr);
 +        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind;
 +        let adjustments = cx.typeck_results().expr_adjustments(parent).iter().collect::<Vec<_>>();
 +        if let
 +            // For matching uses of `Cow::from`
 +            [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    target: referent_ty,
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    target: target_ty,
 +                },
 +            ]
 +            // For matching uses of arrays
 +            | [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    target: referent_ty,
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Pointer(_),
 +                    target: target_ty,
 +                },
 +            ]
 +            // For matching everything else
 +            | [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    target: referent_ty,
 +                },
 +                Adjustment {
 +                    kind: Adjust::Deref(Some(OverloadedDeref { .. })),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    target: target_ty,
 +                },
 +            ] = adjustments[..];
 +        let receiver_ty = cx.typeck_results().expr_ty(receiver);
 +        let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty);
 +        let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty);
 +        // Only flag cases satisfying at least one of the following three conditions:
 +        // * the referent and receiver types are distinct
 +        // * the referent/receiver type is a copyable array
 +        // * the method is `Cow::into_owned`
 +        // This restriction is to ensure there is no overlap between `redundant_clone` and this
 +        // lint. It also avoids the following false positive:
 +        //  https://github.com/rust-lang/rust-clippy/issues/8759
 +        //   Arrays are a bit of a corner case. Non-copyable arrays are handled by
 +        // `redundant_clone`, but copyable arrays are not.
 +        if *referent_ty != receiver_ty
 +            || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty))
 +            || is_cow_into_owned(cx, method_name, method_def_id);
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            if receiver_ty == target_ty && n_target_refs >= n_receiver_refs {
 +                span_lint_and_sugg(
 +                    cx,
 +                    UNNECESSARY_TO_OWNED,
 +                    parent.span,
 +                    &format!("unnecessary use of `{method_name}`"),
 +                    "use",
 +                    format!(
 +                        "{:&>width$}{receiver_snippet}",
 +                        "",
 +                        width = n_target_refs - n_receiver_refs
 +                    ),
 +                    Applicability::MachineApplicable,
 +                );
 +                return true;
 +            }
 +            if_chain! {
 +                if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
 +                if implements_trait(cx, receiver_ty, deref_trait_id, &[]);
 +                if get_associated_type(cx, receiver_ty, deref_trait_id, "Target") == Some(target_ty);
 +                then {
 +                    if n_receiver_refs > 0 {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            UNNECESSARY_TO_OWNED,
 +                            parent.span,
 +                            &format!("unnecessary use of `{method_name}`"),
 +                            "use",
 +                            receiver_snippet,
 +                            Applicability::MachineApplicable,
 +                        );
 +                    } else {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            UNNECESSARY_TO_OWNED,
 +                            expr.span.with_lo(receiver.span.hi()),
 +                            &format!("unnecessary use of `{method_name}`"),
 +                            "remove this",
 +                            String::new(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    }
 +                    return true;
 +                }
 +            }
 +            if_chain! {
 +                if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
 +                if implements_trait(cx, receiver_ty, as_ref_trait_id, &[GenericArg::from(target_ty)]);
 +                then {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        UNNECESSARY_TO_OWNED,
 +                        parent.span,
 +                        &format!("unnecessary use of `{method_name}`"),
 +                        "use",
 +                        format!("{receiver_snippet}.as_ref()"),
 +                        Applicability::MachineApplicable,
 +                    );
 +                    return true;
 +                }
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
 +/// call of a `to_owned`-like function is unnecessary.
 +fn check_into_iter_call_arg(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    method_name: Symbol,
 +    receiver: &Expr<'_>,
 +    msrv: Option<RustcVersion>,
 +) -> bool {
 +    if_chain! {
 +        if let Some(parent) = get_parent_expr(cx, expr);
 +        if let Some(callee_def_id) = fn_def_id(cx, parent);
 +        if is_into_iter(cx, callee_def_id);
 +        if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
 +        let parent_ty = cx.typeck_results().expr_ty(parent);
 +        if implements_trait(cx, parent_ty, iterator_trait_id, &[]);
 +        if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty);
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
 +                return true;
 +            }
 +            let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
 +                "copied"
 +            } else {
 +                "cloned"
 +            };
 +            // The next suggestion may be incorrect because the removal of the `to_owned`-like
 +            // function could cause the iterator to hold a reference to a resource that is used
 +            // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
 +            span_lint_and_sugg(
 +                cx,
 +                UNNECESSARY_TO_OWNED,
 +                parent.span,
 +                &format!("unnecessary use of `{method_name}`"),
 +                "use",
 +                format!("{receiver_snippet}.iter().{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, _, recv, 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) = recv.into_iter().chain(call_args).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, _) = 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);
 +        if trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id;
 +        let receiver_ty = cx.typeck_results().expr_ty(receiver);
 +        if can_change_type(cx, maybe_arg, receiver_ty);
 +        // 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, usize::from(!is_copy(cx, receiver_ty)));
 +        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!("{:&>n_refs$}{receiver_snippet}", ""),
 +                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>, Option<&'tcx Expr<'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, None, args));
 +        }
 +    }
 +    if_chain! {
 +        if let ExprKind::MethodCall(_, recv, 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, Some(recv), 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.param_env(callee_def_id).caller_bounds() {
 +        match predicate.kind().skip_binder() {
 +            PredicateKind::Trait(trait_predicate) => {
 +                if trait_predicate.trait_ref.self_ty() == input {
 +                    trait_predicates.push(trait_predicate);
 +                }
 +            },
 +            PredicateKind::Projection(projection_predicate) => {
 +                if projection_predicate.projection_ty.self_ty() == input {
 +                    projection_predicates.push(projection_predicate);
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +    (trait_predicates, projection_predicates)
 +}
 +
 +fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<'a>) -> bool {
 +    for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
 +        match node {
 +            Node::Stmt(_) => return true,
 +            Node::Block(..) => continue,
 +            Node::Item(item) => {
 +                if let ItemKind::Fn(_, _, body_id) = &item.kind
 +                && let output_ty = return_ty(cx, item.hir_id())
 +                && let local_def_id = cx.tcx.hir().local_def_id(item.hir_id())
 +                && Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
++                    let fn_ctxt = FnCtxt::new(inherited, cx.param_env, item.hir_id());
 +                    fn_ctxt.can_coerce(ty, output_ty)
 +                }) {
 +                    if has_lifetime(output_ty) && has_lifetime(ty) {
 +                        return false;
 +                    }
 +                    let body = cx.tcx.hir().body(*body_id);
 +                    let body_expr = &body.value;
 +                    let mut count = 0;
 +                    return find_all_ret_expressions(cx, body_expr, |_| { count += 1; count <= 1 });
 +                }
 +            }
 +            Node::Expr(parent_expr) => {
 +                if let Some((callee_def_id, call_substs, recv, call_args)) = get_callee_substs_and_args(cx, parent_expr)
 +                {
 +                    if cx.tcx.lang_items().require(LangItem::IntoFutureIntoFuture) == Ok(callee_def_id) {
 +                        return false;
 +                    }
 +
 +                    let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
 +                    if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id)
 +                        && let Some(param_ty) = fn_sig.inputs().get(arg_index)
 +                        && let ty::Param(ParamTy { index: param_index , ..}) = param_ty.kind()
 +                    {
 +                        if fn_sig
 +                            .inputs()
 +                            .iter()
 +                            .enumerate()
 +                            .filter(|(i, _)| *i != arg_index)
 +                            .any(|(_, ty)| ty.contains(*param_ty))
 +                        {
 +                            return false;
 +                        }
 +
 +                        let mut trait_predicates = cx.tcx.param_env(callee_def_id)
 +                            .caller_bounds().iter().filter(|predicate| {
 +                            if let PredicateKind::Trait(trait_predicate) =  predicate.kind().skip_binder()
 +                                && trait_predicate.trait_ref.self_ty() == *param_ty {
 +                                    true
 +                                } else {
 +                                false
 +                            }
 +                        });
 +
 +                        let new_subst = cx.tcx.mk_substs(
 +                            call_substs.iter()
 +                                .enumerate()
 +                                .map(|(i, t)|
 +                                     if i == (*param_index as usize) {
 +                                         GenericArg::from(ty)
 +                                     } else {
 +                                         t
 +                                     }));
 +
 +                        if trait_predicates.any(|predicate| {
 +                            let predicate = EarlyBinder(predicate).subst(cx.tcx, new_subst);
 +                            let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate);
 +                            !cx.tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation)
 +                        }) {
 +                            return false;
 +                        }
 +
 +                        let output_ty = fn_sig.output();
 +                        if output_ty.contains(*param_ty) {
 +                            if let Ok(new_ty)  = cx.tcx.try_subst_and_normalize_erasing_regions(
 +                                new_subst, cx.param_env, output_ty) {
 +                                expr = parent_expr;
 +                                ty = new_ty;
 +                                continue;
 +                            }
 +                            return false;
 +                        }
 +
 +                        return true;
 +                    }
 +                } else if let ExprKind::Block(..) = parent_expr.kind {
 +                    continue;
 +                }
 +                return false;
 +            },
 +            _ => return false,
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn has_lifetime(ty: Ty<'_>) -> bool {
 +    ty.walk().any(|t| matches!(t.unpack(), GenericArgKind::Lifetime(_)))
 +}
 +
 +/// 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<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, 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_on_string_like(cx, call_expr, 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` and it's called on a type that
 +/// is string-like i.e. implements `AsRef<str>` or `Deref<str>`.
 +fn is_to_string_on_string_like<'a>(
 +    cx: &LateContext<'_>,
 +    call_expr: &'a Expr<'a>,
 +    method_name: Symbol,
 +    method_def_id: DefId,
 +) -> bool {
 +    if method_name != sym::to_string || !is_diag_trait_item(cx, method_def_id, sym::ToString) {
 +        return false;
 +    }
 +
 +    if let Some(substs) = cx.typeck_results().node_substs_opt(call_expr.hir_id)
 +        && let [generic_arg] = substs.as_slice()
 +        && let GenericArgKind::Type(ty) = generic_arg.unpack()
 +        && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref)
 +        && let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef)
 +        && (implements_trait(cx, ty, deref_trait_id, &[cx.tcx.types.str_.into()]) ||
 +            implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()])) {
 +            true
 +        } else {
 +            false
 +        }
 +}
index 62c6ca32d31a9da1012cb2fc51a73d976273050e,0000000000000000000000000000000000000000..27e7f8505eb5b854acfaf7fb6b622273f3d551db
mode 100644,000000..100644
--- /dev/null
@@@ -1,38 -1,0 +1,36 @@@
-     let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
-         val
-     } else {
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use rustc_ast::ast::Lit;
 +use rustc_errors::Applicability;
 +use rustc_lint::EarlyContext;
 +
 +use super::{SEPARATED_LITERAL_SUFFIX, UNSEPARATED_LITERAL_SUFFIX};
 +
 +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &str, sugg_type: &str) {
++    let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else {
 +        return; // It's useless so shouldn't lint.
 +    };
 +    // Do not lint when literal is unsuffixed.
 +    if !suffix.is_empty() {
 +        if lit_snip.as_bytes()[maybe_last_sep_idx] == b'_' {
 +            span_lint_and_sugg(
 +                cx,
 +                SEPARATED_LITERAL_SUFFIX,
 +                lit.span,
 +                &format!("{sugg_type} type suffix should not be separated by an underscore"),
 +                "remove the underscore",
 +                format!("{}{suffix}", &lit_snip[..maybe_last_sep_idx]),
 +                Applicability::MachineApplicable,
 +            );
 +        } else {
 +            span_lint_and_sugg(
 +                cx,
 +                UNSEPARATED_LITERAL_SUFFIX,
 +                lit.span,
 +                &format!("{sugg_type} type suffix should be separated by an underscore"),
 +                "add an underscore",
 +                format!("{}_{suffix}", &lit_snip[..=maybe_last_sep_idx]),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
index 80e24213100787f572d6e237b1d86f223ed6034a,0000000000000000000000000000000000000000..263ee1e945a255cfdd23977bab7cf9ef84f5e5d6
mode 100644,000000..100644
--- /dev/null
@@@ -1,34 -1,0 +1,32 @@@
-     let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
-         val
-     } else {
 +use clippy_utils::diagnostics::span_lint;
 +use rustc_ast::ast::Lit;
 +use rustc_lint::EarlyContext;
 +
 +use super::MIXED_CASE_HEX_LITERALS;
 +
 +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &str) {
++    let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else {
 +        return; // It's useless so shouldn't lint.
 +    };
 +    if maybe_last_sep_idx <= 2 {
 +        // It's meaningless or causes range error.
 +        return;
 +    }
 +    let mut seen = (false, false);
 +    for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() {
 +        match ch {
 +            b'a'..=b'f' => seen.0 = true,
 +            b'A'..=b'F' => seen.1 = true,
 +            _ => {},
 +        }
 +        if seen.0 && seen.1 {
 +            span_lint(
 +                cx,
 +                MIXED_CASE_HEX_LITERALS,
 +                lit.span,
 +                "inconsistent casing in hexadecimal literal",
 +            );
 +            break;
 +        }
 +    }
 +}
index 4963bba82f2da169887da3f88a3a5c7641a64000,0000000000000000000000000000000000000000..9ead43ea4a477060961a8ae1afbd842f527cb71f
mode 100644,000000..100644
--- /dev/null
@@@ -1,29 -1,0 +1,33 @@@
-                 lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(),
-                 Applicability::MaybeIncorrect,
-             );
-             diag.span_suggestion(
-                 lit.span,
-                 "if you mean to use an octal constant, use `0o`",
-                 format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')),
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use rustc_ast::ast::Lit;
 +use rustc_errors::Applicability;
 +use rustc_lint::EarlyContext;
 +
 +use super::ZERO_PREFIXED_LITERAL;
 +
 +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) {
++    let trimmed_lit_snip = lit_snip.trim_start_matches(|c| c == '_' || c == '0');
 +    span_lint_and_then(
 +        cx,
 +        ZERO_PREFIXED_LITERAL,
 +        lit.span,
 +        "this is a decimal constant",
 +        |diag| {
 +            diag.span_suggestion(
 +                lit.span,
 +                "if you mean to use a decimal constant, remove the `0` to avoid confusion",
++                trimmed_lit_snip.to_string(),
 +                Applicability::MaybeIncorrect,
 +            );
++            // do not advise to use octal form if the literal cannot be expressed in base 8.
++            if !lit_snip.contains(|c| c == '8' || c == '9') {
++                diag.span_suggestion(
++                    lit.span,
++                    "if you mean to use an octal constant, use `0o`",
++                    format!("0o{trimmed_lit_snip}"),
++                    Applicability::MaybeIncorrect,
++                );
++            }
 +        },
 +    );
 +}
index 6dd76a6531e496e497cff2f30bf6e0457bfed11c,0000000000000000000000000000000000000000..9de4b56b77b56c4d858e55a5868a2861065ac042
mode 100644,000000..100644
--- /dev/null
@@@ -1,121 -1,0 +1,120 @@@
-                 let defid = match path.res {
-                     Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) => defid,
-                     _ => return,
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::{GenericArg, Item, ItemKind, QPath, Ty, TyKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::GenericParamDefKind;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for type parameters which are positioned inconsistently between
 +    /// a type definition and impl block. Specifically, a parameter in an impl
 +    /// block which has the same name as a parameter in the type def, but is in
 +    /// a different place.
 +    ///
 +    /// ### Why is this bad?
 +    /// Type parameters are determined by their position rather than name.
 +    /// Naming type parameters inconsistently may cause you to refer to the
 +    /// wrong type parameter.
 +    ///
 +    /// ### Limitations
 +    /// This lint only applies to impl blocks with simple generic params, e.g.
 +    /// `A`. If there is anything more complicated, such as a tuple, it will be
 +    /// ignored.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Foo<A, B> {
 +    ///     x: A,
 +    ///     y: B,
 +    /// }
 +    /// // inside the impl, B refers to Foo::A
 +    /// impl<B, A> Foo<B, A> {}
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// struct Foo<A, B> {
 +    ///     x: A,
 +    ///     y: B,
 +    /// }
 +    /// impl<A, B> Foo<A, B> {}
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub MISMATCHING_TYPE_PARAM_ORDER,
 +    pedantic,
 +    "type parameter positioned inconsistently between type def and impl block"
 +}
 +declare_lint_pass!(TypeParamMismatch => [MISMATCHING_TYPE_PARAM_ORDER]);
 +
 +impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
 +        if_chain! {
 +            if !item.span.from_expansion();
 +            if let ItemKind::Impl(imp) = &item.kind;
 +            if let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind;
 +            if let Some(segment) = path.segments.iter().next();
 +            if let Some(generic_args) = segment.args;
 +            if !generic_args.args.is_empty();
 +            then {
 +                // get the name and span of the generic parameters in the Impl
 +                let mut impl_params = Vec::new();
 +                for p in generic_args.args.iter() {
 +                    match p {
 +                        GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) =>
 +                            impl_params.push((path.segments[0].ident.to_string(), path.span)),
 +                        GenericArg::Type(_) => return,
 +                        _ => (),
 +                    };
 +                }
 +
 +                // find the type that the Impl is for
 +                // only lint on struct/enum/union for now
++                let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) = path.res else {
++                    return
 +                };
 +
 +                // get the names of the generic parameters in the type
 +                let type_params = &cx.tcx.generics_of(defid).params;
 +                let type_param_names: Vec<_> = type_params.iter()
 +                .filter_map(|p|
 +                    match p.kind {
 +                        GenericParamDefKind::Type {..} => Some(p.name.to_string()),
 +                        _ => None,
 +                    }
 +                ).collect();
 +                // hashmap of name -> index for mismatch_param_name
 +                let type_param_names_hashmap: FxHashMap<&String, usize> =
 +                    type_param_names.iter().enumerate().map(|(i, param)| (param, i)).collect();
 +
 +                let type_name = segment.ident;
 +                for (i, (impl_param_name, impl_param_span)) in impl_params.iter().enumerate() {
 +                    if mismatch_param_name(i, impl_param_name, &type_param_names_hashmap) {
 +                        let msg = format!("`{type_name}` has a similarly named generic type parameter `{impl_param_name}` in its declaration, but in a different order");
 +                        let help = format!("try `{}`, or a name that does not conflict with `{type_name}`'s generic params",
 +                                           type_param_names[i]);
 +                        span_lint_and_help(
 +                            cx,
 +                            MISMATCHING_TYPE_PARAM_ORDER,
 +                            *impl_param_span,
 +                            &msg,
 +                            None,
 +                            &help
 +                        );
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +// Checks if impl_param_name is the same as one of type_param_names,
 +// and is in a different position
 +fn mismatch_param_name(i: usize, impl_param_name: &String, type_param_names: &FxHashMap<&String, usize>) -> bool {
 +    if let Some(j) = type_param_names.get(impl_param_name) {
 +        if i != *j {
 +            return true;
 +        }
 +    }
 +    false
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..68af8a672f6aed19b7e7f22f235a6255129a2abc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,98 @@@
++use clippy_utils::diagnostics::span_lint_and_help;
++use clippy_utils::is_lint_allowed;
++use clippy_utils::macros::span_is_local;
++use rustc_hir::def_id::DefIdMap;
++use rustc_hir::{Impl, Item, ItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::AssocItem;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks if a provided method is used implicitly by a trait
++    /// implementation. A usage example would be a wrapper where every method
++    /// should perform some operation before delegating to the inner type's
++    /// implemenation.
++    ///
++    /// This lint should typically be enabled on a specific trait `impl` item
++    /// rather than globally.
++    ///
++    /// ### Why is this bad?
++    /// Indicates that a method is missing.
++    ///
++    /// ### Example
++    /// ```rust
++    /// trait Trait {
++    ///     fn required();
++    ///
++    ///     fn provided() {}
++    /// }
++    ///
++    /// # struct Type;
++    /// #[warn(clippy::missing_trait_methods)]
++    /// impl Trait for Type {
++    ///     fn required() { /* ... */ }
++    /// }
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// trait Trait {
++    ///     fn required();
++    ///
++    ///     fn provided() {}
++    /// }
++    ///
++    /// # struct Type;
++    /// #[warn(clippy::missing_trait_methods)]
++    /// impl Trait for Type {
++    ///     fn required() { /* ... */ }
++    ///
++    ///     fn provided() { /* ... */ }
++    /// }
++    /// ```
++    #[clippy::version = "1.66.0"]
++    pub MISSING_TRAIT_METHODS,
++    restriction,
++    "trait implementation uses default provided method"
++}
++declare_lint_pass!(MissingTraitMethods => [MISSING_TRAIT_METHODS]);
++
++impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods {
++    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
++        if !is_lint_allowed(cx, MISSING_TRAIT_METHODS, item.hir_id())
++            && span_is_local(item.span)
++            && let ItemKind::Impl(Impl {
++                items,
++                of_trait: Some(trait_ref),
++                ..
++            }) = item.kind
++            && let Some(trait_id) = trait_ref.trait_def_id()
++        {
++            let mut provided: DefIdMap<&AssocItem> = cx
++                .tcx
++                .provided_trait_methods(trait_id)
++                .map(|assoc| (assoc.def_id, assoc))
++                .collect();
++
++            for impl_item in *items {
++                if let Some(def_id) = impl_item.trait_item_def_id {
++                    provided.remove(&def_id);
++                }
++            }
++
++            for assoc in provided.values() {
++                let source_map = cx.tcx.sess.source_map();
++                let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id));
++
++                span_lint_and_help(
++                    cx,
++                    MISSING_TRAIT_METHODS,
++                    source_map.guess_head_span(item.span),
++                    &format!("missing trait method provided by default: `{}`", assoc.name),
++                    Some(definition_span),
++                    "implement the method",
++                );
++            }
++        }
++    }
++}
index a2419c277e9c27d2b270961e6c860c99f306ffe3,0000000000000000000000000000000000000000..6752976348f6045ed45e98ad774cf722d43d14ea
mode 100644,000000..100644
--- /dev/null
@@@ -1,350 -1,0 +1,347 @@@
-         let parent_node = match map.find(parent_id) {
-             Some(parent) => parent,
-             None => break,
-         };
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
 +use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id};
 +use if_chain::if_chain;
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for a read and a write to the same variable where
 +    /// whether the read occurs before or after the write depends on the evaluation
 +    /// order of sub-expressions.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is often confusing to read. As described [here](https://doc.rust-lang.org/reference/expressions.html?highlight=subexpression#evaluation-order-of-operands),
 +    /// the operands of these expressions are evaluated before applying the effects of the expression.
 +    ///
 +    /// ### Known problems
 +    /// Code which intentionally depends on the evaluation
 +    /// order, or which is correct for any evaluation order.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut x = 0;
 +    ///
 +    /// let a = {
 +    ///     x = 1;
 +    ///     1
 +    /// } + x;
 +    /// // Unclear whether a is 1 or 2.
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let mut x = 0;
 +    /// let tmp = {
 +    ///     x = 1;
 +    ///     1
 +    /// };
 +    /// let a = tmp + x;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MIXED_READ_WRITE_IN_EXPRESSION,
 +    restriction,
 +    "whether a variable read occurs before a write depends on sub-expression evaluation order"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for diverging calls that are not match arms or
 +    /// statements.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is often confusing to read. In addition, the
 +    /// sub-expression evaluation order for Rust is not well documented.
 +    ///
 +    /// ### Known problems
 +    /// Someone might want to use `some_bool || panic!()` as a
 +    /// shorthand.
 +    ///
 +    /// ### Example
 +    /// ```rust,no_run
 +    /// # fn b() -> bool { true }
 +    /// # fn c() -> bool { true }
 +    /// let a = b() || panic!() || c();
 +    /// // `c()` is dead, `panic!()` is only called if `b()` returns `false`
 +    /// let x = (a, b, c, panic!());
 +    /// // can simply be replaced by `panic!()`
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DIVERGING_SUB_EXPRESSION,
 +    complexity,
 +    "whether an expression contains a diverging sub expression"
 +}
 +
 +declare_lint_pass!(EvalOrderDependence => [MIXED_READ_WRITE_IN_EXPRESSION, DIVERGING_SUB_EXPRESSION]);
 +
 +impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        // Find a write to a local variable.
 +        let var = if_chain! {
 +            if let ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) = expr.kind;
 +            if let Some(var) = path_to_local(lhs);
 +            if expr.span.desugaring_kind().is_none();
 +            then { var } else { return; }
 +        };
 +        let mut visitor = ReadVisitor {
 +            cx,
 +            var,
 +            write_expr: expr,
 +            last_expr: expr,
 +        };
 +        check_for_unsequenced_reads(&mut visitor);
 +    }
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
 +        match stmt.kind {
 +            StmtKind::Local(local) => {
 +                if let Local { init: Some(e), .. } = local {
 +                    DivergenceVisitor { cx }.visit_expr(e);
 +                }
 +            },
 +            StmtKind::Expr(e) | StmtKind::Semi(e) => DivergenceVisitor { cx }.maybe_walk_expr(e),
 +            StmtKind::Item(..) => {},
 +        }
 +    }
 +}
 +
 +struct DivergenceVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
 +    fn maybe_walk_expr(&mut self, e: &'tcx Expr<'_>) {
 +        match e.kind {
 +            ExprKind::Closure { .. } => {},
 +            ExprKind::Match(e, arms, _) => {
 +                self.visit_expr(e);
 +                for arm in arms {
 +                    if let Some(Guard::If(if_expr)) = arm.guard {
 +                        self.visit_expr(if_expr);
 +                    }
 +                    // make sure top level arm expressions aren't linted
 +                    self.maybe_walk_expr(arm.body);
 +                }
 +            },
 +            _ => walk_expr(self, e),
 +        }
 +    }
 +    fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) {
 +        span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges");
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +        match e.kind {
 +            ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => self.report_diverging_sub_expr(e),
 +            ExprKind::Call(func, _) => {
 +                let typ = self.cx.typeck_results().expr_ty(func);
 +                match typ.kind() {
 +                    ty::FnDef(..) | ty::FnPtr(_) => {
 +                        let sig = typ.fn_sig(self.cx.tcx);
 +                        if self.cx.tcx.erase_late_bound_regions(sig).output().kind() == &ty::Never {
 +                            self.report_diverging_sub_expr(e);
 +                        }
 +                    },
 +                    _ => {},
 +                }
 +            },
 +            ExprKind::MethodCall(..) => {
 +                let borrowed_table = self.cx.typeck_results();
 +                if borrowed_table.expr_ty(e).is_never() {
 +                    self.report_diverging_sub_expr(e);
 +                }
 +            },
 +            _ => {
 +                // do not lint expressions referencing objects of type `!`, as that required a
 +                // diverging expression
 +                // to begin with
 +            },
 +        }
 +        self.maybe_walk_expr(e);
 +    }
 +    fn visit_block(&mut self, _: &'tcx Block<'_>) {
 +        // don't continue over blocks, LateLintPass already does that
 +    }
 +}
 +
 +/// Walks up the AST from the given write expression (`vis.write_expr`) looking
 +/// for reads to the same variable that are unsequenced relative to the write.
 +///
 +/// This means reads for which there is a common ancestor between the read and
 +/// the write such that
 +///
 +/// * evaluating the ancestor necessarily evaluates both the read and the write (for example, `&x`
 +///   and `|| x = 1` don't necessarily evaluate `x`), and
 +///
 +/// * which one is evaluated first depends on the order of sub-expression evaluation. Blocks, `if`s,
 +///   loops, `match`es, and the short-circuiting logical operators are considered to have a defined
 +///   evaluation order.
 +///
 +/// When such a read is found, the lint is triggered.
 +fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) {
 +    let map = &vis.cx.tcx.hir();
 +    let mut cur_id = vis.write_expr.hir_id;
 +    loop {
 +        let parent_id = map.get_parent_node(cur_id);
 +        if parent_id == cur_id {
 +            break;
 +        }
++        let Some(parent_node) = map.find(parent_id) else { break };
 +
 +        let stop_early = match parent_node {
 +            Node::Expr(expr) => check_expr(vis, expr),
 +            Node::Stmt(stmt) => check_stmt(vis, stmt),
 +            Node::Item(_) => {
 +                // We reached the top of the function, stop.
 +                break;
 +            },
 +            _ => StopEarly::KeepGoing,
 +        };
 +        match stop_early {
 +            StopEarly::Stop => break,
 +            StopEarly::KeepGoing => {},
 +        }
 +
 +        cur_id = parent_id;
 +    }
 +}
 +
 +/// Whether to stop early for the loop in `check_for_unsequenced_reads`. (If
 +/// `check_expr` weren't an independent function, this would be unnecessary and
 +/// we could just use `break`).
 +enum StopEarly {
 +    KeepGoing,
 +    Stop,
 +}
 +
 +fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) -> StopEarly {
 +    if expr.hir_id == vis.last_expr.hir_id {
 +        return StopEarly::KeepGoing;
 +    }
 +
 +    match expr.kind {
 +        ExprKind::Array(_)
 +        | ExprKind::Tup(_)
 +        | ExprKind::MethodCall(..)
 +        | ExprKind::Call(_, _)
 +        | ExprKind::Assign(..)
 +        | ExprKind::Index(_, _)
 +        | ExprKind::Repeat(_, _)
 +        | ExprKind::Struct(_, _, _) => {
 +            walk_expr(vis, expr);
 +        },
 +        ExprKind::Binary(op, _, _) | ExprKind::AssignOp(op, _, _) => {
 +            if op.node == BinOpKind::And || op.node == BinOpKind::Or {
 +                // x && y and x || y always evaluate x first, so these are
 +                // strictly sequenced.
 +            } else {
 +                walk_expr(vis, expr);
 +            }
 +        },
 +        ExprKind::Closure { .. } => {
 +            // Either
 +            //
 +            // * `var` is defined in the closure body, in which case we've reached the top of the enclosing
 +            //   function and can stop, or
 +            //
 +            // * `var` is captured by the closure, in which case, because evaluating a closure does not evaluate
 +            //   its body, we don't necessarily have a write, so we need to stop to avoid generating false
 +            //   positives.
 +            //
 +            // This is also the only place we need to stop early (grrr).
 +            return StopEarly::Stop;
 +        },
 +        // All other expressions either have only one child or strictly
 +        // sequence the evaluation order of their sub-expressions.
 +        _ => {},
 +    }
 +
 +    vis.last_expr = expr;
 +
 +    StopEarly::KeepGoing
 +}
 +
 +fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly {
 +    match stmt.kind {
 +        StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr),
 +        // If the declaration is of a local variable, check its initializer
 +        // expression if it has one. Otherwise, keep going.
 +        StmtKind::Local(local) => local
 +            .init
 +            .as_ref()
 +            .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)),
 +        StmtKind::Item(..) => StopEarly::KeepGoing,
 +    }
 +}
 +
 +/// A visitor that looks for reads from a variable.
 +struct ReadVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    /// The ID of the variable we're looking for.
 +    var: HirId,
 +    /// The expressions where the write to the variable occurred (for reporting
 +    /// in the lint).
 +    write_expr: &'tcx Expr<'tcx>,
 +    /// The last (highest in the AST) expression we've checked, so we know not
 +    /// to recheck it.
 +    last_expr: &'tcx Expr<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if expr.hir_id == self.last_expr.hir_id {
 +            return;
 +        }
 +
 +        if path_to_local_id(expr, self.var) {
 +            // Check that this is a read, not a write.
 +            if !is_in_assignment_position(self.cx, expr) {
 +                span_lint_and_note(
 +                    self.cx,
 +                    MIXED_READ_WRITE_IN_EXPRESSION,
 +                    expr.span,
 +                    &format!("unsequenced read of `{}`", self.cx.tcx.hir().name(self.var)),
 +                    Some(self.write_expr.span),
 +                    "whether read occurs before this write depends on evaluation order",
 +                );
 +            }
 +        }
 +        match expr.kind {
 +            // We're about to descend a closure. Since we don't know when (or
 +            // if) the closure will be evaluated, any reads in it might not
 +            // occur here (or ever). Like above, bail to avoid false positives.
 +            ExprKind::Closure{..} |
 +
 +            // We want to avoid a false positive when a variable name occurs
 +            // only to have its address taken, so we stop here. Technically,
 +            // this misses some weird cases, eg.
 +            //
 +            // ```rust
 +            // let mut x = 0;
 +            // let a = foo(&{x = 1; x}, x);
 +            // ```
 +            //
 +            // TODO: fix this
 +            ExprKind::AddrOf(_, _, _) => {
 +                return;
 +            }
 +            _ => {}
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +}
 +
 +/// Returns `true` if `expr` is the LHS of an assignment, like `expr = ...`.
 +fn is_in_assignment_position(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if let Some(parent) = get_parent_expr(cx, expr) {
 +        if let ExprKind::Assign(lhs, ..) = parent.kind {
 +            return lhs.hir_id == expr.hir_id;
 +        }
 +    }
 +    false
 +}
index 3233d87c073193f99a0046745ff49c603f7576a5,0000000000000000000000000000000000000000..c3b633fd6a038919bfcdea2c9935030b7a9ab45f
mode 100644,000000..100644
--- /dev/null
@@@ -1,160 -1,0 +1,159 @@@
-         let expr = match stmt.kind {
-             StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr,
-             _ => return,
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    intravisit::{walk_expr, Visitor},
 +    Closure, Expr, ExprKind, Stmt, StmtKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{source_map::Span, sym, Symbol};
 +
 +use if_chain::if_chain;
 +
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::is_trait_method;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::has_iter_method;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `for_each` that would be more simply written as a
 +    /// `for` loop.
 +    ///
 +    /// ### Why is this bad?
 +    /// `for_each` may be used after applying iterator transformers like
 +    /// `filter` for better readability and performance. It may also be used to fit a simple
 +    /// operation on one line.
 +    /// But when none of these apply, a simple `for` loop is more idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let v = vec![0, 1, 2];
 +    /// v.iter().for_each(|elem| {
 +    ///     println!("{}", elem);
 +    /// })
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let v = vec![0, 1, 2];
 +    /// for elem in v.iter() {
 +    ///     println!("{}", elem);
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub NEEDLESS_FOR_EACH,
 +    pedantic,
 +    "using `for_each` where a `for` loop would be simpler"
 +}
 +
 +declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]);
 +
 +impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
++        let (StmtKind::Expr(expr) | StmtKind::Semi(expr)) = stmt.kind else {
++             return
 +        };
 +
 +        if_chain! {
 +            // Check the method name is `for_each`.
 +            if let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind;
 +            if method_name.ident.name == Symbol::intern("for_each");
 +            // Check `for_each` is an associated function of `Iterator`.
 +            if is_trait_method(cx, expr, sym::Iterator);
 +            // Checks the receiver of `for_each` is also a method call.
 +            if let ExprKind::MethodCall(_, iter_recv, [], _) = for_each_recv.kind;
 +            // Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or
 +            // `v.foo().iter().for_each()` must be skipped.
 +            if matches!(
 +                iter_recv.kind,
 +                ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..)
 +            );
 +            // Checks the type of the `iter` method receiver is NOT a user defined type.
 +            if has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some();
 +            // Skip the lint if the body is not block because this is simpler than `for` loop.
 +            // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop.
 +            if let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind;
 +            let body = cx.tcx.hir().body(body);
 +            if let ExprKind::Block(..) = body.value.kind;
 +            then {
 +                let mut ret_collector = RetCollector::default();
 +                ret_collector.visit_expr(body.value);
 +
 +                // Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`.
 +                if ret_collector.ret_in_loop {
 +                    return;
 +                }
 +
 +                let (mut applicability, ret_suggs) = if ret_collector.spans.is_empty() {
 +                    (Applicability::MachineApplicable, None)
 +                } else {
 +                    (
 +                        Applicability::MaybeIncorrect,
 +                        Some(
 +                            ret_collector
 +                                .spans
 +                                .into_iter()
 +                                .map(|span| (span, "continue".to_string()))
 +                                .collect(),
 +                        ),
 +                    )
 +                };
 +
 +                let sugg = format!(
 +                    "for {} in {} {}",
 +                    snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability),
 +                    snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability),
 +                    snippet_with_applicability(cx, body.value.span, "..", &mut applicability),
 +                );
 +
 +                span_lint_and_then(cx, NEEDLESS_FOR_EACH, stmt.span, "needless use of `for_each`", |diag| {
 +                    diag.span_suggestion(stmt.span, "try", sugg, applicability);
 +                    if let Some(ret_suggs) = ret_suggs {
 +                        diag.multipart_suggestion("...and replace `return` with `continue`", ret_suggs, applicability);
 +                    }
 +                })
 +            }
 +        }
 +    }
 +}
 +
 +/// This type plays two roles.
 +/// 1. Collect spans of `return` in the closure body.
 +/// 2. Detect use of `return` in `Loop` in the closure body.
 +///
 +/// NOTE: The functionality of this type is similar to
 +/// [`clippy_utils::visitors::find_all_ret_expressions`], but we can't use
 +/// `find_all_ret_expressions` instead of this type. The reasons are:
 +/// 1. `find_all_ret_expressions` passes the argument of `ExprKind::Ret` to a callback, but what we
 +///    need here is `ExprKind::Ret` itself.
 +/// 2. We can't trace current loop depth with `find_all_ret_expressions`.
 +#[derive(Default)]
 +struct RetCollector {
 +    spans: Vec<Span>,
 +    ret_in_loop: bool,
 +    loop_depth: u16,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for RetCollector {
 +    fn visit_expr(&mut self, expr: &Expr<'_>) {
 +        match expr.kind {
 +            ExprKind::Ret(..) => {
 +                if self.loop_depth > 0 && !self.ret_in_loop {
 +                    self.ret_in_loop = true;
 +                }
 +
 +                self.spans.push(expr.span);
 +            },
 +
 +            ExprKind::Loop(..) => {
 +                self.loop_depth += 1;
 +                walk_expr(self, expr);
 +                self.loop_depth -= 1;
 +                return;
 +            },
 +
 +            _ => {},
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +}
index a7e0e35787cffbcc092d5ae27a293bbac7a60244,0000000000000000000000000000000000000000..5c2b96f5b2ce6287a327567635a64184cc56e1a0
mode 100644,000000..100644
--- /dev/null
@@@ -1,90 -1,0 +1,88 @@@
- use clippy_utils::{self, get_trait_def_id, paths};
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::ty::implements_trait;
 +use if_chain::if_chain;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the usage of negated comparison operators on types which only implement
 +    /// `PartialOrd` (e.g., `f64`).
 +    ///
 +    /// ### Why is this bad?
 +    /// These operators make it easy to forget that the underlying types actually allow not only three
 +    /// potential Orderings (Less, Equal, Greater) but also a fourth one (Uncomparable). This is
 +    /// especially easy to miss if the operator based comparison result is negated.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = 1.0;
 +    /// let b = f64::NAN;
 +    ///
 +    /// let not_less_or_equal = !(a <= b);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::cmp::Ordering;
 +    /// # let a = 1.0;
 +    /// # let b = f64::NAN;
 +    ///
 +    /// let _not_less_or_equal = match a.partial_cmp(&b) {
 +    ///     None | Some(Ordering::Greater) => true,
 +    ///     _ => false,
 +    /// };
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEG_CMP_OP_ON_PARTIAL_ORD,
 +    complexity,
 +    "The use of negated comparison operators on partially ordered types may produce confusing code."
 +}
 +
 +declare_lint_pass!(NoNegCompOpForPartialOrd => [NEG_CMP_OP_ON_PARTIAL_ORD]);
 +
 +impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if !in_external_macro(cx.sess(), expr.span);
 +            if let ExprKind::Unary(UnOp::Not, inner) = expr.kind;
 +            if let ExprKind::Binary(ref op, left, _) = inner.kind;
 +            if let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node;
 +
 +            then {
-                     if let Some(id) = get_trait_def_id(cx, &paths::ORD) {
 +                let ty = cx.typeck_results().expr_ty(left);
 +
 +                let implements_ord = {
-                         clear that the two values could be incomparable"
++                    if let Some(id) = cx.tcx.get_diagnostic_item(sym::Ord) {
 +                        implements_trait(cx, ty, id, &[])
 +                    } else {
 +                        return;
 +                    }
 +                };
 +
 +                let implements_partial_ord = {
 +                    if let Some(id) = cx.tcx.lang_items().partial_ord_trait() {
 +                        implements_trait(cx, ty, id, &[])
 +                    } else {
 +                        return;
 +                    }
 +                };
 +
 +                if implements_partial_ord && !implements_ord {
 +                    span_lint(
 +                        cx,
 +                        NEG_CMP_OP_ON_PARTIAL_ORD,
 +                        expr.span,
 +                        "the use of negated comparison operators on partially ordered \
 +                        types produces code that is hard to read and refactor, please \
 +                        consider using the `partial_cmp` method instead, to make it \
++                        clear that the two values could be incomparable",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
index 2c839d029c6f7a5bd482145e1cc5257af862e76b,0000000000000000000000000000000000000000..a6742824bc56aae7855f9c76fc1b79f00b8ca200
mode 100644,000000..100644
--- /dev/null
@@@ -1,452 -1,0 +1,451 @@@
-             let item_def_id = match cx.qpath_res(qpath, expr.hir_id) {
-                 Res::Def(DefKind::Const | DefKind::AssocConst, did) => did,
-                 _ => return,
 +//! Checks for uses of const which the type is not `Freeze` (`Cell`-free).
 +//!
 +//! This lint is **warn** by default.
 +
 +use std::ptr;
 +
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::in_constant;
 +use clippy_utils::macros::macro_backtrace;
 +use if_chain::if_chain;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{
 +    BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp,
 +};
 +use rustc_hir_analysis::hir_ty_to_ty;
 +use rustc_lint::{LateContext, LateLintPass, Lint};
 +use rustc_middle::mir;
 +use rustc_middle::mir::interpret::{ConstValue, ErrorHandled};
 +use rustc_middle::ty::adjustment::Adjust;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{sym, InnerSpan, Span, DUMMY_SP};
 +
 +// FIXME: this is a correctness problem but there's no suitable
 +// warn-by-default category.
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for declaration of `const` items which is interior
 +    /// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.).
 +    ///
 +    /// ### Why is this bad?
 +    /// Consts are copied everywhere they are referenced, i.e.,
 +    /// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
 +    /// or `AtomicXxxx` will be created, which defeats the whole purpose of using
 +    /// these types in the first place.
 +    ///
 +    /// The `const` should better be replaced by a `static` item if a global
 +    /// variable is wanted, or replaced by a `const fn` if a constructor is wanted.
 +    ///
 +    /// ### Known problems
 +    /// A "non-constant" const item is a legacy way to supply an
 +    /// initialized value to downstream `static` items (e.g., the
 +    /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,
 +    /// and this lint should be suppressed.
 +    ///
 +    /// Even though the lint avoids triggering on a constant whose type has enums that have variants
 +    /// with interior mutability, and its value uses non interior mutable variants (see
 +    /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and
 +    /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples);
 +    /// it complains about associated constants without default values only based on its types;
 +    /// which might not be preferable.
 +    /// There're other enums plus associated constants cases that the lint cannot handle.
 +    ///
 +    /// Types that have underlying or potential interior mutability trigger the lint whether
 +    /// the interior mutable field is used or not. See issues
 +    /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
 +    ///
 +    /// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);
 +    /// CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged
 +    /// assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
 +    /// static STATIC_ATOM: AtomicUsize = AtomicUsize::new(15);
 +    /// STATIC_ATOM.store(9, SeqCst);
 +    /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DECLARE_INTERIOR_MUTABLE_CONST,
 +    style,
 +    "declaring `const` with interior mutability"
 +}
 +
 +// FIXME: this is a correctness problem but there's no suitable
 +// warn-by-default category.
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks if `const` items which is interior mutable (e.g.,
 +    /// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly.
 +    ///
 +    /// ### Why is this bad?
 +    /// Consts are copied everywhere they are referenced, i.e.,
 +    /// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
 +    /// or `AtomicXxxx` will be created, which defeats the whole purpose of using
 +    /// these types in the first place.
 +    ///
 +    /// The `const` value should be stored inside a `static` item.
 +    ///
 +    /// ### Known problems
 +    /// When an enum has variants with interior mutability, use of its non
 +    /// interior mutable variants can generate false positives. See issue
 +    /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962)
 +    ///
 +    /// Types that have underlying or potential interior mutability trigger the lint whether
 +    /// the interior mutable field is used or not. See issues
 +    /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and
 +    /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825)
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
 +    /// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);
 +    ///
 +    /// CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged
 +    /// assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
 +    /// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);
 +    ///
 +    /// static STATIC_ATOM: AtomicUsize = CONST_ATOM;
 +    /// STATIC_ATOM.store(9, SeqCst);
 +    /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub BORROW_INTERIOR_MUTABLE_CONST,
 +    style,
 +    "referencing `const` with interior mutability"
 +}
 +
 +fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`,
 +    // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is
 +    // 'unfrozen'. However, this code causes a false negative in which
 +    // a type contains a layout-unknown type, but also an unsafe cell like `const CELL: Cell<T>`.
 +    // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)`
 +    // since it works when a pointer indirection involves (`Cell<*const T>`).
 +    // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option;
 +    // but I'm not sure whether it's a decent way, if possible.
 +    cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env)
 +}
 +
 +fn is_value_unfrozen_raw<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    result: Result<ConstValue<'tcx>, ErrorHandled>,
 +    ty: Ty<'tcx>,
 +) -> bool {
 +    fn inner<'tcx>(cx: &LateContext<'tcx>, val: mir::ConstantKind<'tcx>) -> bool {
 +        match val.ty().kind() {
 +            // the fact that we have to dig into every structs to search enums
 +            // leads us to the point checking `UnsafeCell` directly is the only option.
 +            ty::Adt(ty_def, ..) if ty_def.is_unsafe_cell() => true,
 +            // As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the
 +            // contained value.
 +            ty::Adt(def, ..) if def.is_union() => false,
 +            ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => {
 +                let val = cx.tcx.destructure_mir_constant(cx.param_env, val);
 +                val.fields.iter().any(|field| inner(cx, *field))
 +            },
 +            _ => false,
 +        }
 +    }
 +    result.map_or_else(
 +        |err| {
 +            // Consider `TooGeneric` cases as being unfrozen.
 +            // This causes a false positive where an assoc const whose type is unfrozen
 +            // have a value that is a frozen variant with a generic param (an example is
 +            // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::GENERIC_VARIANT`).
 +            // However, it prevents a number of false negatives that is, I think, important:
 +            // 1. assoc consts in trait defs referring to consts of themselves
 +            //    (an example is `declare_interior_mutable_const::traits::ConcreteTypes::ANOTHER_ATOMIC`).
 +            // 2. a path expr referring to assoc consts whose type is doesn't have
 +            //    any frozen variants in trait defs (i.e. without substitute for `Self`).
 +            //    (e.g. borrowing `borrow_interior_mutable_const::trait::ConcreteTypes::ATOMIC`)
 +            // 3. similar to the false positive above;
 +            //    but the value is an unfrozen variant, or the type has no enums. (An example is
 +            //    `declare_interior_mutable_const::enums::BothOfCellAndGeneric::UNFROZEN_VARIANT`
 +            //    and `declare_interior_mutable_const::enums::BothOfCellAndGeneric::NO_ENUM`).
 +            // One might be able to prevent these FNs correctly, and replace this with `false`;
 +            // e.g. implementing `has_frozen_variant` described above, and not running this function
 +            // when the type doesn't have any frozen variants would be the 'correct' way for the 2nd
 +            // case (that actually removes another suboptimal behavior (I won't say 'false positive') where,
 +            // similar to 2., but with the a frozen variant) (e.g. borrowing
 +            // `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`).
 +            // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none).
 +            err == ErrorHandled::TooGeneric
 +        },
 +        |val| inner(cx, mir::ConstantKind::from_value(val, ty)),
 +    )
 +}
 +
 +fn is_value_unfrozen_poly<'tcx>(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool {
 +    let result = cx.tcx.const_eval_poly(body_id.hir_id.owner.to_def_id());
 +    is_value_unfrozen_raw(cx, result, ty)
 +}
 +
 +fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
 +    let substs = cx.typeck_results().node_substs(hir_id);
 +
 +    let result = cx.tcx.const_eval_resolve(
 +        cx.param_env,
 +        mir::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs),
 +        None,
 +    );
 +    is_value_unfrozen_raw(cx, result, ty)
 +}
 +
 +#[derive(Copy, Clone)]
 +enum Source {
 +    Item { item: Span },
 +    Assoc { item: Span },
 +    Expr { expr: Span },
 +}
 +
 +impl Source {
 +    #[must_use]
 +    fn lint(&self) -> (&'static Lint, &'static str, Span) {
 +        match self {
 +            Self::Item { item } | Self::Assoc { item, .. } => (
 +                DECLARE_INTERIOR_MUTABLE_CONST,
 +                "a `const` item should never be interior mutable",
 +                *item,
 +            ),
 +            Self::Expr { expr } => (
 +                BORROW_INTERIOR_MUTABLE_CONST,
 +                "a `const` item with interior mutability should not be borrowed",
 +                *expr,
 +            ),
 +        }
 +    }
 +}
 +
 +fn lint(cx: &LateContext<'_>, source: Source) {
 +    let (lint, msg, span) = source.lint();
 +    span_lint_and_then(cx, lint, span, msg, |diag| {
 +        if span.from_expansion() {
 +            return; // Don't give suggestions into macros.
 +        }
 +        match source {
 +            Source::Item { .. } => {
 +                let const_kw_span = span.from_inner(InnerSpan::new(0, 5));
 +                diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)");
 +            },
 +            Source::Assoc { .. } => (),
 +            Source::Expr { .. } => {
 +                diag.help("assign this const to a local or static variable, and use the variable here");
 +            },
 +        }
 +    });
 +}
 +
 +declare_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]);
 +
 +impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
 +        if let ItemKind::Const(hir_ty, body_id) = it.kind {
 +            let ty = hir_ty_to_ty(cx.tcx, hir_ty);
 +            if !ignored_macro(cx, it) && is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) {
 +                lint(cx, Source::Item { item: it.span });
 +            }
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) {
 +        if let TraitItemKind::Const(hir_ty, body_id_opt) = &trait_item.kind {
 +            let ty = hir_ty_to_ty(cx.tcx, hir_ty);
 +
 +            // Normalize assoc types because ones originated from generic params
 +            // bounded other traits could have their bound.
 +            let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
 +            if is_unfrozen(cx, normalized)
 +                // When there's no default value, lint it only according to its type;
 +                // in other words, lint consts whose value *could* be unfrozen, not definitely is.
 +                // This feels inconsistent with how the lint treats generic types,
 +                // which avoids linting types which potentially become unfrozen.
 +                // One could check whether an unfrozen type have a *frozen variant*
 +                // (like `body_id_opt.map_or_else(|| !has_frozen_variant(...), ...)`),
 +                // and do the same as the case of generic types at impl items.
 +                // Note that it isn't sufficient to check if it has an enum
 +                // since all of that enum's variants can be unfrozen:
 +                // i.e. having an enum doesn't necessary mean a type has a frozen variant.
 +                // And, implementing it isn't a trivial task; it'll probably end up
 +                // re-implementing the trait predicate evaluation specific to `Freeze`.
 +                && body_id_opt.map_or(true, |body_id| is_value_unfrozen_poly(cx, body_id, normalized))
 +            {
 +                lint(cx, Source::Assoc { item: trait_item.span });
 +            }
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
 +        if let ImplItemKind::Const(hir_ty, body_id) = &impl_item.kind {
 +            let item_def_id = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id;
 +            let item = cx.tcx.hir().expect_item(item_def_id);
 +
 +            match &item.kind {
 +                ItemKind::Impl(Impl {
 +                    of_trait: Some(of_trait_ref),
 +                    ..
 +                }) => {
 +                    if_chain! {
 +                        // Lint a trait impl item only when the definition is a generic type,
 +                        // assuming an assoc const is not meant to be an interior mutable type.
 +                        if let Some(of_trait_def_id) = of_trait_ref.trait_def_id();
 +                        if let Some(of_assoc_item) = cx
 +                            .tcx
 +                            .associated_item(impl_item.def_id)
 +                            .trait_item_def_id;
 +                        if cx
 +                            .tcx
 +                            .layout_of(cx.tcx.param_env(of_trait_def_id).and(
 +                                // Normalize assoc types because ones originated from generic params
 +                                // bounded other traits could have their bound at the trait defs;
 +                                // and, in that case, the definition is *not* generic.
 +                                cx.tcx.normalize_erasing_regions(
 +                                    cx.tcx.param_env(of_trait_def_id),
 +                                    cx.tcx.type_of(of_assoc_item),
 +                                ),
 +                            ))
 +                            .is_err();
 +                            // If there were a function like `has_frozen_variant` described above,
 +                            // we should use here as a frozen variant is a potential to be frozen
 +                            // similar to unknown layouts.
 +                            // e.g. `layout_of(...).is_err() || has_frozen_variant(...);`
 +                        let ty = hir_ty_to_ty(cx.tcx, hir_ty);
 +                        let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
 +                        if is_unfrozen(cx, normalized);
 +                        if is_value_unfrozen_poly(cx, *body_id, normalized);
 +                        then {
 +                            lint(
 +                               cx,
 +                               Source::Assoc {
 +                                   item: impl_item.span,
 +                                },
 +                            );
 +                        }
 +                    }
 +                },
 +                ItemKind::Impl(Impl { of_trait: None, .. }) => {
 +                    let ty = hir_ty_to_ty(cx.tcx, hir_ty);
 +                    // Normalize assoc types originated from generic params.
 +                    let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
 +
 +                    if is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, *body_id, normalized) {
 +                        lint(cx, Source::Assoc { item: impl_item.span });
 +                    }
 +                },
 +                _ => (),
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::Path(qpath) = &expr.kind {
 +            // Only lint if we use the const item inside a function.
 +            if in_constant(cx, expr.hir_id) {
 +                return;
 +            }
 +
 +            // Make sure it is a const item.
++            let Res::Def(DefKind::Const | DefKind::AssocConst, item_def_id) = cx.qpath_res(qpath, expr.hir_id) else {
++                return
 +            };
 +
 +            // Climb up to resolve any field access and explicit referencing.
 +            let mut cur_expr = expr;
 +            let mut dereferenced_expr = expr;
 +            let mut needs_check_adjustment = true;
 +            loop {
 +                let parent_id = cx.tcx.hir().get_parent_node(cur_expr.hir_id);
 +                if parent_id == cur_expr.hir_id {
 +                    break;
 +                }
 +                if let Some(Node::Expr(parent_expr)) = cx.tcx.hir().find(parent_id) {
 +                    match &parent_expr.kind {
 +                        ExprKind::AddrOf(..) => {
 +                            // `&e` => `e` must be referenced.
 +                            needs_check_adjustment = false;
 +                        },
 +                        ExprKind::Field(..) => {
 +                            needs_check_adjustment = true;
 +
 +                            // Check whether implicit dereferences happened;
 +                            // if so, no need to go further up
 +                            // because of the same reason as the `ExprKind::Unary` case.
 +                            if cx
 +                                .typeck_results()
 +                                .expr_adjustments(dereferenced_expr)
 +                                .iter()
 +                                .any(|adj| matches!(adj.kind, Adjust::Deref(_)))
 +                            {
 +                                break;
 +                            }
 +
 +                            dereferenced_expr = parent_expr;
 +                        },
 +                        ExprKind::Index(e, _) if ptr::eq(&**e, cur_expr) => {
 +                            // `e[i]` => desugared to `*Index::index(&e, i)`,
 +                            // meaning `e` must be referenced.
 +                            // no need to go further up since a method call is involved now.
 +                            needs_check_adjustment = false;
 +                            break;
 +                        },
 +                        ExprKind::Unary(UnOp::Deref, _) => {
 +                            // `*e` => desugared to `*Deref::deref(&e)`,
 +                            // meaning `e` must be referenced.
 +                            // no need to go further up since a method call is involved now.
 +                            needs_check_adjustment = false;
 +                            break;
 +                        },
 +                        _ => break,
 +                    }
 +                    cur_expr = parent_expr;
 +                } else {
 +                    break;
 +                }
 +            }
 +
 +            let ty = if needs_check_adjustment {
 +                let adjustments = cx.typeck_results().expr_adjustments(dereferenced_expr);
 +                if let Some(i) = adjustments
 +                    .iter()
 +                    .position(|adj| matches!(adj.kind, Adjust::Borrow(_) | Adjust::Deref(_)))
 +                {
 +                    if i == 0 {
 +                        cx.typeck_results().expr_ty(dereferenced_expr)
 +                    } else {
 +                        adjustments[i - 1].target
 +                    }
 +                } else {
 +                    // No borrow adjustments means the entire const is moved.
 +                    return;
 +                }
 +            } else {
 +                cx.typeck_results().expr_ty(dereferenced_expr)
 +            };
 +
 +            if is_unfrozen(cx, ty) && is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty) {
 +                lint(cx, Source::Expr { expr: expr.span });
 +            }
 +        }
 +    }
 +}
 +
 +fn ignored_macro(cx: &LateContext<'_>, it: &rustc_hir::Item<'_>) -> bool {
 +    macro_backtrace(it.span).any(|macro_call| {
 +        matches!(
 +            cx.tcx.get_diagnostic_name(macro_call.def_id),
 +            Some(sym::thread_local_macro)
 +        )
 +    })
 +}
index 1a765b14892f6fccba6e1f6b4a2c0ca52435e6f2,0000000000000000000000000000000000000000..2ecb04874842f8aeca17b4c7ba5ba69e2e9c0665
mode 100644,000000..100644
--- /dev/null
@@@ -1,107 -1,0 +1,100 @@@
-                         let snip = match snippet_opt(cx, param.span) {
-                             Some(s) => s,
-                             _ => return,
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::{snippet_opt, snippet_with_applicability};
 +use clippy_utils::ty::{is_type_diagnostic_item, match_type};
 +use clippy_utils::{match_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for non-octal values used to set Unix file permissions.
 +    ///
 +    /// ### Why is this bad?
 +    /// They will be converted into octal, creating potentially
 +    /// unintended file permissions.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// use std::fs::OpenOptions;
 +    /// use std::os::unix::fs::OpenOptionsExt;
 +    ///
 +    /// let mut options = OpenOptions::new();
 +    /// options.mode(644);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// use std::fs::OpenOptions;
 +    /// use std::os::unix::fs::OpenOptionsExt;
 +    ///
 +    /// let mut options = OpenOptions::new();
 +    /// options.mode(0o644);
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub NON_OCTAL_UNIX_PERMISSIONS,
 +    correctness,
 +    "use of non-octal value to set unix file permissions, which will be translated into octal"
 +}
 +
 +declare_lint_pass!(NonOctalUnixPermissions => [NON_OCTAL_UNIX_PERMISSIONS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
 +        match &expr.kind {
 +            ExprKind::MethodCall(path, func, [param], _) => {
 +                let obj_ty = cx.typeck_results().expr_ty(func).peel_refs();
 +
 +                if_chain! {
 +                    if (path.ident.name == sym!(mode)
 +                        && (match_type(cx, obj_ty, &paths::OPEN_OPTIONS)
 +                            || is_type_diagnostic_item(cx, obj_ty, sym::DirBuilder)))
 +                        || (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS));
 +                    if let ExprKind::Lit(_) = param.kind;
 +
 +                    then {
++                        let Some(snip) = snippet_opt(cx, param.span) else {
++                            return
 +                        };
 +
 +                        if !snip.starts_with("0o") {
 +                            show_error(cx, param);
 +                        }
 +                    }
 +                }
 +            },
 +            ExprKind::Call(func, [param]) => {
 +                if_chain! {
 +                    if let ExprKind::Path(ref path) = func.kind;
 +                    if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id();
 +                    if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE);
 +                    if let ExprKind::Lit(_) = param.kind;
-                         let snip = match snippet_opt(cx, param.span) {
-                             Some(s) => s,
-                             _ => return,
-                         };
-                         if !snip.starts_with("0o") {
-                             show_error(cx, param);
-                         }
++                    if let Some(snip) = snippet_opt(cx, param.span);
++                    if !snip.starts_with("0o");
 +                    then {
++                        show_error(cx, param);
 +                    }
 +                }
 +            },
 +            _ => {},
 +        };
 +    }
 +}
 +
 +fn show_error(cx: &LateContext<'_>, param: &Expr<'_>) {
 +    let mut applicability = Applicability::MachineApplicable;
 +    span_lint_and_sugg(
 +        cx,
 +        NON_OCTAL_UNIX_PERMISSIONS,
 +        param.span,
 +        "using a non-octal value to set unix file permissions",
 +        "consider using an octal literal instead",
 +        format!(
 +            "0o{}",
 +            snippet_with_applicability(cx, param.span, "0o..", &mut applicability,),
 +        ),
 +        applicability,
 +    );
 +}
index 0ca0befc13515e92f16c59e871215ce4c815ff90,0000000000000000000000000000000000000000..6c909e5ed73ea6b5faac116b6ca5bb657a3cc21f
mode 100644,000000..100644
--- /dev/null
@@@ -1,277 -1,0 +1,277 @@@
-                         .ok_or_else(|| de::Error::custom(&format!("expected one of `(`, `{{`, `[` found `{brace}`")))?,
 +use std::{
 +    fmt,
 +    hash::{Hash, Hasher},
 +};
 +
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_opt;
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::DefId;
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::hygiene::{ExpnKind, MacroKind};
 +use rustc_span::Span;
 +use serde::{de, Deserialize};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks that common macros are used with consistent bracing.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is mostly a consistency lint although using () or []
 +    /// doesn't give you a semicolon in item position, which can be unexpected.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// vec!{1, 2, 3};
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// vec![1, 2, 3];
 +    /// ```
 +    #[clippy::version = "1.55.0"]
 +    pub NONSTANDARD_MACRO_BRACES,
 +    nursery,
 +    "check consistent use of braces in macro"
 +}
 +
 +const BRACES: &[(&str, &str)] = &[("(", ")"), ("{", "}"), ("[", "]")];
 +
 +/// The (callsite span, (open brace, close brace), source snippet)
 +type MacroInfo<'a> = (Span, &'a (String, String), String);
 +
 +#[derive(Clone, Debug, Default)]
 +pub struct MacroBraces {
 +    macro_braces: FxHashMap<String, (String, String)>,
 +    done: FxHashSet<Span>,
 +}
 +
 +impl MacroBraces {
 +    pub fn new(conf: &FxHashSet<MacroMatcher>) -> Self {
 +        let macro_braces = macro_braces(conf.clone());
 +        Self {
 +            macro_braces,
 +            done: FxHashSet::default(),
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(MacroBraces => [NONSTANDARD_MACRO_BRACES]);
 +
 +impl EarlyLintPass for MacroBraces {
 +    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
 +        if let Some((span, braces, snip)) = is_offending_macro(cx, item.span, self) {
 +            emit_help(cx, &snip, braces, span);
 +            self.done.insert(span);
 +        }
 +    }
 +
 +    fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) {
 +        if let Some((span, braces, snip)) = is_offending_macro(cx, stmt.span, self) {
 +            emit_help(cx, &snip, braces, span);
 +            self.done.insert(span);
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
 +        if let Some((span, braces, snip)) = is_offending_macro(cx, expr.span, self) {
 +            emit_help(cx, &snip, braces, span);
 +            self.done.insert(span);
 +        }
 +    }
 +
 +    fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
 +        if let Some((span, braces, snip)) = is_offending_macro(cx, ty.span, self) {
 +            emit_help(cx, &snip, braces, span);
 +            self.done.insert(span);
 +        }
 +    }
 +}
 +
 +fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, mac_braces: &'a MacroBraces) -> Option<MacroInfo<'a>> {
 +    let unnested_or_local = || {
 +        !span.ctxt().outer_expn_data().call_site.from_expansion()
 +            || span
 +                .macro_backtrace()
 +                .last()
 +                .map_or(false, |e| e.macro_def_id.map_or(false, DefId::is_local))
 +    };
 +    let span_call_site = span.ctxt().outer_expn_data().call_site;
 +    if_chain! {
 +        if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind;
 +        let name = mac_name.as_str();
 +        if let Some(braces) = mac_braces.macro_braces.get(name);
 +        if let Some(snip) = snippet_opt(cx, span_call_site);
 +        // we must check only invocation sites
 +        // https://github.com/rust-lang/rust-clippy/issues/7422
 +        if snip.starts_with(&format!("{name}!"));
 +        if unnested_or_local();
 +        // make formatting consistent
 +        let c = snip.replace(' ', "");
 +        if !c.starts_with(&format!("{name}!{}", braces.0));
 +        if !mac_braces.done.contains(&span_call_site);
 +        then {
 +            Some((span_call_site, braces, snip))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +fn emit_help(cx: &EarlyContext<'_>, snip: &str, braces: &(String, String), span: Span) {
 +    if let Some((macro_name, macro_args_str)) = snip.split_once('!') {
 +        let mut macro_args = macro_args_str.trim().to_string();
 +        // now remove the wrong braces
 +        macro_args.remove(0);
 +        macro_args.pop();
 +        span_lint_and_sugg(
 +            cx,
 +            NONSTANDARD_MACRO_BRACES,
 +            span,
 +            &format!("use of irregular braces for `{macro_name}!` macro"),
 +            "consider writing",
 +            format!("{macro_name}!{}{macro_args}{}", braces.0, braces.1),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +fn macro_braces(conf: FxHashSet<MacroMatcher>) -> FxHashMap<String, (String, String)> {
 +    let mut braces = vec![
 +        macro_matcher!(
 +            name: "print",
 +            braces: ("(", ")"),
 +        ),
 +        macro_matcher!(
 +            name: "println",
 +            braces: ("(", ")"),
 +        ),
 +        macro_matcher!(
 +            name: "eprint",
 +            braces: ("(", ")"),
 +        ),
 +        macro_matcher!(
 +            name: "eprintln",
 +            braces: ("(", ")"),
 +        ),
 +        macro_matcher!(
 +            name: "write",
 +            braces: ("(", ")"),
 +        ),
 +        macro_matcher!(
 +            name: "writeln",
 +            braces: ("(", ")"),
 +        ),
 +        macro_matcher!(
 +            name: "format",
 +            braces: ("(", ")"),
 +        ),
 +        macro_matcher!(
 +            name: "format_args",
 +            braces: ("(", ")"),
 +        ),
 +        macro_matcher!(
 +            name: "vec",
 +            braces: ("[", "]"),
 +        ),
 +        macro_matcher!(
 +            name: "matches",
 +            braces: ("(", ")"),
 +        ),
 +    ]
 +    .into_iter()
 +    .collect::<FxHashMap<_, _>>();
 +    // We want users items to override any existing items
 +    for it in conf {
 +        braces.insert(it.name, it.braces);
 +    }
 +    braces
 +}
 +
 +macro_rules! macro_matcher {
 +    (name: $name:expr, braces: ($open:expr, $close:expr) $(,)?) => {
 +        ($name.to_owned(), ($open.to_owned(), $close.to_owned()))
 +    };
 +}
 +pub(crate) use macro_matcher;
 +
 +#[derive(Clone, Debug)]
 +pub struct MacroMatcher {
 +    name: String,
 +    braces: (String, String),
 +}
 +
 +impl Hash for MacroMatcher {
 +    fn hash<H: Hasher>(&self, state: &mut H) {
 +        self.name.hash(state);
 +    }
 +}
 +
 +impl PartialEq for MacroMatcher {
 +    fn eq(&self, other: &Self) -> bool {
 +        self.name == other.name
 +    }
 +}
 +impl Eq for MacroMatcher {}
 +
 +impl<'de> Deserialize<'de> for MacroMatcher {
 +    fn deserialize<D>(deser: D) -> Result<Self, D::Error>
 +    where
 +        D: de::Deserializer<'de>,
 +    {
 +        #[derive(Deserialize)]
 +        #[serde(field_identifier, rename_all = "lowercase")]
 +        enum Field {
 +            Name,
 +            Brace,
 +        }
 +        struct MacVisitor;
 +        impl<'de> de::Visitor<'de> for MacVisitor {
 +            type Value = MacroMatcher;
 +
 +            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
 +                formatter.write_str("struct MacroMatcher")
 +            }
 +
 +            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
 +            where
 +                V: de::MapAccess<'de>,
 +            {
 +                let mut name = None;
 +                let mut brace: Option<&str> = None;
 +                while let Some(key) = map.next_key()? {
 +                    match key {
 +                        Field::Name => {
 +                            if name.is_some() {
 +                                return Err(de::Error::duplicate_field("name"));
 +                            }
 +                            name = Some(map.next_value()?);
 +                        },
 +                        Field::Brace => {
 +                            if brace.is_some() {
 +                                return Err(de::Error::duplicate_field("brace"));
 +                            }
 +                            brace = Some(map.next_value()?);
 +                        },
 +                    }
 +                }
 +                let name = name.ok_or_else(|| de::Error::missing_field("name"))?;
 +                let brace = brace.ok_or_else(|| de::Error::missing_field("brace"))?;
 +                Ok(MacroMatcher {
 +                    name,
 +                    braces: BRACES
 +                        .iter()
 +                        .find(|b| b.0 == brace)
 +                        .map(|(o, c)| ((*o).to_owned(), (*c).to_owned()))
++                        .ok_or_else(|| de::Error::custom(format!("expected one of `(`, `{{`, `[` found `{brace}`")))?,
 +                })
 +            }
 +        }
 +
 +        const FIELDS: &[&str] = &["name", "brace"];
 +        deser.deserialize_struct("MacroMatcher", FIELDS, MacVisitor)
 +    }
 +}
index c9c777f1bd8d8854e66e19f4c0fa77b17a3bc059,0000000000000000000000000000000000000000..24aeb82a37f3107095a9944ca1b5fb9738a0c51b
mode 100644,000000..100644
--- /dev/null
@@@ -1,145 -1,0 +1,147 @@@
- use clippy_utils::{match_any_def_paths, path_def_id, paths};
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::{implements_trait, is_copy};
-             if path_def_id(cx, path)
-                 .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
-                 .map_or(false, |idx| match idx {
-                     0 => true,
-                     1 => !is_copy(cx, typeck.expr_ty(expr)),
-                     _ => false,
-                 }) =>
++use clippy_utils::{match_def_path, path_def_id, paths};
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::Ty;
 +use rustc_span::symbol::sym;
 +
 +use super::CMP_OWNED;
 +
 +pub(super) fn check(cx: &LateContext<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>) {
 +    if op.is_comparison() {
 +        check_op(cx, lhs, rhs, true);
 +        check_op(cx, rhs, lhs, false);
 +    }
 +}
 +
 +#[derive(Default)]
 +struct EqImpl {
 +    ty_eq_other: bool,
 +    other_eq_ty: bool,
 +}
 +impl EqImpl {
 +    fn is_implemented(&self) -> bool {
 +        self.ty_eq_other || self.other_eq_ty
 +    }
 +}
 +
 +fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> Option<EqImpl> {
 +    cx.tcx.lang_items().eq_trait().map(|def_id| EqImpl {
 +        ty_eq_other: implements_trait(cx, ty, def_id, &[other.into()]),
 +        other_eq_ty: implements_trait(cx, other, def_id, &[ty.into()]),
 +    })
 +}
 +
 +fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
 +    let typeck = cx.typeck_results();
 +    let (arg, arg_span) = match expr.kind {
 +        ExprKind::MethodCall(_, arg, [], _)
 +            if typeck
 +                .type_dependent_def_id(expr.hir_id)
 +                .and_then(|id| cx.tcx.trait_of_item(id))
 +                .map_or(false, |id| {
 +                    matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned))
 +                }) =>
 +        {
 +            (arg, arg.span)
 +        },
 +        ExprKind::Call(path, [arg])
++            if path_def_id(cx, path).map_or(false, |id| {
++                if match_def_path(cx, id, &paths::FROM_STR_METHOD) {
++                    true
++                } else if cx.tcx.lang_items().from_fn() == Some(id) {
++                    !is_copy(cx, typeck.expr_ty(expr))
++                } else {
++                    false
++                }
++            }) =>
 +        {
 +            (arg, arg.span)
 +        },
 +        _ => return,
 +    };
 +
 +    let arg_ty = typeck.expr_ty(arg);
 +    let other_ty = typeck.expr_ty(other);
 +
 +    let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default();
 +    let with_deref = arg_ty
 +        .builtin_deref(true)
 +        .and_then(|tam| symmetric_partial_eq(cx, tam.ty, other_ty))
 +        .unwrap_or_default();
 +
 +    if !with_deref.is_implemented() && !without_deref.is_implemented() {
 +        return;
 +    }
 +
 +    let other_gets_derefed = matches!(other.kind, ExprKind::Unary(UnOp::Deref, _));
 +
 +    let lint_span = if other_gets_derefed {
 +        expr.span.to(other.span)
 +    } else {
 +        expr.span
 +    };
 +
 +    span_lint_and_then(
 +        cx,
 +        CMP_OWNED,
 +        lint_span,
 +        "this creates an owned instance just for comparison",
 +        |diag| {
 +            // This also catches `PartialEq` implementations that call `to_owned`.
 +            if other_gets_derefed {
 +                diag.span_label(lint_span, "try implementing the comparison without allocating");
 +                return;
 +            }
 +
 +            let arg_snip = snippet(cx, arg_span, "..");
 +            let expr_snip;
 +            let eq_impl;
 +            if with_deref.is_implemented() {
 +                expr_snip = format!("*{arg_snip}");
 +                eq_impl = with_deref;
 +            } else {
 +                expr_snip = arg_snip.to_string();
 +                eq_impl = without_deref;
 +            };
 +
 +            let span;
 +            let hint;
 +            if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) {
 +                span = expr.span;
 +                hint = expr_snip;
 +            } else {
 +                span = expr.span.to(other.span);
 +
 +                let cmp_span = if other.span < expr.span {
 +                    other.span.between(expr.span)
 +                } else {
 +                    expr.span.between(other.span)
 +                };
 +                if eq_impl.ty_eq_other {
 +                    hint = format!(
 +                        "{expr_snip}{}{}",
 +                        snippet(cx, cmp_span, ".."),
 +                        snippet(cx, other.span, "..")
 +                    );
 +                } else {
 +                    hint = format!(
 +                        "{}{}{expr_snip}",
 +                        snippet(cx, other.span, ".."),
 +                        snippet(cx, cmp_span, "..")
 +                    );
 +                }
 +            }
 +
 +            diag.span_suggestion(
 +                span,
 +                "try",
 +                hint,
 +                Applicability::MachineApplicable, // snippet
 +            );
 +        },
 +    );
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f60d9d65b1207a345698bde417aa7094ee1deae3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,81 @@@
++use clippy_utils::diagnostics::span_lint_and_help;
++use rustc_ast::ast::{Item, ItemKind};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks whether partial fields of a struct are public.
++    ///
++    /// Either make all fields of a type public, or make none of them public
++    ///
++    /// ### Why is this bad?
++    /// Most types should either be:
++    /// * Abstract data types: complex objects with opaque implementation which guard
++    /// interior invariants and expose intentionally limited API to the outside world.
++    /// * Data: relatively simple objects which group a bunch of related attributes together.
++    ///
++    /// ### Example
++    /// ```rust
++    /// pub struct Color {
++    ///     pub r: u8,
++    ///     pub g: u8,
++    ///     b: u8,
++    /// }
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// pub struct Color {
++    ///     pub r: u8,
++    ///     pub g: u8,
++    ///     pub b: u8,
++    /// }
++    /// ```
++    #[clippy::version = "1.66.0"]
++    pub PARTIAL_PUB_FIELDS,
++    restriction,
++    "partial fields of a struct are public"
++}
++declare_lint_pass!(PartialPubFields => [PARTIAL_PUB_FIELDS]);
++
++impl EarlyLintPass for PartialPubFields {
++    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
++        let ItemKind::Struct(ref st, _) = item.kind else {
++            return;
++        };
++
++        let mut fields = st.fields().iter();
++        let Some(first_field) = fields.next() else {
++            // Empty struct.
++            return;
++        };
++        let all_pub = first_field.vis.kind.is_pub();
++        let all_priv = !all_pub;
++
++        let msg = "mixed usage of pub and non-pub fields";
++
++        for field in fields {
++            if all_priv && field.vis.kind.is_pub() {
++                span_lint_and_help(
++                    cx,
++                    PARTIAL_PUB_FIELDS,
++                    field.vis.span,
++                    msg,
++                    None,
++                    "consider using private field here",
++                );
++                return;
++            } else if all_pub && !field.vis.kind.is_pub() {
++                span_lint_and_help(
++                    cx,
++                    PARTIAL_PUB_FIELDS,
++                    field.vis.span,
++                    msg,
++                    None,
++                    "consider using public field here",
++                );
++                return;
++            }
++        }
++    }
++}
index d296a150b46d085cec600152a3c2a05abac8af49,0000000000000000000000000000000000000000..40db315bf272612a86ab6b0b2aeff431e9159de5
mode 100644,000000..100644
--- /dev/null
@@@ -1,687 -1,0 +1,727 @@@
- use rustc_middle::ty::{self, Ty};
 +//! Checks for usage of  `&Vec[_]` and `&String`.
 +
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_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::{
 +    self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg,
 +    ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn,
 +    TraitItem, TraitItemKind, TyKind, Unsafety,
 +};
++use rustc_infer::infer::TyCtxtInferExt;
++use rustc_infer::traits::{Obligation, ObligationCause};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
-             let args_idx = match path_to_local(e).and_then(|id| self.bindings.get(&id)) {
-                 Some(&i) => i,
-                 None => return walk_expr(self, e),
++use rustc_middle::ty::{self, Binder, ExistentialPredicate, List, PredicateKind, 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 rustc_trait_selection::infer::InferCtxtExt as _;
++use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 +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
 +    /// fn foo(&Vec<u32>) { .. }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```ignore
 +    /// 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
 +    /// ```rust,ignore
 +    /// use std::ptr;
 +    ///
 +    /// if x == ptr::null {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// if x.is_null() {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CMP_NULL,
 +    style,
 +    "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for functions that take immutable references and return
 +    /// mutable ones. 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?
 +    /// 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
 +    /// 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
 +    /// // Undefined behavior
 +    /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```ignore
 +    /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub INVALID_NULL_PTR_USAGE,
 +    correctness,
 +    "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
 +}
 +
 +declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Ptr {
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if let TraitItemKind::Fn(sig, trait_method) = &item.kind {
 +            if matches!(trait_method, TraitFn::Provided(_)) {
 +                // Handled by check body.
 +                return;
 +            }
 +
 +            check_mut_from_ref(cx, sig, 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_hir_and_then(cx, PTR_ARG, arg.emission_id, arg.span, &arg.build_msg(), |diag| {
 +                    diag.span_suggestion(
 +                        arg.span,
 +                        "change this to",
 +                        format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
 +                        Applicability::Unspecified,
 +                    );
 +                });
 +            }
 +        }
 +    }
 +
 +    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
 +        let hir = cx.tcx.hir();
 +        let mut parents = hir.parent_iter(body.value.hir_id);
 +        let (item_id, sig, is_trait_item) = match parents.next() {
 +            Some((_, Node::Item(i))) => {
 +                if let ItemKind::Fn(sig, ..) = &i.kind {
 +                    (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, false)
 +                } else {
 +                    return;
 +                }
 +            },
 +            Some((_, Node::TraitItem(i))) => {
 +                if let TraitItemKind::Fn(sig, _) = &i.kind {
 +                    (i.def_id, sig, true)
 +                } else {
 +                    return;
 +                }
 +            },
 +            _ => 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_hir_and_then(cx, PTR_ARG, args.emission_id, 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,
 +    emission_id: hir::HirId,
 +    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::Infer => 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 ty(&self, cx: &LateContext<'tcx>) -> Ty<'tcx> {
++        match *self {
++            Self::Str => cx.tcx.types.str_,
++            Self::Path => cx.tcx.mk_adt(
++                cx.tcx.adt_def(cx.tcx.get_diagnostic_item(sym::Path).unwrap()),
++                List::empty(),
++            ),
++            Self::Slice(_, ty) => cx.tcx.mk_slice(ty),
++        }
++    }
++
 +    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<'tcx>],
 +    hir_tys: &'tcx [hir::Ty<'tcx>],
 +    params: &'tcx [Param<'tcx>],
 +) -> 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;
 +
 +                then {
 +                    let emission_id = params.get(i).map_or(hir_ty.hir_id, |param| param.hir_id);
 +                    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_hir_and_then(
 +                                cx,
 +                                PTR_ARG,
 +                                emission_id,
 +                                hir_ty.span,
 +                                "using a reference to `Cow` is not recommended",
 +                                |diag| {
 +                                    diag.span_suggestion(
 +                                        hir_ty.span,
 +                                        "change this to",
 +                                        format!("&{}{ty_name}", mutability.prefix_str()),
 +                                        Applicability::Unspecified,
 +                                    );
 +                                }
 +                            );
 +                            return None;
 +                        },
 +                        _ => return None,
 +                    };
 +                    return Some(PtrArg {
 +                        idx: i,
 +                        emission_id,
 +                        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
 +        })
 +}
 +
 +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 out_region = cx.tcx.named_region(out.hir_id);
 +        let args: Option<Vec<_>> = sig
 +            .decl
 +            .inputs
 +            .iter()
 +            .filter_map(get_rptr_lm)
 +            .filter(|&(lt, _, _)| cx.tcx.named_region(lt.hir_id) == out_region)
 +            .map(|(_, mutability, span)| (mutability == Mutability::Not).then_some(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");
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +#[expect(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 id = if let Some(x) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) {
-                             x
-                         } else {
++            let Some(&args_idx) = path_to_local(e).and_then(|id| self.bindings.get(&id)) else {
++                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::NONE, 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).and_then(|sig| sig.input(i)).map_or(true, |ty| {
 +                            match *ty.skip_binder().peel_refs().kind() {
++                                ty::Dynamic(preds, _, _) => !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds),
 +                                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, self_arg, expr_args, _) => {
 +                        let i = std::iter::once(self_arg)
 +                            .chain(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 Some(id) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) else {
 +                            set_skip_flag();
 +                            return;
 +                        };
 +
 +                        match *self.cx.tcx.fn_sig(id).skip_binder().inputs()[i].peel_refs().kind() {
++                            ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => {
++                                set_skip_flag();
++                            },
 +                            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::NONE, 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 matches_preds<'tcx>(
++    cx: &LateContext<'tcx>,
++    ty: Ty<'tcx>,
++    preds: &'tcx [Binder<'tcx, ExistentialPredicate<'tcx>>],
++) -> bool {
++    let infcx = cx.tcx.infer_ctxt().build();
++    preds.iter().all(|&p| match cx.tcx.erase_late_bound_regions(p) {
++        ExistentialPredicate::Trait(p) => infcx
++            .type_implements_trait(p.def_id, ty, p.substs, cx.param_env)
++            .must_apply_modulo_regions(),
++        ExistentialPredicate::Projection(p) => infcx.predicate_must_hold_modulo_regions(&Obligation::new(
++            ObligationCause::dummy(),
++            cx.param_env,
++            cx.tcx.mk_predicate(Binder::bind_with_vars(
++                PredicateKind::Projection(p.with_self_ty(cx.tcx, ty)),
++                List::empty(),
++            )),
++        )),
++        ExistentialPredicate::AutoTrait(p) => infcx
++            .type_implements_trait(p, ty, List::empty(), cx.param_env)
++            .must_apply_modulo_regions(),
++    })
++}
++
 +fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
 +    if let TyKind::Rptr(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 b0a5d1a6758285d268071ffbc1e2a586dfa2da45,0000000000000000000000000000000000000000..72dda67c72b25d09a64aa9c953d668c42342b4da
mode 100644,000000..100644
--- /dev/null
@@@ -1,153 -1,0 +1,151 @@@
-         let (receiver_expr, arg_expr, method) = match expr_as_ptr_offset_call(cx, expr) {
-             Some(call_arg) => call_arg,
-             None => return,
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 +use clippy_utils::source::snippet_opt;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +use std::fmt;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of the `offset` pointer method with a `usize` casted to an
 +    /// `isize`.
 +    ///
 +    /// ### Why is this bad?
 +    /// If we’re always increasing the pointer address, we can avoid the numeric
 +    /// cast by using the `add` method instead.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec = vec![b'a', b'b', b'c'];
 +    /// let ptr = vec.as_ptr();
 +    /// let offset = 1_usize;
 +    ///
 +    /// unsafe {
 +    ///     ptr.offset(offset as isize);
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```rust
 +    /// let vec = vec![b'a', b'b', b'c'];
 +    /// let ptr = vec.as_ptr();
 +    /// let offset = 1_usize;
 +    ///
 +    /// unsafe {
 +    ///     ptr.add(offset);
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.30.0"]
 +    pub PTR_OFFSET_WITH_CAST,
 +    complexity,
 +    "unneeded pointer offset cast"
 +}
 +
 +declare_lint_pass!(PtrOffsetWithCast => [PTR_OFFSET_WITH_CAST]);
 +
 +impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call
-         let cast_lhs_expr = match expr_as_cast_from_usize(cx, arg_expr) {
-             Some(cast_lhs_expr) => cast_lhs_expr,
-             None => return,
++        let Some((receiver_expr, arg_expr, method)) = expr_as_ptr_offset_call(cx, expr) else {
++            return
 +        };
 +
 +        // Check if the argument to the method call is a cast from usize
++        let Some(cast_lhs_expr) = expr_as_cast_from_usize(cx, arg_expr) else {
++            return
 +        };
 +
 +        let msg = format!("use of `{method}` with a `usize` casted to an `isize`");
 +        if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) {
 +            span_lint_and_sugg(
 +                cx,
 +                PTR_OFFSET_WITH_CAST,
 +                expr.span,
 +                &msg,
 +                "try",
 +                sugg,
 +                Applicability::MachineApplicable,
 +            );
 +        } else {
 +            span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, &msg);
 +        }
 +    }
 +}
 +
 +// If the given expression is a cast from a usize, return the lhs of the cast
 +fn expr_as_cast_from_usize<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +    if let ExprKind::Cast(cast_lhs_expr, _) = expr.kind {
 +        if is_expr_ty_usize(cx, cast_lhs_expr) {
 +            return Some(cast_lhs_expr);
 +        }
 +    }
 +    None
 +}
 +
 +// If the given expression is a ptr::offset  or ptr::wrapping_offset method call, return the
 +// receiver, the arg of the method call, and the method.
 +fn expr_as_ptr_offset_call<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> {
 +    if let ExprKind::MethodCall(path_segment, arg_0, [arg_1, ..], _) = &expr.kind {
 +        if is_expr_ty_raw_ptr(cx, arg_0) {
 +            if path_segment.ident.name == sym::offset {
 +                return Some((arg_0, arg_1, Method::Offset));
 +            }
 +            if path_segment.ident.name == sym!(wrapping_offset) {
 +                return Some((arg_0, arg_1, Method::WrappingOffset));
 +            }
 +        }
 +    }
 +    None
 +}
 +
 +// Is the type of the expression a usize?
 +fn is_expr_ty_usize<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool {
 +    cx.typeck_results().expr_ty(expr) == cx.tcx.types.usize
 +}
 +
 +// Is the type of the expression a raw pointer?
 +fn is_expr_ty_raw_ptr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool {
 +    cx.typeck_results().expr_ty(expr).is_unsafe_ptr()
 +}
 +
 +fn build_suggestion<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    method: Method,
 +    receiver_expr: &Expr<'_>,
 +    cast_lhs_expr: &Expr<'_>,
 +) -> Option<String> {
 +    let receiver = snippet_opt(cx, receiver_expr.span)?;
 +    let cast_lhs = snippet_opt(cx, cast_lhs_expr.span)?;
 +    Some(format!("{receiver}.{}({cast_lhs})", method.suggestion()))
 +}
 +
 +#[derive(Copy, Clone)]
 +enum Method {
 +    Offset,
 +    WrappingOffset,
 +}
 +
 +impl Method {
 +    #[must_use]
 +    fn suggestion(self) -> &'static str {
 +        match self {
 +            Self::Offset => "add",
 +            Self::WrappingOffset => "wrapping_add",
 +        }
 +    }
 +}
 +
 +impl fmt::Display for Method {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        match self {
 +            Self::Offset => write!(f, "offset"),
 +            Self::WrappingOffset => write!(f, "wrapping_offset"),
 +        }
 +    }
 +}
index 9fd86331ec755d8fbeafb87bce01d4f98f4786af,0000000000000000000000000000000000000000..aedbe08e3e46e2e340c0263cc7766f4ab9c50c81
mode 100644,000000..100644
--- /dev/null
@@@ -1,776 -1,0 +1,392 @@@
- use rustc_data_structures::fx::FxHashMap;
 +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
++use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth};
 +use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths};
 +use if_chain::if_chain;
- use rustc_index::bit_set::{BitSet, HybridBitSet};
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::FnKind;
 +use rustc_hir::{def_id, Body, FnDecl, HirId};
- use rustc_middle::mir::{
-     self, traversal,
-     visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _},
-     Mutability,
- };
- use rustc_middle::ty::{self, visit::TypeVisitor, Ty};
- use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis, ResultsCursor};
 +use rustc_lint::{LateContext, LateLintPass};
- use std::ops::ControlFlow;
++use rustc_middle::mir;
++use rustc_middle::ty::{self, Ty};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::{BytePos, Span};
 +use rustc_span::sym;
-         let possible_origin = {
-             let mut vis = PossibleOriginVisitor::new(mir);
-             vis.visit_body(mir);
-             vis.into_map(cx)
-         };
-         let maybe_storage_live_result = MaybeStorageLive
-             .into_engine(cx.tcx, mir)
-             .pass_name("redundant_clone")
-             .iterate_to_fixpoint()
-             .into_results_cursor(mir);
-         let mut possible_borrower = {
-             let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin);
-             vis.visit_body(mir);
-             vis.into_map(cx, maybe_storage_live_result)
-         };
 +
 +macro_rules! unwrap_or_continue {
 +    ($x:expr) => {
 +        match $x {
 +            Some(x) => x,
 +            None => continue,
 +        }
 +    };
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for a redundant `clone()` (and its relatives) which clones an owned
 +    /// value that is going to be dropped without further use.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is not always possible for the compiler to eliminate useless
 +    /// allocations and deallocations generated by redundant `clone()`s.
 +    ///
 +    /// ### Known problems
 +    /// False-negatives: analysis performed by this lint is conservative and limited.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::path::Path;
 +    /// # #[derive(Clone)]
 +    /// # struct Foo;
 +    /// # impl Foo {
 +    /// #     fn new() -> Self { Foo {} }
 +    /// # }
 +    /// # fn call(x: Foo) {}
 +    /// {
 +    ///     let x = Foo::new();
 +    ///     call(x.clone());
 +    ///     call(x.clone()); // this can just pass `x`
 +    /// }
 +    ///
 +    /// ["lorem", "ipsum"].join(" ").to_string();
 +    ///
 +    /// Path::new("/a/b").join("c").to_path_buf();
 +    /// ```
 +    #[clippy::version = "1.32.0"]
 +    pub REDUNDANT_CLONE,
 +    perf,
 +    "`clone()` of an owned value that is going to be dropped immediately"
 +}
 +
 +declare_lint_pass!(RedundantClone => [REDUNDANT_CLONE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for RedundantClone {
 +    #[expect(clippy::too_many_lines)]
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        _: FnKind<'tcx>,
 +        _: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        _: Span,
 +        _: HirId,
 +    ) {
 +        let def_id = cx.tcx.hir().body_owner_def_id(body.id());
 +
 +        // Building MIR for `fn`s with unsatisfiable preds results in ICE.
 +        if fn_has_unsatisfiable_preds(cx, def_id.to_def_id()) {
 +            return;
 +        }
 +
 +        let mir = cx.tcx.optimized_mir(def_id.to_def_id());
 +
- fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage {
-     struct V {
-         cloned: mir::Local,
-         clone: mir::Local,
-         result: CloneUsage,
-     }
-     impl<'tcx> mir::visit::Visitor<'tcx> for V {
-         fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) {
-             let statements = &data.statements;
-             for (statement_index, statement) in statements.iter().enumerate() {
-                 self.visit_statement(statement, mir::Location { block, statement_index });
-             }
-             self.visit_terminator(
-                 data.terminator(),
-                 mir::Location {
-                     block,
-                     statement_index: statements.len(),
-                 },
-             );
-         }
-         fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, loc: mir::Location) {
-             let local = place.local;
-             if local == self.cloned
-                 && !matches!(
-                     ctx,
-                     PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)
-                 )
-             {
-                 self.result.cloned_used = true;
-                 self.result.cloned_consume_or_mutate_loc = self.result.cloned_consume_or_mutate_loc.or_else(|| {
-                     matches!(
-                         ctx,
-                         PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
-                             | PlaceContext::MutatingUse(MutatingUseContext::Borrow)
-                     )
-                     .then(|| loc)
-                 });
-             } else if local == self.clone {
-                 match ctx {
-                     PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
-                     | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
-                         self.result.clone_consumed_or_mutated = true;
-                     },
-                     _ => {},
-                 }
-             }
-         }
-     }
-     let init = CloneUsage {
-         cloned_used: false,
-         cloned_consume_or_mutate_loc: None,
-         // Consider non-temporary clones consumed.
-         // TODO: Actually check for mutation of non-temporaries.
-         clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp,
-     };
-     traversal::ReversePostorder::new(mir, bb)
-         .skip(1)
-         .fold(init, |usage, (tbb, tdata)| {
-             // Short-circuit
-             if (usage.cloned_used && usage.clone_consumed_or_mutated) ||
-                 // Give up on loops
-                 tdata.terminator().successors().any(|s| s == bb)
-             {
-                 return CloneUsage {
-                     cloned_used: true,
-                     clone_consumed_or_mutated: true,
-                     ..usage
-                 };
-             }
-             let mut v = V {
-                 cloned,
-                 clone,
-                 result: usage,
-             };
-             v.visit_basic_block_data(tbb, tdata);
-             v.result
-         })
- }
- /// Determines liveness of each local purely based on `StorageLive`/`Dead`.
- #[derive(Copy, Clone)]
- struct MaybeStorageLive;
- impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive {
-     type Domain = BitSet<mir::Local>;
-     const NAME: &'static str = "maybe_storage_live";
-     fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
-         // bottom = dead
-         BitSet::new_empty(body.local_decls.len())
-     }
-     fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
-         for arg in body.args_iter() {
-             state.insert(arg);
-         }
-     }
- }
- impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive {
-     type Idx = mir::Local;
-     fn statement_effect(&self, trans: &mut impl GenKill<Self::Idx>, stmt: &mir::Statement<'tcx>, _: mir::Location) {
-         match stmt.kind {
-             mir::StatementKind::StorageLive(l) => trans.gen(l),
-             mir::StatementKind::StorageDead(l) => trans.kill(l),
-             _ => (),
-         }
-     }
-     fn terminator_effect(
-         &self,
-         _trans: &mut impl GenKill<Self::Idx>,
-         _terminator: &mir::Terminator<'tcx>,
-         _loc: mir::Location,
-     ) {
-     }
-     fn call_return_effect(
-         &self,
-         _trans: &mut impl GenKill<Self::Idx>,
-         _block: mir::BasicBlock,
-         _return_places: CallReturnPlaces<'_, 'tcx>,
-     ) {
-         // Nothing to do when a call returns successfully
-     }
- }
- /// Collects the possible borrowers of each local.
- /// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
- /// possible borrowers of `a`.
- struct PossibleBorrowerVisitor<'a, 'tcx> {
-     possible_borrower: TransitiveRelation,
-     body: &'a mir::Body<'tcx>,
-     cx: &'a LateContext<'tcx>,
-     possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
- }
- impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> {
-     fn new(
-         cx: &'a LateContext<'tcx>,
-         body: &'a mir::Body<'tcx>,
-         possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
-     ) -> Self {
-         Self {
-             possible_borrower: TransitiveRelation::default(),
-             cx,
-             body,
-             possible_origin,
-         }
-     }
-     fn into_map(
-         self,
-         cx: &LateContext<'tcx>,
-         maybe_live: ResultsCursor<'tcx, 'tcx, MaybeStorageLive>,
-     ) -> PossibleBorrowerMap<'a, 'tcx> {
-         let mut map = FxHashMap::default();
-         for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
-             if is_copy(cx, self.body.local_decls[row].ty) {
-                 continue;
-             }
-             let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len());
-             borrowers.remove(mir::Local::from_usize(0));
-             if !borrowers.is_empty() {
-                 map.insert(row, borrowers);
-             }
-         }
-         let bs = BitSet::new_empty(self.body.local_decls.len());
-         PossibleBorrowerMap {
-             map,
-             maybe_live,
-             bitset: (bs.clone(), bs),
-         }
-     }
- }
- impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> {
-     fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
-         let lhs = place.local;
-         match rvalue {
-             mir::Rvalue::Ref(_, _, borrowed) => {
-                 self.possible_borrower.add(borrowed.local, lhs);
-             },
-             other => {
-                 if ContainsRegion
-                     .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty)
-                     .is_continue()
-                 {
-                     return;
-                 }
-                 rvalue_locals(other, |rhs| {
-                     if lhs != rhs {
-                         self.possible_borrower.add(rhs, lhs);
-                     }
-                 });
-             },
-         }
-     }
-     fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) {
-         if let mir::TerminatorKind::Call {
-             args,
-             destination: mir::Place { local: dest, .. },
-             ..
-         } = &terminator.kind
-         {
-             // TODO add doc
-             // If the call returns something with lifetimes,
-             // let's conservatively assume the returned value contains lifetime of all the arguments.
-             // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`.
-             let mut immutable_borrowers = vec![];
-             let mut mutable_borrowers = vec![];
-             for op in args {
-                 match op {
-                     mir::Operand::Copy(p) | mir::Operand::Move(p) => {
-                         if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() {
-                             mutable_borrowers.push(p.local);
-                         } else {
-                             immutable_borrowers.push(p.local);
-                         }
-                     },
-                     mir::Operand::Constant(..) => (),
-                 }
-             }
-             let mut mutable_variables: Vec<mir::Local> = mutable_borrowers
-                 .iter()
-                 .filter_map(|r| self.possible_origin.get(r))
-                 .flat_map(HybridBitSet::iter)
-                 .collect();
-             if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() {
-                 mutable_variables.push(*dest);
-             }
-             for y in mutable_variables {
-                 for x in &immutable_borrowers {
-                     self.possible_borrower.add(*x, y);
-                 }
-                 for x in &mutable_borrowers {
-                     self.possible_borrower.add(*x, y);
-                 }
-             }
-         }
-     }
- }
- /// Collect possible borrowed for every `&mut` local.
- /// For example, `_1 = &mut _2` generate _1: {_2,...}
- /// Known Problems: not sure all borrowed are tracked
- struct PossibleOriginVisitor<'a, 'tcx> {
-     possible_origin: TransitiveRelation,
-     body: &'a mir::Body<'tcx>,
- }
- impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> {
-     fn new(body: &'a mir::Body<'tcx>) -> Self {
-         Self {
-             possible_origin: TransitiveRelation::default(),
-             body,
-         }
-     }
-     fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, HybridBitSet<mir::Local>> {
-         let mut map = FxHashMap::default();
-         for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
-             if is_copy(cx, self.body.local_decls[row].ty) {
-                 continue;
-             }
-             let mut borrowers = self.possible_origin.reachable_from(row, self.body.local_decls.len());
-             borrowers.remove(mir::Local::from_usize(0));
-             if !borrowers.is_empty() {
-                 map.insert(row, borrowers);
-             }
-         }
-         map
-     }
- }
- impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> {
-     fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
-         let lhs = place.local;
-         match rvalue {
-             // Only consider `&mut`, which can modify origin place
-             mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) |
-             // _2: &mut _;
-             // _3 = move _2
-             mir::Rvalue::Use(mir::Operand::Move(borrowed))  |
-             // _3 = move _2 as &mut _;
-             mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _)
-                 => {
-                 self.possible_origin.add(lhs, borrowed.local);
-             },
-             _ => {},
-         }
-     }
- }
- struct ContainsRegion;
- impl TypeVisitor<'_> for ContainsRegion {
-     type BreakTy = ();
-     fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow<Self::BreakTy> {
-         ControlFlow::BREAK
-     }
- }
- fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
-     use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use};
-     let mut visit_op = |op: &mir::Operand<'_>| match op {
-         mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local),
-         mir::Operand::Constant(..) => (),
-     };
++        let mut possible_borrower = PossibleBorrowerMap::new(cx, mir);
 +
 +        for (bb, bbdata) in mir.basic_blocks.iter_enumerated() {
 +            let terminator = bbdata.terminator();
 +
 +            if terminator.source_info.span.from_expansion() {
 +                continue;
 +            }
 +
 +            // Give up on loops
 +            if terminator.successors().any(|s| s == bb) {
 +                continue;
 +            }
 +
 +            let (fn_def_id, arg, arg_ty, clone_ret) =
 +                unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind));
 +
 +            let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
 +                || match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD)
 +                || (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD)
 +                    && is_type_diagnostic_item(cx, arg_ty, sym::String));
 +
 +            let from_deref = !from_borrow
 +                && (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF)
 +                    || match_def_path(cx, fn_def_id, &paths::OS_STR_TO_OS_STRING));
 +
 +            if !from_borrow && !from_deref {
 +                continue;
 +            }
 +
 +            if let ty::Adt(def, _) = arg_ty.kind() {
 +                if def.is_manually_drop() {
 +                    continue;
 +                }
 +            }
 +
 +            // `{ arg = &cloned; clone(move arg); }` or `{ arg = &cloned; to_path_buf(arg); }`
 +            let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(cx, mir, arg, from_borrow, bb));
 +
 +            let loc = mir::Location {
 +                block: bb,
 +                statement_index: bbdata.statements.len(),
 +            };
 +
 +            // `Local` to be cloned, and a local of `clone` call's destination
 +            let (local, ret_local) = if from_borrow {
 +                // `res = clone(arg)` can be turned into `res = move arg;`
 +                // if `arg` is the only borrow of `cloned` at this point.
 +
 +                if cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc) {
 +                    continue;
 +                }
 +
 +                (cloned, clone_ret)
 +            } else {
 +                // `arg` is a reference as it is `.deref()`ed in the previous block.
 +                // Look into the predecessor block and find out the source of deref.
 +
 +                let ps = &mir.basic_blocks.predecessors()[bb];
 +                if ps.len() != 1 {
 +                    continue;
 +                }
 +                let pred_terminator = mir[ps[0]].terminator();
 +
 +                // receiver of the `deref()` call
 +                let (pred_arg, deref_clone_ret) = if_chain! {
 +                    if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, res)) =
 +                        is_call_with_ref_arg(cx, mir, &pred_terminator.kind);
 +                    if res == cloned;
 +                    if cx.tcx.is_diagnostic_item(sym::deref_method, pred_fn_def_id);
 +                    if is_type_diagnostic_item(cx, pred_arg_ty, sym::PathBuf)
 +                        || is_type_diagnostic_item(cx, pred_arg_ty, sym::OsString);
 +                    then {
 +                        (pred_arg, res)
 +                    } else {
 +                        continue;
 +                    }
 +                };
 +
 +                let (local, cannot_move_out) =
 +                    unwrap_or_continue!(find_stmt_assigns_to(cx, mir, pred_arg, true, ps[0]));
 +                let loc = mir::Location {
 +                    block: bb,
 +                    statement_index: mir.basic_blocks[bb].statements.len(),
 +                };
 +
 +                // This can be turned into `res = move local` if `arg` and `cloned` are not borrowed
 +                // at the last statement:
 +                //
 +                // ```
 +                // pred_arg = &local;
 +                // cloned = deref(pred_arg);
 +                // arg = &cloned;
 +                // StorageDead(pred_arg);
 +                // res = to_path_buf(cloned);
 +                // ```
 +                if cannot_move_out || !possible_borrower.only_borrowers(&[arg, cloned], local, loc) {
 +                    continue;
 +                }
 +
 +                (local, deref_clone_ret)
 +            };
 +
 +            let clone_usage = if local == ret_local {
 +                CloneUsage {
 +                    cloned_used: false,
 +                    cloned_consume_or_mutate_loc: None,
 +                    clone_consumed_or_mutated: true,
 +                }
 +            } else {
 +                let clone_usage = visit_clone_usage(local, ret_local, mir, bb);
 +                if clone_usage.cloned_used && clone_usage.clone_consumed_or_mutated {
 +                    // cloned value is used, and the clone is modified or moved
 +                    continue;
 +                } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc {
 +                    // cloned value is mutated, and the clone is alive.
 +                    if possible_borrower.local_is_alive_at(ret_local, loc) {
 +                        continue;
 +                    }
 +                }
 +                clone_usage
 +            };
 +
 +            let span = terminator.source_info.span;
 +            let scope = terminator.source_info.scope;
 +            let node = mir.source_scopes[scope]
 +                .local_data
 +                .as_ref()
 +                .assert_crate_local()
 +                .lint_root;
 +
 +            if_chain! {
 +                if let Some(snip) = snippet_opt(cx, span);
 +                if let Some(dot) = snip.rfind('.');
 +                then {
 +                    let sugg_span = span.with_lo(
 +                        span.lo() + BytePos(u32::try_from(dot).unwrap())
 +                    );
 +                    let mut app = Applicability::MaybeIncorrect;
 +
 +                    let call_snip = &snip[dot + 1..];
 +                    // Machine applicable when `call_snip` looks like `foobar()`
 +                    if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) {
 +                        if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') {
 +                            app = Applicability::MachineApplicable;
 +                        }
 +                    }
 +
 +                    span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| {
 +                        diag.span_suggestion(
 +                            sugg_span,
 +                            "remove this",
 +                            "",
 +                            app,
 +                        );
 +                        if clone_usage.cloned_used {
 +                            diag.span_note(
 +                                span,
 +                                "cloned value is neither consumed nor mutated",
 +                            );
 +                        } else {
 +                            diag.span_note(
 +                                span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())),
 +                                "this value is dropped without further use",
 +                            );
 +                        }
 +                    });
 +                } else {
 +                    span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone");
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// If `kind` is `y = func(x: &T)` where `T: !Copy`, returns `(DefId of func, x, T, y)`.
 +fn is_call_with_ref_arg<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    mir: &'tcx mir::Body<'tcx>,
 +    kind: &'tcx mir::TerminatorKind<'tcx>,
 +) -> Option<(def_id::DefId, mir::Local, Ty<'tcx>, mir::Local)> {
 +    if_chain! {
 +        if let mir::TerminatorKind::Call { func, args, destination, .. } = kind;
 +        if args.len() == 1;
 +        if let mir::Operand::Move(mir::Place { local, .. }) = &args[0];
 +        if let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind();
 +        if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(mir, cx.tcx));
 +        if !is_copy(cx, inner_ty);
 +        then {
 +            Some((def_id, *local, inner_ty, destination.as_local()?))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +type CannotMoveOut = bool;
 +
 +/// Finds the first `to = (&)from`, and returns
 +/// ``Some((from, whether `from` cannot be moved out))``.
 +fn find_stmt_assigns_to<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    mir: &mir::Body<'tcx>,
 +    to_local: mir::Local,
 +    by_ref: bool,
 +    bb: mir::BasicBlock,
 +) -> Option<(mir::Local, CannotMoveOut)> {
 +    let rvalue = mir.basic_blocks[bb].statements.iter().rev().find_map(|stmt| {
 +        if let mir::StatementKind::Assign(box (mir::Place { local, .. }, v)) = &stmt.kind {
 +            return if *local == to_local { Some(v) } else { None };
 +        }
 +
 +        None
 +    })?;
 +
 +    match (by_ref, rvalue) {
 +        (true, mir::Rvalue::Ref(_, _, place)) | (false, mir::Rvalue::Use(mir::Operand::Copy(place))) => {
 +            Some(base_local_and_movability(cx, mir, *place))
 +        },
 +        (false, mir::Rvalue::Ref(_, _, place)) => {
 +            if let [mir::ProjectionElem::Deref] = place.as_ref().projection {
 +                Some(base_local_and_movability(cx, mir, *place))
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +/// Extracts and returns the undermost base `Local` of given `place`. Returns `place` itself
 +/// if it is already a `Local`.
 +///
 +/// Also reports whether given `place` cannot be moved out.
 +fn base_local_and_movability<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    mir: &mir::Body<'tcx>,
 +    place: mir::Place<'tcx>,
 +) -> (mir::Local, CannotMoveOut) {
 +    use rustc_middle::mir::PlaceRef;
 +
 +    // Dereference. You cannot move things out from a borrowed value.
 +    let mut deref = false;
 +    // Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509.
 +    let mut field = false;
 +    // If projection is a slice index then clone can be removed only if the
 +    // underlying type implements Copy
 +    let mut slice = false;
 +
 +    let PlaceRef { local, mut projection } = place.as_ref();
 +    while let [base @ .., elem] = projection {
 +        projection = base;
 +        deref |= matches!(elem, mir::ProjectionElem::Deref);
 +        field |= matches!(elem, mir::ProjectionElem::Field(..))
 +            && has_drop(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty);
 +        slice |= matches!(elem, mir::ProjectionElem::Index(..))
 +            && !is_copy(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty);
 +    }
 +
 +    (local, deref || field || slice)
 +}
 +
 +#[derive(Default)]
 +struct CloneUsage {
 +    /// Whether the cloned value is used after the clone.
 +    cloned_used: bool,
 +    /// The first location where the cloned value is consumed or mutated, if any.
 +    cloned_consume_or_mutate_loc: Option<mir::Location>,
 +    /// Whether the clone value is mutated.
 +    clone_consumed_or_mutated: bool,
 +}
-     match rvalue {
-         Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op),
-         Aggregate(_, ops) => ops.iter().for_each(visit_op),
-         BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => {
-             visit_op(lhs);
-             visit_op(rhs);
 +
-         _ => (),
-     }
- }
- /// Result of `PossibleBorrowerVisitor`.
- struct PossibleBorrowerMap<'a, 'tcx> {
-     /// Mapping `Local -> its possible borrowers`
-     map: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
-     maybe_live: ResultsCursor<'a, 'tcx, MaybeStorageLive>,
-     // Caches to avoid allocation of `BitSet` on every query
-     bitset: (BitSet<mir::Local>, BitSet<mir::Local>),
- }
- impl PossibleBorrowerMap<'_, '_> {
-     /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`.
-     fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool {
-         self.maybe_live.seek_after_primary_effect(at);
-         self.bitset.0.clear();
-         let maybe_live = &mut self.maybe_live;
-         if let Some(bitset) = self.map.get(&borrowed) {
-             for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) {
-                 self.bitset.0.insert(b);
-             }
-         } else {
-             return false;
-         }
-         self.bitset.1.clear();
-         for b in borrowers {
-             self.bitset.1.insert(*b);
++fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage {
++    if let Some((
++        LocalUsage {
++            local_use_locs: cloned_use_locs,
++            local_consume_or_mutate_locs: cloned_consume_or_mutate_locs,
 +        },
-         self.bitset.0 == self.bitset.1
-     }
-     fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {
-         self.maybe_live.seek_after_primary_effect(at);
-         self.maybe_live.contains(local)
-     }
- }
- #[derive(Default)]
- struct TransitiveRelation {
-     relations: FxHashMap<mir::Local, Vec<mir::Local>>,
- }
- impl TransitiveRelation {
-     fn add(&mut self, a: mir::Local, b: mir::Local) {
-         self.relations.entry(a).or_default().push(b);
-     }
-     fn reachable_from(&self, a: mir::Local, domain_size: usize) -> HybridBitSet<mir::Local> {
-         let mut seen = HybridBitSet::new_empty(domain_size);
-         let mut stack = vec![a];
-         while let Some(u) = stack.pop() {
-             if let Some(edges) = self.relations.get(&u) {
-                 for &v in edges {
-                     if seen.insert(v) {
-                         stack.push(v);
-                     }
-                 }
-             }
++        LocalUsage {
++            local_use_locs: _,
++            local_consume_or_mutate_locs: clone_consume_or_mutate_locs,
++        },
++    )) = visit_local_usage(
++        &[cloned, clone],
++        mir,
++        mir::Location {
++            block: bb,
++            statement_index: mir.basic_blocks[bb].statements.len(),
++        },
++    )
++    .map(|mut vec| (vec.remove(0), vec.remove(0)))
++    {
++        CloneUsage {
++            cloned_used: !cloned_use_locs.is_empty(),
++            cloned_consume_or_mutate_loc: cloned_consume_or_mutate_locs.first().copied(),
++            // Consider non-temporary clones consumed.
++            // TODO: Actually check for mutation of non-temporaries.
++            clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp
++                || !clone_consume_or_mutate_locs.is_empty(),
 +        }
-         seen
++    } else {
++        CloneUsage {
++            cloned_used: true,
++            cloned_consume_or_mutate_loc: None,
++            clone_consumed_or_mutated: true,
 +        }
 +    }
 +}
index 42514f861be1c25f33bcca47ef88ac7a2cef5b47,0000000000000000000000000000000000000000..f21b3ea6c3b05b078d8ad92ea700fc302fb9d85e
mode 100644,000000..100644
--- /dev/null
@@@ -1,70 -1,0 +1,71 @@@
-             if let TyKind::Rptr(_, _) = inner_ty.kind;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::last_path_segment;
 +use clippy_utils::source::snippet;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{GenericArg, Mutability, Ty, TyKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `&Option<&T>`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Since `&` is Copy, it's useless to have a
 +    /// reference on `Option<&T>`.
 +    ///
 +    /// ### Known problems
 +    /// It may be irrelevant to use this lint on
 +    /// public API code as it will make a breaking change to apply it.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let x: &Option<&u32> = &Some(&0u32);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// let x: Option<&u32> = Some(&0u32);
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub REF_OPTION_REF,
 +    pedantic,
 +    "use `Option<&T>` instead of `&Option<&T>`"
 +}
 +
 +declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]);
 +
 +impl<'tcx> LateLintPass<'tcx> for RefOptionRef {
 +    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
 +        if_chain! {
 +            if let TyKind::Rptr(_, ref mut_ty) = ty.kind;
 +            if mut_ty.mutbl == Mutability::Not;
 +            if let TyKind::Path(ref qpath) = &mut_ty.ty.kind;
 +            let last = last_path_segment(qpath);
 +            if let Some(def_id) = last.res.opt_def_id();
 +
 +            if cx.tcx.is_diagnostic_item(sym::Option, def_id);
 +            if let Some(params) = last_path_segment(qpath).args ;
 +            if !params.parenthesized;
 +            if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg {
 +                GenericArg::Type(inner_ty) => Some(inner_ty),
 +                _ => None,
 +            });
++            if let TyKind::Rptr(_, ref inner_mut_ty) = inner_ty.kind;
++            if inner_mut_ty.mutbl == Mutability::Not;
 +
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    REF_OPTION_REF,
 +                    ty.span,
 +                    "since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`",
 +                    "try",
 +                    format!("Option<{}>", &snippet(cx, inner_ty.span, "..")),
 +                    Applicability::MaybeIncorrect,
 +                );
 +            }
 +        }
 +    }
 +}
index 5dcdab5b8ab90e9a35a57ca5735ff56825ed9aad,0000000000000000000000000000000000000000..87f966ced0df121b640a5b301809027ed3dd3cb3
mode 100644,000000..100644
--- /dev/null
@@@ -1,252 -1,0 +1,249 @@@
-         let (id, ident) = match pat.kind {
-             PatKind::Binding(_, hir_id, ident, _) => (hir_id, ident),
-             _ => return,
-         };
 +use clippy_utils::diagnostics::span_lint_and_note;
 +use clippy_utils::source::snippet;
 +use clippy_utils::visitors::is_local_used;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::def::Res;
 +use rustc_hir::def_id::LocalDefId;
 +use rustc_hir::hir_id::ItemLocalId;
 +use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Let, Node, Pat, PatKind, QPath, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{Span, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for bindings that shadow other bindings already in
 +    /// scope, while just changing reference level or mutability.
 +    ///
 +    /// ### Why is this bad?
 +    /// Not much, in fact it's a very common pattern in Rust
 +    /// code. Still, some may opt to avoid it in their code base, they can set this
 +    /// lint to `Warn`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 1;
 +    /// let x = &x;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = 1;
 +    /// let y = &x; // use different variable name
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SHADOW_SAME,
 +    restriction,
 +    "rebinding a name to itself, e.g., `let mut x = &mut x`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for bindings that shadow other bindings already in
 +    /// scope, while reusing the original value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Not too much, in fact it's a common pattern in Rust
 +    /// code. Still, some argue that name shadowing like this hurts readability,
 +    /// because a value may be bound to different things depending on position in
 +    /// the code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 2;
 +    /// let x = x + 1;
 +    /// ```
 +    /// use different variable name:
 +    /// ```rust
 +    /// let x = 2;
 +    /// let y = x + 1;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SHADOW_REUSE,
 +    restriction,
 +    "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for bindings that shadow other bindings already in
 +    /// scope, either without an initialization or with one that does not even use
 +    /// the original value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Name shadowing can hurt readability, especially in
 +    /// large code bases, because it is easy to lose track of the active binding at
 +    /// any place in the code. This can be alleviated by either giving more specific
 +    /// names to bindings or introducing more scopes to contain the bindings.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let y = 1;
 +    /// # let z = 2;
 +    /// let x = y;
 +    /// let x = z; // shadows the earlier binding
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let y = 1;
 +    /// # let z = 2;
 +    /// let x = y;
 +    /// let w = z; // use different variable name
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SHADOW_UNRELATED,
 +    restriction,
 +    "rebinding a name without even using the original value"
 +}
 +
 +#[derive(Default)]
 +pub(crate) struct Shadow {
 +    bindings: Vec<(FxHashMap<Symbol, Vec<ItemLocalId>>, LocalDefId)>,
 +}
 +
 +impl_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Shadow {
 +    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
++        let PatKind::Binding(_, id, ident, _) = pat.kind else { return };
 +
 +        if pat.span.desugaring_kind().is_some() {
 +            return;
 +        }
 +
 +        if ident.span.from_expansion() || ident.span.is_dummy() {
 +            return;
 +        }
 +
 +        let HirId { owner, local_id } = id;
 +        // get (or insert) the list of items for this owner and symbol
 +        let (ref mut data, scope_owner) = *self.bindings.last_mut().unwrap();
 +        let items_with_name = data.entry(ident.name).or_default();
 +
 +        // check other bindings with the same name, most recently seen first
 +        for &prev in items_with_name.iter().rev() {
 +            if prev == local_id {
 +                // repeated binding in an `Or` pattern
 +                return;
 +            }
 +
 +            if is_shadow(cx, scope_owner, prev, local_id) {
 +                let prev_hir_id = HirId { owner, local_id: prev };
 +                lint_shadow(cx, pat, prev_hir_id, ident.span);
 +                // only lint against the "nearest" shadowed binding
 +                break;
 +            }
 +        }
 +        // store the binding
 +        items_with_name.push(local_id);
 +    }
 +
 +    fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
 +        let hir = cx.tcx.hir();
 +        let owner_id = hir.body_owner_def_id(body.id());
 +        if !matches!(hir.body_owner_kind(owner_id), BodyOwnerKind::Closure) {
 +            self.bindings.push((FxHashMap::default(), owner_id));
 +        }
 +    }
 +
 +    fn check_body_post(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
 +        let hir = cx.tcx.hir();
 +        if !matches!(
 +            hir.body_owner_kind(hir.body_owner_def_id(body.id())),
 +            BodyOwnerKind::Closure
 +        ) {
 +            self.bindings.pop();
 +        }
 +    }
 +}
 +
 +fn is_shadow(cx: &LateContext<'_>, owner: LocalDefId, first: ItemLocalId, second: ItemLocalId) -> bool {
 +    let scope_tree = cx.tcx.region_scope_tree(owner.to_def_id());
 +    if let Some(first_scope) = scope_tree.var_scope(first) {
 +        if let Some(second_scope) = scope_tree.var_scope(second) {
 +            return scope_tree.is_subscope_of(second_scope, first_scope);
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) {
 +    let (lint, msg) = match find_init(cx, pat.hir_id) {
 +        Some(expr) if is_self_shadow(cx, pat, expr, shadowed) => {
 +            let msg = format!(
 +                "`{}` is shadowed by itself in `{}`",
 +                snippet(cx, pat.span, "_"),
 +                snippet(cx, expr.span, "..")
 +            );
 +            (SHADOW_SAME, msg)
 +        },
 +        Some(expr) if is_local_used(cx, expr, shadowed) => {
 +            let msg = format!("`{}` is shadowed", snippet(cx, pat.span, "_"));
 +            (SHADOW_REUSE, msg)
 +        },
 +        _ => {
 +            let msg = format!("`{}` shadows a previous, unrelated binding", snippet(cx, pat.span, "_"));
 +            (SHADOW_UNRELATED, msg)
 +        },
 +    };
 +    span_lint_and_note(
 +        cx,
 +        lint,
 +        span,
 +        &msg,
 +        Some(cx.tcx.hir().span(shadowed)),
 +        "previous binding is here",
 +    );
 +}
 +
 +/// Returns true if the expression is a simple transformation of a local binding such as `&x`
 +fn is_self_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, mut expr: &Expr<'_>, hir_id: HirId) -> bool {
 +    let hir = cx.tcx.hir();
 +    let is_direct_binding = hir
 +        .parent_iter(pat.hir_id)
 +        .map_while(|(_id, node)| match node {
 +            Node::Pat(pat) => Some(pat),
 +            _ => None,
 +        })
 +        .all(|pat| matches!(pat.kind, PatKind::Ref(..) | PatKind::Or(_)));
 +    if !is_direct_binding {
 +        return false;
 +    }
 +    loop {
 +        expr = match expr.kind {
 +            ExprKind::Box(e)
 +            | ExprKind::AddrOf(_, _, e)
 +            | ExprKind::Block(
 +                &Block {
 +                    stmts: [],
 +                    expr: Some(e),
 +                    ..
 +                },
 +                _,
 +            )
 +            | ExprKind::Unary(UnOp::Deref, e) => e,
 +            ExprKind::Path(QPath::Resolved(None, path)) => break path.res == Res::Local(hir_id),
 +            _ => break false,
 +        }
 +    }
 +}
 +
 +/// Finds the "init" expression for a pattern: `let <pat> = <init>;` (or `if let`) or
 +/// `match <init> { .., <pat> => .., .. }`
 +fn find_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
 +    for (_, node) in cx.tcx.hir().parent_iter(hir_id) {
 +        let init = match node {
 +            Node::Arm(_) | Node::Pat(_) => continue,
 +            Node::Expr(expr) => match expr.kind {
 +                ExprKind::Match(e, _, _) | ExprKind::Let(&Let { init: e, .. }) => Some(e),
 +                _ => None,
 +            },
 +            Node::Local(local) => local.init,
 +            _ => None,
 +        };
 +        return init;
 +    }
 +    None
 +}
index 70d166c4854c484bfe6502f75747c3e77c13c9b0,0000000000000000000000000000000000000000..2f190e594a83197c57e62750423037927cd1b6f8
mode 100644,000000..100644
--- /dev/null
@@@ -1,84 -1,0 +1,76 @@@
- pub(super) fn is_layout_incompatible<'tcx>(
-     cx: &LateContext<'tcx>,
-     from: Ty<'tcx>,
-     to: Ty<'tcx>,
- ) -> bool {
 +use rustc_hir::Expr;
 +use rustc_hir_typeck::{cast, FnCtxt, Inherited};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{cast::CastKind, Ty};
 +use rustc_span::DUMMY_SP;
 +
 +// check if the component types of the transmuted collection and the result have different ABI,
 +// size or alignment
-     use CastKind::{
-         AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast,
-     };
++pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
 +    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 {
- fn check_cast<'tcx>(
-     cx: &LateContext<'tcx>,
-     e: &'tcx Expr<'_>,
-     from_ty: Ty<'tcx>,
-     to_ty: Ty<'tcx>,
- ) -> Option<CastKind> {
++    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.
-         let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, hir_id);
++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.def_id;
 +
 +    Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
-         assert!(!fn_ctxt.errors_reported_since_creation(), "Newly created FnCtxt contained errors");
++        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) = cast::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 6b9de64e24c93875b41de0b5fdd09de632acdcf1,0000000000000000000000000000000000000000..fa567b9b2d243661efa259217572da6005efcc9d
mode 100644,000000..100644
--- /dev/null
@@@ -1,106 -1,0 +1,101 @@@
-                 Applicability::MachineApplicable,
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{path_def_id, qpath_generic_tys};
 +use rustc_errors::Applicability;
 +use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind};
 +use rustc_lint::LateContext;
 +use rustc_span::symbol::sym;
 +
 +use super::RC_BUFFER;
 +
 +pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
++    let app = Applicability::Unspecified;
 +    if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
 +        if let Some(alternate) = match_buffer_type(cx, qpath) {
 +            span_lint_and_sugg(
 +                cx,
 +                RC_BUFFER,
 +                hir_ty.span,
 +                "usage of `Rc<T>` when T is a buffer type",
 +                "try",
 +                format!("Rc<{alternate}>"),
-             let qpath = match &ty.kind {
-                 TyKind::Path(qpath) => qpath,
-                 _ => return false,
-             };
++                app,
 +            );
 +        } else {
 +            let Some(ty) = qpath_generic_tys(qpath).next() else { return false };
 +            let Some(id) = path_def_id(cx, ty) else { return false };
 +            if !cx.tcx.is_diagnostic_item(sym::Vec, id) {
 +                return false;
 +            }
-             let mut applicability = Applicability::MachineApplicable;
++            let TyKind::Path(qpath) = &ty.kind else { return false };
 +            let inner_span = match qpath_generic_tys(qpath).next() {
 +                Some(ty) => ty.span,
 +                None => return false,
 +            };
-                 Applicability::MachineApplicable,
++            let mut applicability = app;
 +            span_lint_and_sugg(
 +                cx,
 +                RC_BUFFER,
 +                hir_ty.span,
 +                "usage of `Rc<T>` when T is a buffer type",
 +                "try",
 +                format!(
 +                    "Rc<[{}]>",
 +                    snippet_with_applicability(cx, inner_span, "..", &mut applicability)
 +                ),
-                 Applicability::MachineApplicable,
++                app,
 +            );
 +            return true;
 +        }
 +    } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
 +        if let Some(alternate) = match_buffer_type(cx, qpath) {
 +            span_lint_and_sugg(
 +                cx,
 +                RC_BUFFER,
 +                hir_ty.span,
 +                "usage of `Arc<T>` when T is a buffer type",
 +                "try",
 +                format!("Arc<{alternate}>"),
-             let qpath = match &ty.kind {
-                 TyKind::Path(qpath) => qpath,
-                 _ => return false,
-             };
++                app,
 +            );
 +        } else if let Some(ty) = qpath_generic_tys(qpath).next() {
 +            let Some(id) = path_def_id(cx, ty) else { return false };
 +            if !cx.tcx.is_diagnostic_item(sym::Vec, id) {
 +                return false;
 +            }
-             let mut applicability = Applicability::MachineApplicable;
++            let TyKind::Path(qpath) = &ty.kind else { return false };
 +            let inner_span = match qpath_generic_tys(qpath).next() {
 +                Some(ty) => ty.span,
 +                None => return false,
 +            };
-                 Applicability::MachineApplicable,
++            let mut applicability = app;
 +            span_lint_and_sugg(
 +                cx,
 +                RC_BUFFER,
 +                hir_ty.span,
 +                "usage of `Arc<T>` when T is a buffer type",
 +                "try",
 +                format!(
 +                    "Arc<[{}]>",
 +                    snippet_with_applicability(cx, inner_span, "..", &mut applicability)
 +                ),
++                app,
 +            );
 +            return true;
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
 +    let ty = qpath_generic_tys(qpath).next()?;
 +    let id = path_def_id(cx, ty)?;
 +    let path = match cx.tcx.get_diagnostic_name(id)? {
 +        sym::String => "str",
 +        sym::OsString => "std::ffi::OsStr",
 +        sym::PathBuf => "std::path::Path",
 +        _ => return None,
 +    };
 +    Some(path)
 +}
index ecb67200539089276fd008fabd402e02c335ae2b,0000000000000000000000000000000000000000..7883353e3fef61ba137464222f5fb621d719db14
mode 100644,000000..100644
--- /dev/null
@@@ -1,104 -1,0 +1,102 @@@
-         let mut applicability = Applicability::MaybeIncorrect;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::{snippet, snippet_with_applicability};
 +use clippy_utils::{path_def_id, qpath_generic_tys};
 +use rustc_errors::Applicability;
 +use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind};
 +use rustc_hir_analysis::hir_ty_to_ty;
 +use rustc_lint::LateContext;
 +use rustc_span::symbol::sym;
 +
 +use super::{utils, REDUNDANT_ALLOCATION};
 +
 +pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
++    let mut applicability = Applicability::MaybeIncorrect;
 +    let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() {
 +        "Box"
 +    } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
 +        "Rc"
 +    } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
 +        "Arc"
 +    } else {
 +        return false;
 +    };
 +
 +    if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
-     let inner_qpath = match &ty.kind {
-         TyKind::Path(inner_qpath) => inner_qpath,
-         _ => return false,
 +        let generic_snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
 +        span_lint_and_then(
 +            cx,
 +            REDUNDANT_ALLOCATION,
 +            hir_ty.span,
 +            &format!("usage of `{outer_sym}<{generic_snippet}>`"),
 +            |diag| {
 +                diag.span_suggestion(hir_ty.span, "try", format!("{generic_snippet}"), applicability);
 +                diag.note(&format!(
 +                    "`{generic_snippet}` is already a pointer, `{outer_sym}<{generic_snippet}>` allocates a pointer on the heap"
 +                ));
 +            },
 +        );
 +        return true;
 +    }
 +
 +    let Some(ty) = qpath_generic_tys(qpath).next() else { return false };
 +    let Some(id) = path_def_id(cx, ty) else { return false };
 +    let (inner_sym, ty) = match cx.tcx.get_diagnostic_name(id) {
 +        Some(sym::Arc) => ("Arc", ty),
 +        Some(sym::Rc) => ("Rc", ty),
 +        _ if Some(id) == cx.tcx.lang_items().owned_box() => ("Box", ty),
 +        _ => return false,
 +    };
 +
-         let mut applicability = Applicability::MaybeIncorrect;
++    let TyKind::Path(inner_qpath) = &ty.kind else {
++        return false
 +    };
 +    let inner_span = match qpath_generic_tys(inner_qpath).next() {
 +        Some(ty) => {
 +            // Reallocation of a fat pointer causes it to become thin. `hir_ty_to_ty` is safe to use
 +            // here because `mod.rs` guarantees this lint is only run on types outside of bodies and
 +            // is not run on locals.
 +            if !hir_ty_to_ty(cx.tcx, ty).is_sized(cx.tcx.at(ty.span), cx.param_env) {
 +                return false;
 +            }
 +            ty.span
 +        },
 +        None => return false,
 +    };
 +    if inner_sym == outer_sym {
 +        let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability);
 +        span_lint_and_then(
 +            cx,
 +            REDUNDANT_ALLOCATION,
 +            hir_ty.span,
 +            &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"),
 +            |diag| {
 +                diag.span_suggestion(
 +                    hir_ty.span,
 +                    "try",
 +                    format!("{outer_sym}<{generic_snippet}>"),
 +                    applicability,
 +                );
 +                diag.note(&format!(
 +                    "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation"
 +                ));
 +            },
 +        );
 +    } else {
 +        let generic_snippet = snippet(cx, inner_span, "..");
 +        span_lint_and_then(
 +            cx,
 +            REDUNDANT_ALLOCATION,
 +            hir_ty.span,
 +            &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"),
 +            |diag| {
 +                diag.note(&format!(
 +                    "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation"
 +                ));
 +                diag.help(&format!(
 +                    "consider using just `{outer_sym}<{generic_snippet}>` or `{inner_sym}<{generic_snippet}>`"
 +                ));
 +            },
 +        );
 +    }
 +    true
 +}
index 57aff5367dd155af70592424eaf95cc1355093d3,0000000000000000000000000000000000000000..1307288623f95f3d99e85f572fcc9df379c7856a
mode 100644,000000..100644
--- /dev/null
@@@ -1,183 -1,0 +1,186 @@@
- use clippy_utils::{get_trait_def_id, paths};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
- use rustc_span::{BytePos, Span};
 +use if_chain::if_chain;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{Closure, Expr, ExprKind, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_middle::ty::{GenericPredicates, PredicateKind, ProjectionPredicate, TraitPredicate};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
-         let ord_preds = get_trait_predicates_for_trait_id(cx, generics, get_trait_def_id(cx, &paths::ORD));
++use rustc_span::{sym, BytePos, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for functions that expect closures of type
 +    /// Fn(...) -> Ord where the implemented closure returns the unit type.
 +    /// The lint also suggests to remove the semi-colon at the end of the statement if present.
 +    ///
 +    /// ### Why is this bad?
 +    /// Likely, returning the unit type is unintentional, and
 +    /// could simply be caused by an extra semi-colon. Since () implements Ord
 +    /// it doesn't cause a compilation error.
 +    /// This is the same reasoning behind the unit_cmp lint.
 +    ///
 +    /// ### Known problems
 +    /// If returning unit is intentional, then there is no
 +    /// way of specifying this without triggering needless_return lint
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut twins = vec!((1, 1), (2, 2));
 +    /// twins.sort_by_key(|x| { x.1; });
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub UNIT_RETURN_EXPECTING_ORD,
 +    correctness,
 +    "fn arguments of type Fn(...) -> Ord returning the unit type ()."
 +}
 +
 +declare_lint_pass!(UnitReturnExpectingOrd => [UNIT_RETURN_EXPECTING_ORD]);
 +
 +fn get_trait_predicates_for_trait_id<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    generics: GenericPredicates<'tcx>,
 +    trait_id: Option<DefId>,
 +) -> Vec<TraitPredicate<'tcx>> {
 +    let mut preds = Vec::new();
 +    for (pred, _) in generics.predicates {
 +        if_chain! {
 +            if let PredicateKind::Trait(poly_trait_pred) = pred.kind().skip_binder();
 +            let trait_pred = cx.tcx.erase_late_bound_regions(pred.kind().rebind(poly_trait_pred));
 +            if let Some(trait_def_id) = trait_id;
 +            if trait_def_id == trait_pred.trait_ref.def_id;
 +            then {
 +                preds.push(trait_pred);
 +            }
 +        }
 +    }
 +    preds
 +}
 +
 +fn get_projection_pred<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    generics: GenericPredicates<'tcx>,
 +    trait_pred: TraitPredicate<'tcx>,
 +) -> Option<ProjectionPredicate<'tcx>> {
 +    generics.predicates.iter().find_map(|(proj_pred, _)| {
 +        if let ty::PredicateKind::Projection(pred) = proj_pred.kind().skip_binder() {
 +            let projection_pred = cx.tcx.erase_late_bound_regions(proj_pred.kind().rebind(pred));
 +            if projection_pred.projection_ty.substs == trait_pred.trait_ref.substs {
 +                return Some(projection_pred);
 +            }
 +        }
 +        None
 +    })
 +}
 +
 +fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Vec<(usize, String)> {
 +    let mut args_to_check = Vec::new();
 +    if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
 +        let fn_sig = cx.tcx.fn_sig(def_id);
 +        let generics = cx.tcx.predicates_of(def_id);
 +        let fn_mut_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().fn_mut_trait());
-                             if ord_preds.iter().any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) {
++        let ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.get_diagnostic_item(sym::Ord));
 +        let partial_ord_preds =
 +            get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().partial_ord_trait());
 +        // Trying to call erase_late_bound_regions on fn_sig.inputs() gives the following error
 +        // The trait `rustc::ty::TypeFoldable<'_>` is not implemented for
 +        // `&[rustc_middle::ty::Ty<'_>]`
 +        let inputs_output = cx.tcx.erase_late_bound_regions(fn_sig.inputs_and_output());
 +        inputs_output
 +            .iter()
 +            .rev()
 +            .skip(1)
 +            .rev()
 +            .enumerate()
 +            .for_each(|(i, inp)| {
 +                for trait_pred in &fn_mut_preds {
 +                    if_chain! {
 +                        if trait_pred.self_ty() == inp;
 +                        if let Some(return_ty_pred) = get_projection_pred(cx, generics, *trait_pred);
 +                        then {
-                             } else if partial_ord_preds.iter().any(|pord| {
-                                 pord.self_ty() == return_ty_pred.term.ty().unwrap()
-                             }) {
++                            if ord_preds
++                                .iter()
++                                .any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty())
++                            {
 +                                args_to_check.push((i, "Ord".to_string()));
++                            } else if partial_ord_preds
++                                .iter()
++                                .any(|pord| pord.self_ty() == return_ty_pred.term.ty().unwrap())
++                            {
 +                                args_to_check.push((i, "PartialOrd".to_string()));
 +                            }
 +                        }
 +                    }
 +                }
 +            });
 +    }
 +    args_to_check
 +}
 +
 +fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Span, Option<Span>)> {
 +    if_chain! {
 +        if let ExprKind::Closure(&Closure { body, fn_decl_span, .. }) = arg.kind;
 +        if let ty::Closure(_def_id, substs) = &cx.typeck_results().node_type(arg.hir_id).kind();
 +        let ret_ty = substs.as_closure().sig().output();
 +        let ty = cx.tcx.erase_late_bound_regions(ret_ty);
 +        if ty.is_unit();
 +        then {
 +            let body = cx.tcx.hir().body(body);
 +            if_chain! {
 +                if let ExprKind::Block(block, _) = body.value.kind;
 +                if block.expr.is_none();
 +                if let Some(stmt) = block.stmts.last();
 +                if let StmtKind::Semi(_) = stmt.kind;
 +                then {
 +                    let data = stmt.span.data();
 +                    // Make a span out of the semicolon for the help message
 +                    Some((fn_decl_span, Some(data.with_lo(data.hi-BytePos(1)))))
 +                } else {
 +                    Some((fn_decl_span, None))
 +                }
 +            }
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
 +        if let ExprKind::MethodCall(_, receiver, args, _) = expr.kind {
 +            let arg_indices = get_args_to_check(cx, expr);
 +            let args = std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>();
 +            for (i, trait_name) in arg_indices {
 +                if i < args.len() {
 +                    match check_arg(cx, args[i]) {
 +                        Some((span, None)) => {
 +                            span_lint(
 +                                cx,
 +                                UNIT_RETURN_EXPECTING_ORD,
 +                                span,
 +                                &format!(
 +                                    "this closure returns \
 +                                   the unit type which also implements {trait_name}"
 +                                ),
 +                            );
 +                        },
 +                        Some((span, Some(last_semi))) => {
 +                            span_lint_and_help(
 +                                cx,
 +                                UNIT_RETURN_EXPECTING_ORD,
 +                                span,
 +                                &format!(
 +                                    "this closure returns \
 +                                   the unit type which also implements {trait_name}"
 +                                ),
 +                                Some(last_semi),
 +                                "probably caused by this trailing semicolon",
 +                            );
 +                        },
 +                        None => {},
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
index fb73c386640b49babd971298c21445d4286f9225,0000000000000000000000000000000000000000..b305dae76084c3e698263f59701c83698ca470db
mode 100644,000000..100644
--- /dev/null
@@@ -1,428 -1,0 +1,427 @@@
-             let alternatives = match &mut p.kind {
-                 Or(ps) => ps,
-                 _ => return,
 +#![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;
 +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?
 +    /// 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, 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, 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, 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, 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::BindingAnnotation;
 +            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, Mutability::Not) if matches!(p.kind, Ident(BindingAnnotation::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 Or(alternatives) = &mut p.kind else {
++                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),
 +        ),
 +        // 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 8bcdff66331d162c44d06971af7f9774f09698a3,0000000000000000000000000000000000000000..92053cec59fc8da8254403fadbbae6099812d4b2
mode 100644,000000..100644
--- /dev/null
@@@ -1,171 -1,0 +1,170 @@@
-         let expr = match s.kind {
-             hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr,
-             _ => return,
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 +use clippy_utils::{is_trait_method, is_try, match_trait_method, paths};
 +use rustc_hir as hir;
 +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 unused written/read amount.
 +    ///
 +    /// ### Why is this bad?
 +    /// `io::Write::write(_vectored)` and
 +    /// `io::Read::read(_vectored)` are not guaranteed to
 +    /// process the entire buffer. They return how many bytes were processed, which
 +    /// might be smaller
 +    /// than a given buffer's length. If you don't need to deal with
 +    /// partial-write/read, use
 +    /// `write_all`/`read_exact` instead.
 +    ///
 +    /// When working with asynchronous code (either with the `futures`
 +    /// crate or with `tokio`), a similar issue exists for
 +    /// `AsyncWriteExt::write()` and `AsyncReadExt::read()` : these
 +    /// functions are also not guaranteed to process the entire
 +    /// buffer.  Your code should either handle partial-writes/reads, or
 +    /// call the `write_all`/`read_exact` methods on those traits instead.
 +    ///
 +    /// ### Known problems
 +    /// Detects only common patterns.
 +    ///
 +    /// ### Examples
 +    /// ```rust,ignore
 +    /// use std::io;
 +    /// fn foo<W: io::Write>(w: &mut W) -> io::Result<()> {
 +    ///     // must be `w.write_all(b"foo")?;`
 +    ///     w.write(b"foo")?;
 +    ///     Ok(())
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNUSED_IO_AMOUNT,
 +    correctness,
 +    "unused written/read amount"
 +}
 +
 +declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
 +    fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
++        let (hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr)) = s.kind else {
++            return
 +        };
 +
 +        match expr.kind {
 +            hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => {
 +                if let hir::ExprKind::Call(func, [ref arg_0, ..]) = res.kind {
 +                    if matches!(
 +                        func.kind,
 +                        hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..))
 +                    ) {
 +                        check_map_error(cx, arg_0, expr);
 +                    }
 +                } else {
 +                    check_map_error(cx, res, expr);
 +                }
 +            },
 +            hir::ExprKind::MethodCall(path, arg_0, ..) => match path.ident.as_str() {
 +                "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => {
 +                    check_map_error(cx, arg_0, expr);
 +                },
 +                _ => (),
 +            },
 +            _ => (),
 +        }
 +    }
 +}
 +
 +/// If `expr` is an (e).await, return the inner expression "e" that's being
 +/// waited on.  Otherwise return None.
 +fn try_remove_await<'a>(expr: &'a hir::Expr<'a>) -> Option<&hir::Expr<'a>> {
 +    if let hir::ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind {
 +        if let hir::ExprKind::Call(func, [ref arg_0, ..]) = expr.kind {
 +            if matches!(
 +                func.kind,
 +                hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..))
 +            ) {
 +                return Some(arg_0);
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
 +    let mut call = call;
 +    while let hir::ExprKind::MethodCall(path, receiver, ..) = call.kind {
 +        if matches!(path.ident.as_str(), "or" | "or_else" | "ok") {
 +            call = receiver;
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    if let Some(call) = try_remove_await(call) {
 +        check_method_call(cx, call, expr, true);
 +    } else {
 +        check_method_call(cx, call, expr, false);
 +    }
 +}
 +
 +fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>, is_await: bool) {
 +    if let hir::ExprKind::MethodCall(path, ..) = call.kind {
 +        let symbol = path.ident.as_str();
 +        let read_trait = if is_await {
 +            match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT)
 +                || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT)
 +        } else {
 +            is_trait_method(cx, call, sym::IoRead)
 +        };
 +        let write_trait = if is_await {
 +            match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT)
 +                || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT)
 +        } else {
 +            is_trait_method(cx, call, sym::IoWrite)
 +        };
 +
 +        match (read_trait, write_trait, symbol, is_await) {
 +            (true, _, "read", false) => span_lint_and_help(
 +                cx,
 +                UNUSED_IO_AMOUNT,
 +                expr.span,
 +                "read amount is not handled",
 +                None,
 +                "use `Read::read_exact` instead, or handle partial reads",
 +            ),
 +            (true, _, "read", true) => span_lint_and_help(
 +                cx,
 +                UNUSED_IO_AMOUNT,
 +                expr.span,
 +                "read amount is not handled",
 +                None,
 +                "use `AsyncReadExt::read_exact` instead, or handle partial reads",
 +            ),
 +            (true, _, "read_vectored", _) => {
 +                span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled");
 +            },
 +            (_, true, "write", false) => span_lint_and_help(
 +                cx,
 +                UNUSED_IO_AMOUNT,
 +                expr.span,
 +                "written amount is not handled",
 +                None,
 +                "use `Write::write_all` instead, or handle partial writes",
 +            ),
 +            (_, true, "write", true) => span_lint_and_help(
 +                cx,
 +                UNUSED_IO_AMOUNT,
 +                expr.span,
 +                "written amount is not handled",
 +                None,
 +                "use `AsyncWriteExt::write_all` instead, or handle partial writes",
 +            ),
 +            (_, true, "write_vectored", _) => {
 +                span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled");
 +            },
 +            _ => (),
 +        }
 +    }
 +}
index a82643a59f97bb11bc1c17c5ba62157073a5c753,0000000000000000000000000000000000000000..1f69db1cbca40e4145a26752a8294557684e5b14
mode 100644,000000..100644
--- /dev/null
@@@ -1,188 -1,0 +1,187 @@@
-                 let e = match arms[0].body.kind {
-                     ExprKind::Ret(Some(e)) | ExprKind::Break(_, Some(e)) => e,
-                     _ => return,
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
 +use clippy_utils::source::{snippet, snippet_with_macro_callsite};
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts};
 +use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, HirId, LangItem, MatchSource};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls
 +    /// which uselessly convert to the same type.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // format!() returns a `String`
 +    /// let s: String = format!("hello").into();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let s: String = format!("hello");
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub USELESS_CONVERSION,
 +    complexity,
 +    "calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` which perform useless conversions to the same type"
 +}
 +
 +#[derive(Default)]
 +pub struct UselessConversion {
 +    try_desugar_arm: Vec<HirId>,
 +}
 +
 +impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]);
 +
 +#[expect(clippy::too_many_lines)]
 +impl<'tcx> LateLintPass<'tcx> for UselessConversion {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if e.span.from_expansion() {
 +            return;
 +        }
 +
 +        if Some(&e.hir_id) == self.try_desugar_arm.last() {
 +            return;
 +        }
 +
 +        match e.kind {
 +            ExprKind::Match(_, arms, MatchSource::TryDesugar) => {
++                let (ExprKind::Ret(Some(e)) | ExprKind::Break(_, Some(e))) = arms[0].body.kind else {
++                     return
 +                };
 +                if let ExprKind::Call(_, [arg, ..]) = e.kind {
 +                    self.try_desugar_arm.push(arg.hir_id);
 +                }
 +            },
 +
 +            ExprKind::MethodCall(name, recv, ..) => {
 +                if is_trait_method(cx, e, sym::Into) && name.ident.as_str() == "into" {
 +                    let a = cx.typeck_results().expr_ty(e);
 +                    let b = cx.typeck_results().expr_ty(recv);
 +                    if same_type_and_consts(a, b) {
 +                        let sugg = snippet_with_macro_callsite(cx, recv.span, "<expr>").to_string();
 +                        span_lint_and_sugg(
 +                            cx,
 +                            USELESS_CONVERSION,
 +                            e.span,
 +                            &format!("useless conversion to the same type: `{b}`"),
 +                            "consider removing `.into()`",
 +                            sugg,
 +                            Applicability::MachineApplicable, // snippet
 +                        );
 +                    }
 +                }
 +                if is_trait_method(cx, e, sym::IntoIterator) && name.ident.name == sym::into_iter {
 +                    if let Some(parent_expr) = get_parent_expr(cx, e) {
 +                        if let ExprKind::MethodCall(parent_name, ..) = parent_expr.kind {
 +                            if parent_name.ident.name != sym::into_iter {
 +                                return;
 +                            }
 +                        }
 +                    }
 +                    let a = cx.typeck_results().expr_ty(e);
 +                    let b = cx.typeck_results().expr_ty(recv);
 +                    if same_type_and_consts(a, b) {
 +                        let sugg = snippet(cx, recv.span, "<expr>").into_owned();
 +                        span_lint_and_sugg(
 +                            cx,
 +                            USELESS_CONVERSION,
 +                            e.span,
 +                            &format!("useless conversion to the same type: `{b}`"),
 +                            "consider removing `.into_iter()`",
 +                            sugg,
 +                            Applicability::MachineApplicable, // snippet
 +                        );
 +                    }
 +                }
 +                if_chain! {
 +                    if is_trait_method(cx, e, sym::TryInto) && name.ident.name == sym::try_into;
 +                    let a = cx.typeck_results().expr_ty(e);
 +                    let b = cx.typeck_results().expr_ty(recv);
 +                    if is_type_diagnostic_item(cx, a, sym::Result);
 +                    if let ty::Adt(_, substs) = a.kind();
 +                    if let Some(a_type) = substs.types().next();
 +                    if same_type_and_consts(a_type, b);
 +
 +                    then {
 +                        span_lint_and_help(
 +                            cx,
 +                            USELESS_CONVERSION,
 +                            e.span,
 +                            &format!("useless conversion to the same type: `{b}`"),
 +                            None,
 +                            "consider removing `.try_into()`",
 +                        );
 +                    }
 +                }
 +            },
 +
 +            ExprKind::Call(path, [arg]) => {
 +                if_chain! {
 +                    if let ExprKind::Path(ref qpath) = path.kind;
 +                    if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
 +                    then {
 +                        let a = cx.typeck_results().expr_ty(e);
 +                        let b = cx.typeck_results().expr_ty(arg);
 +                        if_chain! {
 +                            if match_def_path(cx, def_id, &paths::TRY_FROM);
 +                            if is_type_diagnostic_item(cx, a, sym::Result);
 +                            if let ty::Adt(_, substs) = a.kind();
 +                            if let Some(a_type) = substs.types().next();
 +                            if same_type_and_consts(a_type, b);
 +
 +                            then {
 +                                let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from"));
 +                                span_lint_and_help(
 +                                    cx,
 +                                    USELESS_CONVERSION,
 +                                    e.span,
 +                                    &format!("useless conversion to the same type: `{b}`"),
 +                                    None,
 +                                    &hint,
 +                                );
 +                            }
 +                        }
 +
 +                        if_chain! {
 +                            if cx.tcx.lang_items().require(LangItem::FromFrom).ok() == Some(def_id);
 +                            if same_type_and_consts(a, b);
 +
 +                            then {
 +                                let sugg = Sugg::hir_with_macro_callsite(cx, arg, "<expr>").maybe_par();
 +                                let sugg_msg =
 +                                    format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));
 +                                span_lint_and_sugg(
 +                                    cx,
 +                                    USELESS_CONVERSION,
 +                                    e.span,
 +                                    &format!("useless conversion to the same type: `{b}`"),
 +                                    &sugg_msg,
 +                                    sugg.to_string(),
 +                                    Applicability::MachineApplicable, // snippet
 +                                );
 +                            }
 +                        }
 +                    }
 +                }
 +            },
 +
 +            _ => {},
 +        }
 +    }
 +
 +    fn check_expr_post(&mut self, _: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if Some(&e.hir_id) == self.try_desugar_arm.last() {
 +            self.try_desugar_arm.pop();
 +        }
 +    }
 +}
index e069de8cb5c7e5148cba5f9789f9e98809c20dde,0000000000000000000000000000000000000000..0c052d86eda409dc1eb4fa3103f8e6b4ea4fdbae
mode 100644,000000..100644
--- /dev/null
@@@ -1,750 -1,0 +1,757 @@@
-     /// 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
-     ///     }
 +//! 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, BindingAnnotation, Closure, 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::cell::Cell;
 +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
- fn prelude() {
-     println!("if_chain! {{");
- }
- fn done() {
-     println!("    then {{");
-     println!("        // report your lint here");
-     println!("    }}");
-     println!("}}");
++    /// if ExprKind::If(ref cond, ref then, None) = item.kind
++    ///     && let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind
++    ///     && let ExprKind::Path(ref path) = left.kind
++    ///     && let ExprKind::Lit(ref lit) = right.kind
++    ///     && let LitKind::Int(42, _) = lit.node
++    /// {
++    ///     // 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,
 +        }
 +    };
 +}
 +
-         prelude();
++/// Print a condition of a let chain, `chain!(self, "let Some(x) = y")` will print
++/// `if let Some(x) = y` on the first call and `    && let Some(x) = y` thereafter
++macro_rules! chain {
++    ($self:ident, $($t:tt)*) => {
++        if $self.first.take() {
++            println!("if {}", format_args!($($t)*));
++        } else {
++            println!("    && {}", format_args!($($t)*));
++        }
++    }
 +}
 +
 +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.expect_owner().def_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) {
-         done();
 +        f(&PrintVisitor::new(cx));
-     ids: std::cell::Cell<FxHashMap<&'static str, u32>>,
++        println!("{{");
++        println!("    // report your lint here");
++        println!("}}");
 +    }
 +}
 +
 +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::default(),
++    ids: Cell<FxHashMap<&'static str, u32>>,
++    /// Currently at the first condition in the if chain
++    first: Cell<bool>,
 +}
 +
 +#[allow(clippy::unused_self)]
 +impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            cx,
-             None => out!("if {option}.is_none();"),
++            ids: Cell::default(),
++            first: Cell::new(true),
 +        }
 +    }
 +
 +    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 {
-                 out!("if let Some({value}) = {option};");
++            None => chain!(self, "{option}.is_none()"),
 +            Some(value) => {
 +                let value = &self.bind(name, value);
-             out!("if {slice}.is_empty();");
++                chain!(self, "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}.len() == {};", slice.value.len());
++            chain!(self, "{slice}.is_empty()");
 +        } else {
-         out!("if {ident}.as_str() == {:?};", ident.value.as_str());
++            chain!(self, "{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 {symbol}.as_str() == {:?};", symbol.value.as_str());
++        chain!(self, "{ident}.as_str() == {:?}", ident.value.as_str());
 +    }
 +
 +    fn symbol(&self, symbol: &Binding<Symbol>) {
-             out!("if matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _));");
++        chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str());
 +    }
 +
 +    fn qpath(&self, qpath: &Binding<&QPath<'_>>) {
 +        if let QPath::LangItem(lang_item, ..) = *qpath.value {
-             out!("if match_qpath({qpath}, &[{}]);", path_to_string(qpath.value));
++            chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))");
 +        } else {
-         let kind = |kind| out!("if let LitKind::{kind} = {lit}.node;");
++            chain!(self, "match_qpath({qpath}, &[{}])", path_to_string(qpath.value));
 +        }
 +    }
 +
 +    fn lit(&self, lit: &Binding<&Lit>) {
-                 out!("if let [{:?}] = **{vec};", vec.value);
++        let kind = |kind| chain!(self, "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 => kind!("Err"),
 +            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})");
-             None => out!("if {arm}.guard.is_none();"),
++                chain!(self, "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 {
-                 out!("if let Some(Guard::If({expr})) = {arm}.guard;");
++            None => chain!(self, "{arm}.guard.is_none()"),
 +            Some(hir::Guard::If(expr)) => {
 +                bind!(self, expr);
-                 out!("if let Some(Guard::IfLet({let_expr}) = {arm}.guard;");
++                chain!(self, "let Some(Guard::If({expr})) = {arm}.guard");
 +                self.expr(expr);
 +            },
 +            Some(hir::Guard::IfLet(let_expr)) => {
 +                bind!(self, let_expr);
-             out!(
-                 "if let Some(higher::While {{ condition: {condition}, body: {body} }}) \
-                 = higher::While::hir({expr});"
++                chain!(self, "let Some(Guard::IfLet({let_expr}) = {arm}.guard");
 +                self.pat(field!(let_expr.pat));
 +                self.expr(field!(let_expr.init));
 +            },
 +        }
 +        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::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \
-                 = higher::WhileLet::hir({expr});"
++            chain!(
++                self,
++                "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::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \
-                 = higher::ForLoop::hir({expr});"
++            chain!(
++                self,
++                "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);
-         let kind = |kind| out!("if let ExprKind::{kind} = {expr}.kind;");
++            chain!(
++                self,
++                "let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \
++                = higher::ForLoop::hir({expr})"
 +            );
 +            self.pat(pat);
 +            self.expr(arg);
 +            self.expr(body);
 +            return;
 +        }
 +
-                     out!("if let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind;");
++        let kind = |kind| chain!(self, "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 BinOpKind::{:?} == {op}.node;", op.value.node);
++                    chain!(self, "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, receiver, args, _) => {
 +                bind!(self, method_name, receiver, args);
 +                kind!("MethodCall({method_name}, {receiver}, {args}, _)");
 +                self.ident(field!(method_name.ident));
 +                self.expr(receiver);
 +                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 let TyKind::Path(ref {qpath}) = {cast_ty}.kind;");
++                chain!(self, "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 {ret_ty} = {fn_decl}.output;");
++                    chain!(self, "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(&Closure {
 +                capture_clause,
 +                fn_decl,
 +                body: 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_clause:?}, {fn_decl}, {body_id}, _, {movability})");
-                 out!("if BinOpKind::{:?} == {op}.node;", op.value.node);
++                chain!(self, "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})");
-                     ArrayLen::Infer(..) => out!("if let ArrayLen::Infer(..) = length;"),
++                chain!(self, "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 {
-                         out!("if let ArrayLen::Body({anon_const}) = {length};");
++                    ArrayLen::Infer(..) => chain!(self, "let ArrayLen::Infer(..) = length"),
 +                    ArrayLen::Body(anon_const) => {
 +                        bind!(self, anon_const);
-         out!("let {expr} = &cx.tcx.hir().body({body_id}).value;");
++                        chain!(self, "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);
-         let kind = |kind| out!("if let PatKind::{kind} = {pat}.kind;");
++        chain!(self, "{expr} = &cx.tcx.hir().body({body_id}).value");
 +        self.expr(expr);
 +    }
 +
 +    fn pat(&self, pat: &Binding<&hir::Pat<'_>>) {
-         let kind = |kind| out!("if let StmtKind::{kind} = {stmt}.kind;");
++        let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind");
 +        macro_rules! kind {
 +            ($($t:tt)*) => (kind(format_args!($($t)*)));
 +        }
 +
 +        match pat.value.kind {
 +            PatKind::Wild => kind!("Wild"),
 +            PatKind::Binding(ann, _, name, sub) => {
 +                bind!(self, name);
 +                opt_bind!(self, sub);
 +                let ann = match ann {
 +                    BindingAnnotation::NONE => "NONE",
 +                    BindingAnnotation::REF => "REF",
 +                    BindingAnnotation::MUT => "MUT",
 +                    BindingAnnotation::REF_MUT => "REF_MUT",
 +                };
 +                kind!("Binding(BindingAnnotation::{ann}, _, {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| chain!(self, "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 85bcbc7ad236966d9ec406e6caadc69432c3b1fb,0000000000000000000000000000000000000000..71f6c9909ddda5881d6d9e5f181e20154516e879
mode 100644,000000..100644
--- /dev/null
@@@ -1,1566 -1,0 +1,12 @@@
- use crate::utils::internal_lints::metadata_collector::is_deprecated_lint;
- use clippy_utils::consts::{constant_simple, Constant};
- use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
- use clippy_utils::macros::root_macro_call_first_node;
- use clippy_utils::source::{snippet, snippet_with_applicability};
- 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_any_def_paths,
-     match_def_path, method_calls, paths, peel_blocks_with_stmt, peel_hir_expr_refs, 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, Namespace, Res};
- use rustc_hir::def_id::DefId;
- use rustc_hir::hir_id::CRATE_HIR_ID;
- use rustc_hir::intravisit::Visitor;
- use rustc_hir::{
-     BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind,
-     TyKind, UnOp,
- };
- use rustc_hir_analysis::hir_ty_to_ty;
- use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
- use rustc_middle::hir::nested_filter;
- use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc};
- use rustc_middle::ty::{
-     self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, AssocKind, DefIdTree, FloatTy, Ty,
- };
- use rustc_semver::RustcVersion;
- use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
- use rustc_span::source_map::Spanned;
- use rustc_span::symbol::{Ident, Symbol};
- use rustc_span::{sym, BytePos, Span};
- use std::borrow::{Borrow, Cow};
- use std::str;
- #[cfg(feature = "internal")]
++pub mod clippy_lints_internal;
++pub mod collapsible_calls;
++pub mod compiler_lint_functions;
++pub mod if_chain_style;
++pub mod interning_defined_symbol;
++pub mod invalid_paths;
++pub mod lint_without_lint_pass;
 +pub mod metadata_collector;
- declare_clippy_lint! {
-     /// ### What it does
-     /// Checks for various things we like to keep tidy in clippy.
-     ///
-     /// ### Why is this bad?
-     /// We like to pretend we're an example of tidy code.
-     ///
-     /// ### Example
-     /// Wrong ordering of the util::paths constants.
-     pub CLIPPY_LINTS_INTERNAL,
-     internal,
-     "various things that will negatively affect your clippy experience"
- }
- declare_clippy_lint! {
-     /// ### What it does
-     /// Ensures every lint is associated to a `LintPass`.
-     ///
-     /// ### Why is this bad?
-     /// The compiler only knows lints via a `LintPass`. Without
-     /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
-     /// know the name of the lint.
-     ///
-     /// ### Known problems
-     /// Only checks for lints associated using the
-     /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros.
-     ///
-     /// ### Example
-     /// ```rust,ignore
-     /// declare_lint! { pub LINT_1, ... }
-     /// declare_lint! { pub LINT_2, ... }
-     /// declare_lint! { pub FORGOTTEN_LINT, ... }
-     /// // ...
-     /// declare_lint_pass!(Pass => [LINT_1, LINT_2]);
-     /// // missing FORGOTTEN_LINT
-     /// ```
-     pub LINT_WITHOUT_LINT_PASS,
-     internal,
-     "declaring a lint without associating it in a LintPass"
- }
- declare_clippy_lint! {
-     /// ### What it does
-     /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*`
-     /// variant of the function.
-     ///
-     /// ### Why is this bad?
-     /// The `utils::*` variants also add a link to the Clippy documentation to the
-     /// warning/error messages.
-     ///
-     /// ### Example
-     /// ```rust,ignore
-     /// cx.span_lint(LINT_NAME, "message");
-     /// ```
-     ///
-     /// Use instead:
-     /// ```rust,ignore
-     /// utils::span_lint(cx, LINT_NAME, "message");
-     /// ```
-     pub COMPILER_LINT_FUNCTIONS,
-     internal,
-     "usage of the lint functions of the compiler instead of the utils::* variant"
- }
- declare_clippy_lint! {
-     /// ### What it does
-     /// Checks for calls to `cx.outer().expn_data()` and suggests to use
-     /// the `cx.outer_expn_data()`
-     ///
-     /// ### Why is this bad?
-     /// `cx.outer_expn_data()` is faster and more concise.
-     ///
-     /// ### Example
-     /// ```rust,ignore
-     /// expr.span.ctxt().outer().expn_data()
-     /// ```
-     ///
-     /// Use instead:
-     /// ```rust,ignore
-     /// expr.span.ctxt().outer_expn_data()
-     /// ```
-     pub OUTER_EXPN_EXPN_DATA,
-     internal,
-     "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`"
- }
- declare_clippy_lint! {
-     /// ### What it does
-     /// Not an actual lint. This lint is only meant for testing our customized internal compiler
-     /// error message by calling `panic`.
-     ///
-     /// ### Why is this bad?
-     /// ICE in large quantities can damage your teeth
-     ///
-     /// ### Example
-     /// ```rust,ignore
-     /// 🍦🍦🍦🍦🍦
-     /// ```
-     pub PRODUCE_ICE,
-     internal,
-     "this message should not appear anywhere as we ICE before and don't emit the lint"
- }
- declare_clippy_lint! {
-     /// ### What it does
-     /// Checks for cases of an auto-generated lint without an updated description,
-     /// i.e. `default lint description`.
-     ///
-     /// ### Why is this bad?
-     /// Indicates that the lint is not finished.
-     ///
-     /// ### Example
-     /// ```rust,ignore
-     /// declare_lint! { pub COOL_LINT, nursery, "default lint description" }
-     /// ```
-     ///
-     /// Use instead:
-     /// ```rust,ignore
-     /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" }
-     /// ```
-     pub DEFAULT_LINT,
-     internal,
-     "found 'default lint description' in a lint declaration"
- }
- declare_clippy_lint! {
-     /// ### What it does
-     /// Lints `span_lint_and_then` function calls, where the
-     /// closure argument has only one statement and that statement is a method
-     /// call to `span_suggestion`, `span_help`, `span_note` (using the same
-     /// span), `help` or `note`.
-     ///
-     /// These usages of `span_lint_and_then` should be replaced with one of the
-     /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or
-     /// `span_lint_and_note`.
-     ///
-     /// ### Why is this bad?
-     /// Using the wrapper `span_lint_and_*` functions, is more
-     /// convenient, readable and less error prone.
-     ///
-     /// ### Example
-     /// ```rust,ignore
-     /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
-     ///     diag.span_suggestion(
-     ///         expr.span,
-     ///         help_msg,
-     ///         sugg.to_string(),
-     ///         Applicability::MachineApplicable,
-     ///     );
-     /// });
-     /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
-     ///     diag.span_help(expr.span, help_msg);
-     /// });
-     /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
-     ///     diag.help(help_msg);
-     /// });
-     /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
-     ///     diag.span_note(expr.span, note_msg);
-     /// });
-     /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
-     ///     diag.note(note_msg);
-     /// });
-     /// ```
-     ///
-     /// Use instead:
-     /// ```rust,ignore
-     /// span_lint_and_sugg(
-     ///     cx,
-     ///     TEST_LINT,
-     ///     expr.span,
-     ///     lint_msg,
-     ///     help_msg,
-     ///     sugg.to_string(),
-     ///     Applicability::MachineApplicable,
-     /// );
-     /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg);
-     /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg);
-     /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg);
-     /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg);
-     /// ```
-     pub COLLAPSIBLE_SPAN_LINT_CALLS,
-     internal,
-     "found collapsible `span_lint_and_then` calls"
- }
- declare_clippy_lint! {
-     /// ### What it does
-     /// Checks for usages of def paths when a diagnostic item or a `LangItem` could be used.
-     ///
-     /// ### Why is this bad?
-     /// The path for an item is subject to change and is less efficient to look up than a
-     /// diagnostic item or a `LangItem`.
-     ///
-     /// ### Example
-     /// ```rust,ignore
-     /// utils::match_type(cx, ty, &paths::VEC)
-     /// ```
-     ///
-     /// Use instead:
-     /// ```rust,ignore
-     /// utils::is_type_diagnostic_item(cx, ty, sym::Vec)
-     /// ```
-     pub UNNECESSARY_DEF_PATH,
-     internal,
-     "using a def path when a diagnostic item or a `LangItem` is available"
- }
- declare_clippy_lint! {
-     /// ### What it does
-     /// Checks the paths module for invalid paths.
-     ///
-     /// ### Why is this bad?
-     /// It indicates a bug in the code.
-     ///
-     /// ### Example
-     /// None.
-     pub INVALID_PATHS,
-     internal,
-     "invalid path"
- }
- declare_clippy_lint! {
-     /// ### What it does
-     /// Checks for interning symbols that have already been pre-interned and defined as constants.
-     ///
-     /// ### Why is this bad?
-     /// It's faster and easier to use the symbol constant.
-     ///
-     /// ### Example
-     /// ```rust,ignore
-     /// let _ = sym!(f32);
-     /// ```
-     ///
-     /// Use instead:
-     /// ```rust,ignore
-     /// let _ = sym::f32;
-     /// ```
-     pub INTERNING_DEFINED_SYMBOL,
-     internal,
-     "interning a symbol that is pre-interned and defined as a constant"
- }
- declare_clippy_lint! {
-     /// ### What it does
-     /// Checks for unnecessary conversion from Symbol to a string.
-     ///
-     /// ### Why is this bad?
-     /// It's faster use symbols directly instead of strings.
-     ///
-     /// ### Example
-     /// ```rust,ignore
-     /// symbol.as_str() == "clippy";
-     /// ```
-     ///
-     /// Use instead:
-     /// ```rust,ignore
-     /// symbol == sym::clippy;
-     /// ```
-     pub UNNECESSARY_SYMBOL_STR,
-     internal,
-     "unnecessary conversion between Symbol and string"
- }
- declare_clippy_lint! {
-     /// Finds unidiomatic usage of `if_chain!`
-     pub IF_CHAIN_STYLE,
-     internal,
-     "non-idiomatic `if_chain!` usage"
- }
- declare_clippy_lint! {
-     /// ### What it does
-     /// Checks for invalid `clippy::version` attributes.
-     ///
-     /// Valid values are:
-     /// * "pre 1.29.0"
-     /// * any valid semantic version
-     pub INVALID_CLIPPY_VERSION_ATTRIBUTE,
-     internal,
-     "found an invalid `clippy::version` attribute"
- }
- declare_clippy_lint! {
-     /// ### What it does
-     /// Checks for declared clippy lints without the `clippy::version` attribute.
-     ///
-     pub MISSING_CLIPPY_VERSION_ATTRIBUTE,
-     internal,
-     "found clippy lint without `clippy::version` attribute"
- }
- declare_clippy_lint! {
-     /// ### What it does
-     /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV.
-     ///
-     pub MISSING_MSRV_ATTR_IMPL,
-     internal,
-     "checking if all necessary steps were taken when adding a MSRV to a lint"
- }
- declare_clippy_lint! {
-     /// ### What it does
-     /// Checks for cases of an auto-generated deprecated lint without an updated reason,
-     /// i.e. `"default deprecation note"`.
-     ///
-     /// ### Why is this bad?
-     /// Indicates that the documentation is incomplete.
-     ///
-     /// ### Example
-     /// ```rust,ignore
-     /// declare_deprecated_lint! {
-     ///     /// ### What it does
-     ///     /// Nothing. This lint has been deprecated.
-     ///     ///
-     ///     /// ### Deprecation reason
-     ///     /// TODO
-     ///     #[clippy::version = "1.63.0"]
-     ///     pub COOL_LINT,
-     ///     "default deprecation note"
-     /// }
-     /// ```
-     ///
-     /// Use instead:
-     /// ```rust,ignore
-     /// declare_deprecated_lint! {
-     ///     /// ### What it does
-     ///     /// Nothing. This lint has been deprecated.
-     ///     ///
-     ///     /// ### Deprecation reason
-     ///     /// This lint has been replaced by `cooler_lint`
-     ///     #[clippy::version = "1.63.0"]
-     ///     pub COOL_LINT,
-     ///     "this lint has been replaced by `cooler_lint`"
-     /// }
-     /// ```
-     pub DEFAULT_DEPRECATION_REASON,
-     internal,
-     "found 'default deprecation note' in a deprecated lint declaration"
- }
- declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
- impl EarlyLintPass for ClippyLintsInternal {
-     fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
-         if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") {
-             if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind {
-                 if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") {
-                     if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind {
-                         let mut last_name: Option<&str> = None;
-                         for item in items {
-                             let name = item.ident.as_str();
-                             if let Some(last_name) = last_name {
-                                 if *last_name > *name {
-                                     span_lint(
-                                         cx,
-                                         CLIPPY_LINTS_INTERNAL,
-                                         item.span,
-                                         "this constant should be before the previous constant due to lexical \
-                                          ordering",
-                                     );
-                                 }
-                             }
-                             last_name = Some(name);
-                         }
-                     }
-                 }
-             }
-         }
-     }
- }
- #[derive(Clone, Debug, Default)]
- pub struct LintWithoutLintPass {
-     declared_lints: FxHashMap<Symbol, Span>,
-     registered_lints: FxHashSet<Symbol>,
- }
- impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]);
- impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
-     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-         if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id())
-             || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id())
-         {
-             return;
-         }
-         if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
-             let is_lint_ref_ty = is_lint_ref_type(cx, ty);
-             if is_deprecated_lint(cx, ty) || is_lint_ref_ty {
-                 check_invalid_clippy_version_attribute(cx, item);
-                 let expr = &cx.tcx.hir().body(body_id).value;
-                 let fields;
-                 if is_lint_ref_ty {
-                     if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind
-                         && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind {
-                             fields = struct_fields;
-                     } else {
-                         return;
-                     }
-                 } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind {
-                     fields = struct_fields;
-                 } else {
-                     return;
-                 }
-                 let field = fields
-                     .iter()
-                     .find(|f| f.ident.as_str() == "desc")
-                     .expect("lints must have a description field");
-                 if let ExprKind::Lit(Spanned {
-                     node: LitKind::Str(ref sym, _),
-                     ..
-                 }) = field.expr.kind
-                 {
-                     let sym_str = sym.as_str();
-                     if is_lint_ref_ty {
-                         if sym_str == "default lint description" {
-                             span_lint(
-                                 cx,
-                                 DEFAULT_LINT,
-                                 item.span,
-                                 &format!("the lint `{}` has the default lint description", item.ident.name),
-                             );
-                         }
-                         self.declared_lints.insert(item.ident.name, item.span);
-                     } else if sym_str == "default deprecation note" {
-                         span_lint(
-                             cx,
-                             DEFAULT_DEPRECATION_REASON,
-                             item.span,
-                             &format!("the lint `{}` has the default deprecation reason", item.ident.name),
-                         );
-                     }
-                 }
-             }
-         } else if let Some(macro_call) = root_macro_call_first_node(cx, item) {
-             if !matches!(
-                 cx.tcx.item_name(macro_call.def_id).as_str(),
-                 "impl_lint_pass" | "declare_lint_pass"
-             ) {
-                 return;
-             }
-             if let hir::ItemKind::Impl(hir::Impl {
-                 of_trait: None,
-                 items: impl_item_refs,
-                 ..
-             }) = item.kind
-             {
-                 let mut collector = LintCollector {
-                     output: &mut self.registered_lints,
-                     cx,
-                 };
-                 let body_id = cx.tcx.hir().body_owned_by(
-                     cx.tcx.hir().local_def_id(
-                         impl_item_refs
-                             .iter()
-                             .find(|iiref| iiref.ident.as_str() == "get_lints")
-                             .expect("LintPass needs to implement get_lints")
-                             .id
-                             .hir_id(),
-                     ),
-                 );
-                 collector.visit_expr(cx.tcx.hir().body(body_id).value);
-             }
-         }
-     }
-     fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
-         if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) {
-             return;
-         }
-         for (lint_name, &lint_span) in &self.declared_lints {
-             // When using the `declare_tool_lint!` macro, the original `lint_span`'s
-             // file points to "<rustc macros>".
-             // `compiletest-rs` thinks that's an error in a different file and
-             // just ignores it. This causes the test in compile-fail/lint_pass
-             // not able to capture the error.
-             // Therefore, we need to climb the macro expansion tree and find the
-             // actual span that invoked `declare_tool_lint!`:
-             let lint_span = lint_span.ctxt().outer_expn_data().call_site;
-             if !self.registered_lints.contains(lint_name) {
-                 span_lint(
-                     cx,
-                     LINT_WITHOUT_LINT_PASS,
-                     lint_span,
-                     &format!("the lint `{lint_name}` is not added to any `LintPass`"),
-                 );
-             }
-         }
-     }
- }
- fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool {
-     if let TyKind::Rptr(
-         _,
-         MutTy {
-             ty: inner,
-             mutbl: Mutability::Not,
-         },
-     ) = ty.kind
-     {
-         if let TyKind::Path(ref path) = inner.kind {
-             if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) {
-                 return match_def_path(cx, def_id, &paths::LINT);
-             }
-         }
-     }
-     false
- }
- fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
-     if let Some(value) = extract_clippy_version_value(cx, item) {
-         // The `sym!` macro doesn't work as it only expects a single token.
-         // It's better to keep it this way and have a direct `Symbol::intern` call here.
-         if value == Symbol::intern("pre 1.29.0") {
-             return;
-         }
-         if RustcVersion::parse(value.as_str()).is_err() {
-             span_lint_and_help(
-                 cx,
-                 INVALID_CLIPPY_VERSION_ATTRIBUTE,
-                 item.span,
-                 "this item has an invalid `clippy::version` attribute",
-                 None,
-                 "please use a valid semantic version, see `doc/adding_lints.md`",
-             );
-         }
-     } else {
-         span_lint_and_help(
-             cx,
-             MISSING_CLIPPY_VERSION_ATTRIBUTE,
-             item.span,
-             "this lint is missing the `clippy::version` attribute or version value",
-             None,
-             "please use a `clippy::version` attribute, see `doc/adding_lints.md`",
-         );
-     }
- }
- /// This function extracts the version value of a `clippy::version` attribute if the given value has
- /// one
- fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> {
-     let attrs = cx.tcx.hir().attrs(item.hir_id());
-     attrs.iter().find_map(|attr| {
-         if_chain! {
-             // Identify attribute
-             if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind;
-             if let [tool_name, attr_name] = &attr_kind.item.path.segments[..];
-             if tool_name.ident.name == sym::clippy;
-             if attr_name.ident.name == sym::version;
-             if let Some(version) = attr.value_str();
-             then {
-                 Some(version)
-             } else {
-                 None
-             }
-         }
-     })
- }
- struct LintCollector<'a, 'tcx> {
-     output: &'a mut FxHashSet<Symbol>,
-     cx: &'a LateContext<'tcx>,
- }
- impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
-     type NestedFilter = nested_filter::All;
-     fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
-         if path.segments.len() == 1 {
-             self.output.insert(path.segments[0].ident.name);
-         }
-     }
-     fn nested_visit_map(&mut self) -> Self::Map {
-         self.cx.tcx.hir()
-     }
- }
- #[derive(Clone, Default)]
- pub struct CompilerLintFunctions {
-     map: FxHashMap<&'static str, &'static str>,
- }
- impl CompilerLintFunctions {
-     #[must_use]
-     pub fn new() -> Self {
-         let mut map = FxHashMap::default();
-         map.insert("span_lint", "utils::span_lint");
-         map.insert("struct_span_lint", "utils::span_lint");
-         map.insert("lint", "utils::span_lint");
-         map.insert("span_lint_note", "utils::span_lint_and_note");
-         map.insert("span_lint_help", "utils::span_lint_and_help");
-         Self { map }
-     }
- }
- impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
- impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
-     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-         if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) {
-             return;
-         }
-         if_chain! {
-             if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind;
-             let fn_name = path.ident;
-             if let Some(sugg) = self.map.get(fn_name.as_str());
-             let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
-             if match_type(cx, ty, &paths::EARLY_CONTEXT)
-                 || match_type(cx, ty, &paths::LATE_CONTEXT);
-             then {
-                 span_lint_and_help(
-                     cx,
-                     COMPILER_LINT_FUNCTIONS,
-                     path.ident.span,
-                     "usage of a compiler lint function",
-                     None,
-                     &format!("please use the Clippy variant of this function: `{sugg}`"),
-                 );
-             }
-         }
-     }
- }
- declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
- impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
-     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-         if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) {
-             return;
-         }
-         let (method_names, arg_lists, spans) = method_calls(expr, 2);
-         let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect();
-         if_chain! {
-             if let ["expn_data", "outer_expn"] = method_names.as_slice();
-             let (self_arg, args)= arg_lists[1];
-             if args.is_empty();
-             let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
-             if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
-             then {
-                 span_lint_and_sugg(
-                     cx,
-                     OUTER_EXPN_EXPN_DATA,
-                     spans[1].with_hi(expr.span.hi()),
-                     "usage of `outer_expn().expn_data()`",
-                     "try",
-                     "outer_expn_data()".to_string(),
-                     Applicability::MachineApplicable,
-                 );
-             }
-         }
-     }
- }
- declare_lint_pass!(ProduceIce => [PRODUCE_ICE]);
- impl EarlyLintPass for ProduceIce {
-     fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
-         assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?");
-     }
- }
- fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
-     match fn_kind {
-         FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy",
-         FnKind::Closure(..) => false,
-     }
- }
- declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]);
- impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
-     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-         if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) {
-             return;
-         }
-         if_chain! {
-             if let ExprKind::Call(func, and_then_args) = expr.kind;
-             if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
-             if and_then_args.len() == 5;
-             if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind;
-             let body = cx.tcx.hir().body(body);
-             let only_expr = peel_blocks_with_stmt(body.value);
-             if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind;
-             if let ExprKind::Path(..) = recv.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[0]) => {
-                         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[0]) => {
-                         let help_snippet = snippet(cx, span_call_args[1].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[0]) => {
-                         let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
-                         suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
-                     },
-                     "help" => {
-                         let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#);
-                         suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
-                     }
-                     "note" => {
-                         let note_snippet = snippet(cx, span_call_args[0].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[1].span, r#""...""#);
-     let sugg_snippet = snippet(cx, span_call_args[2].span, "..");
-     let applicability_snippet = snippet(cx, span_call_args[3].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({}, {}, {}, {}, {}, {help})",
-             and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span,
-         ),
-         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({}, {}, {}, {}, {note_span}, {note})",
-             and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg,
-         ),
-         Applicability::MachineApplicable,
-     );
- }
- declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]);
- #[allow(clippy::too_many_lines)]
- impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath {
-     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-         enum Item {
-             LangItem(Symbol),
-             DiagnosticItem(Symbol),
-         }
-         static PATHS: &[&[&str]] = &[
-             &["clippy_utils", "match_def_path"],
-             &["clippy_utils", "match_trait_method"],
-             &["clippy_utils", "ty", "match_type"],
-             &["clippy_utils", "is_expr_path_def_path"],
-         ];
-         if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) {
-             return;
-         }
-         if_chain! {
-             if let ExprKind::Call(func, [cx_arg, def_arg, args@..]) = expr.kind;
-             if let ExprKind::Path(path) = &func.kind;
-             if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id();
-             if let Some(which_path) = match_any_def_paths(cx, id, PATHS);
-             let item_arg = if which_path == 4 { &args[1] } else { &args[0] };
-             // Extract the path to the matched type
-             if let Some(segments) = path_to_matched_type(cx, item_arg);
-             let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
-             if let Some(def_id) = def_path_res(cx, &segments[..], None).opt_def_id();
-             then {
-                 // def_path_res will match field names before anything else, but for this we want to match
-                 // inherent functions first.
-                 let def_id = if cx.tcx.def_kind(def_id) == DefKind::Field {
-                     let method_name = *segments.last().unwrap();
-                     cx.tcx.def_key(def_id).parent
-                         .and_then(|parent_idx|
-                             cx.tcx.inherent_impls(DefId { index: parent_idx, krate: def_id.krate }).iter()
-                                 .find_map(|impl_id| cx.tcx.associated_items(*impl_id)
-                                     .find_by_name_and_kind(
-                                         cx.tcx,
-                                         Ident::from_str(method_name),
-                                         AssocKind::Fn,
-                                         *impl_id,
-                                     )
-                                 )
-                         )
-                         .map_or(def_id, |item| item.def_id)
-                 } else {
-                     def_id
-                 };
-                 // Check if the target item is a diagnostic item or LangItem.
-                 let (msg, item) = if let Some(item_name)
-                     = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id)
-                 {
-                     (
-                         "use of a def path to a diagnostic item",
-                         Item::DiagnosticItem(*item_name),
-                     )
-                 } else if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) {
-                     let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id();
-                     let item_name = cx.tcx.adt_def(lang_items).variants().iter().nth(lang_item).unwrap().name;
-                     (
-                         "use of a def path to a `LangItem`",
-                         Item::LangItem(item_name),
-                     )
-                 } else {
-                     return;
-                 };
-                 let has_ctor = match cx.tcx.def_kind(def_id) {
-                     DefKind::Struct => {
-                         let variant = cx.tcx.adt_def(def_id).non_enum_variant();
-                         variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public())
-                     }
-                     DefKind::Variant => {
-                         let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id);
-                         variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public())
-                     }
-                     _ => false,
-                 };
-                 let mut app = Applicability::MachineApplicable;
-                 let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app);
-                 let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app);
-                 let (sugg, with_note) = match (which_path, item) {
-                     // match_def_path
-                     (0, Item::DiagnosticItem(item)) =>
-                         (format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), has_ctor),
-                     (0, Item::LangItem(item)) => (
-                         format!("{cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some({def_snip})"),
-                         has_ctor
-                     ),
-                     // match_trait_method
-                     (1, Item::DiagnosticItem(item)) =>
-                         (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false),
-                     // match_type
-                     (2, Item::DiagnosticItem(item)) =>
-                         (format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false),
-                     (2, Item::LangItem(item)) =>
-                         (format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), false),
-                     // is_expr_path_def_path
-                     (3, Item::DiagnosticItem(item)) if has_ctor => (
-                         format!(
-                             "is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",
-                         ),
-                         false,
-                     ),
-                     (3, Item::LangItem(item)) if has_ctor => (
-                         format!(
-                             "is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",
-                         ),
-                         false,
-                     ),
-                     (3, Item::DiagnosticItem(item)) =>
-                         (format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false),
-                     (3, Item::LangItem(item)) => (
-                         format!(
-                             "path_res({cx_snip}, {def_snip}).opt_def_id()\
-                                 .map_or(false, |id| {cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some(id))",
-                         ),
-                         false,
-                     ),
-                     _ => return,
-                 };
-                 span_lint_and_then(
-                     cx,
-                     UNNECESSARY_DEF_PATH,
-                     expr.span,
-                     msg,
-                     |diag| {
-                         diag.span_suggestion(expr.span, "try", sugg, app);
-                         if with_note {
-                             diag.help(
-                                 "if this `DefId` came from a constructor expression or pattern then the \
-                                     parent `DefId` should be used instead"
-                             );
-                         }
-                     },
-                 );
-             }
-         }
-     }
- }
- fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<String>> {
-     match peel_hir_expr_refs(expr).0.kind {
-         ExprKind::Path(ref 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 { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) {
-                     path_to_matched_type(cx, init)
-                 } else {
-                     None
-                 }
-             },
-             Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path(
-                 cx,
-                 cx.tcx.eval_static_initializer(def_id).ok()?.inner(),
-                 cx.tcx.type_of(def_id),
-             ),
-             Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? {
-                 ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => {
-                     read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id))
-                 },
-                 _ => None,
-             },
-             _ => None,
-         },
-         ExprKind::Array(exprs) => exprs
-             .iter()
-             .map(|expr| {
-                 if let ExprKind::Lit(lit) = &expr.kind {
-                     if let LitKind::Str(sym, _) = lit.node {
-                         return Some((*sym.as_str()).to_owned());
-                     }
-                 }
-                 None
-             })
-             .collect(),
-         _ => None,
-     }
- }
- fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option<Vec<String>> {
-     let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() {
-         let &alloc = alloc.provenance().values().next()?;
-         if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) {
-             (alloc.inner(), ty)
-         } else {
-             return None;
-         }
-     } else {
-         (alloc, ty)
-     };
-     if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind()
-         && let ty::Ref(_, ty, Mutability::Not) = *ty.kind()
-         && ty.is_str()
-     {
-         alloc
-             .provenance()
-             .values()
-             .map(|&alloc| {
-                 if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) {
-                     let alloc = alloc.inner();
-                     str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()))
-                         .ok().map(ToOwned::to_owned)
-                 } else {
-                     None
-                 }
-             })
-             .collect()
-     } else {
-         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, None) != 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, None).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,
-                 );
-             }
-         }
-     }
- }
++pub mod msrv_attr_impl;
++pub mod outer_expn_data_pass;
++pub mod produce_ice;
++pub mod unnecessary_def_path;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..da9514dd15eee4c85293c8ea8e0c546c99f5cb0e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,49 @@@
++use clippy_utils::diagnostics::span_lint;
++use rustc_ast::ast::{Crate, ItemKind, ModKind};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++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_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);
++                        }
++                    }
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d7666b77f6e969d1086f1ba0db9b5dd4f4c5bace
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,245 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet;
++use clippy_utils::{is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt, SpanlessEq};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::{Closure, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use std::borrow::{Borrow, Cow};
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Lints `span_lint_and_then` function calls, where the
++    /// closure argument has only one statement and that statement is a method
++    /// call to `span_suggestion`, `span_help`, `span_note` (using the same
++    /// span), `help` or `note`.
++    ///
++    /// These usages of `span_lint_and_then` should be replaced with one of the
++    /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or
++    /// `span_lint_and_note`.
++    ///
++    /// ### Why is this bad?
++    /// Using the wrapper `span_lint_and_*` functions, is more
++    /// convenient, readable and less error prone.
++    ///
++    /// ### Example
++    /// ```rust,ignore
++    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
++    ///     diag.span_suggestion(
++    ///         expr.span,
++    ///         help_msg,
++    ///         sugg.to_string(),
++    ///         Applicability::MachineApplicable,
++    ///     );
++    /// });
++    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
++    ///     diag.span_help(expr.span, help_msg);
++    /// });
++    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
++    ///     diag.help(help_msg);
++    /// });
++    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
++    ///     diag.span_note(expr.span, note_msg);
++    /// });
++    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
++    ///     diag.note(note_msg);
++    /// });
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust,ignore
++    /// span_lint_and_sugg(
++    ///     cx,
++    ///     TEST_LINT,
++    ///     expr.span,
++    ///     lint_msg,
++    ///     help_msg,
++    ///     sugg.to_string(),
++    ///     Applicability::MachineApplicable,
++    /// );
++    /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg);
++    /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg);
++    /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg);
++    /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg);
++    /// ```
++    pub COLLAPSIBLE_SPAN_LINT_CALLS,
++    internal,
++    "found collapsible `span_lint_and_then` calls"
++}
++
++declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]);
++
++impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) {
++            return;
++        }
++
++        if_chain! {
++            if let ExprKind::Call(func, and_then_args) = expr.kind;
++            if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
++            if and_then_args.len() == 5;
++            if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind;
++            let body = cx.tcx.hir().body(body);
++            let only_expr = peel_blocks_with_stmt(body.value);
++            if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind;
++            if let ExprKind::Path(..) = recv.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[0]) => {
++                        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[0]) => {
++                        let help_snippet = snippet(cx, span_call_args[1].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[0]) => {
++                        let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
++                        suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
++                    },
++                    "help" => {
++                        let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#);
++                        suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
++                    },
++                    "note" => {
++                        let note_snippet = snippet(cx, span_call_args[0].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[1].span, r#""...""#);
++    let sugg_snippet = snippet(cx, span_call_args[2].span, "..");
++    let applicability_snippet = snippet(cx, span_call_args[3].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({}, {}, {}, {}, {}, {help})",
++            and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span,
++        ),
++        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({}, {}, {}, {}, {note_span}, {note})",
++            and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg,
++        ),
++        Applicability::MachineApplicable,
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cacd05262a2151093552d0cfde6329911e3710b6
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::match_type;
++use clippy_utils::{is_lint_allowed, paths};
++use if_chain::if_chain;
++use rustc_data_structures::fx::FxHashMap;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*`
++    /// variant of the function.
++    ///
++    /// ### Why is this bad?
++    /// The `utils::*` variants also add a link to the Clippy documentation to the
++    /// warning/error messages.
++    ///
++    /// ### Example
++    /// ```rust,ignore
++    /// cx.span_lint(LINT_NAME, "message");
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust,ignore
++    /// utils::span_lint(cx, LINT_NAME, "message");
++    /// ```
++    pub COMPILER_LINT_FUNCTIONS,
++    internal,
++    "usage of the lint functions of the compiler instead of the utils::* variant"
++}
++
++impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
++
++#[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<'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}`"),
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..883a5c08e5c11b1f86ec1c84e3a627a27ffc1aad
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,164 @@@
++use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
++use clippy_utils::{higher, is_else_clause, is_expn_of};
++use if_chain::if_chain;
++use rustc_hir as hir;
++use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Local, Node, Stmt, StmtKind};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::{BytePos, Span};
++
++declare_clippy_lint! {
++    /// Finds unidiomatic usage of `if_chain!`
++    pub IF_CHAIN_STYLE,
++    internal,
++    "non-idiomatic `if_chain!` usage"
++}
++
++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 ExprKind::Block(then_block, _) = then.kind else { 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 Some(if_chain_span) = if_chain_span else { return };
++        // check for `if a && b;`
++        if_chain! {
++            if let ExprKind::Binary(op, _, _) = cond.kind;
++            if op.node == BinOpKind::And;
++            if cx.sess().source_map().is_multiline(cond.span);
++            then {
++                span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`");
++            }
++        }
++        if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span)
++            && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span)
++        {
++            span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`");
++        }
++    }
++}
++
++fn check_nested_if_chains(
++    cx: &LateContext<'_>,
++    if_expr: &Expr<'_>,
++    then_block: &Block<'_>,
++    if_chain_span: Option<Span>,
++) {
++    #[rustfmt::skip]
++    let (head, tail) = match *then_block {
++        Block { stmts, expr: Some(tail), .. } => (stmts, tail),
++        Block {
++            stmts: &[
++                ref head @ ..,
++                Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. }
++            ],
++            ..
++        } => (head, tail),
++        _ => return,
++    };
++    if_chain! {
++        if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail);
++        let sm = cx.sess().source_map();
++        if head
++            .iter()
++            .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span));
++        if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr);
++        then {
++        } else {
++            return;
++        }
++    }
++    let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) {
++        (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"),
++        (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"),
++        (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"),
++        _ => return,
++    };
++    span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| {
++        let (span, msg) = match head {
++            [] => return,
++            [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"),
++            [a, .., b] => (
++                a.span.to(b.span),
++                "these `let` statements can also be in the `if_chain!`",
++            ),
++        };
++        diag.span_help(span, msg);
++    });
++}
++
++fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool {
++    cx.tcx
++        .hir()
++        .parent_iter(hir_id)
++        .find(|(_, node)| {
++            #[rustfmt::skip]
++            !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_))
++        })
++        .map_or(false, |(id, _)| {
++            is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span)
++        })
++}
++
++/// Checks a trailing slice of statements and expression of a `Block` to see if they are part
++/// of the `then {..}` portion of an `if_chain!`
++fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool {
++    let span = if let [stmt, ..] = stmts {
++        stmt.span
++    } else if let Some(expr) = expr {
++        expr.span
++    } else {
++        // empty `then {}`
++        return true;
++    };
++    is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span)
++}
++
++/// Creates a `Span` for `let x = ..;` in an `if_chain!` call.
++fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span {
++    let mut span = local.pat.span;
++    if let Some(init) = local.init {
++        span = span.to(init.span);
++    }
++    span.adjust(if_chain_span.ctxt().outer_expn());
++    let sm = cx.sess().source_map();
++    let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span);
++    let span = sm.span_extend_to_next_char(span, ';', false);
++    Span::new(
++        span.lo() - BytePos(3),
++        span.hi() + BytePos(1),
++        span.ctxt(),
++        span.parent(),
++    )
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..096b601572b4db5dcce28ff090f39b8e1d4eadf6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,239 @@@
++use clippy_utils::consts::{constant_simple, Constant};
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet;
++use clippy_utils::ty::match_type;
++use clippy_utils::{def_path_res, is_expn_of, match_def_path, paths};
++use if_chain::if_chain;
++use rustc_data_structures::fx::FxHashMap;
++use rustc_errors::Applicability;
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::def_id::DefId;
++use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::mir::interpret::ConstValue;
++use rustc_middle::ty::{self};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::symbol::Symbol;
++
++use std::borrow::Cow;
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for interning symbols that have already been pre-interned and defined as constants.
++    ///
++    /// ### Why is this bad?
++    /// It's faster and easier to use the symbol constant.
++    ///
++    /// ### Example
++    /// ```rust,ignore
++    /// let _ = sym!(f32);
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust,ignore
++    /// let _ = sym::f32;
++    /// ```
++    pub INTERNING_DEFINED_SYMBOL,
++    internal,
++    "interning a symbol that is pre-interned and defined as a constant"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for unnecessary conversion from Symbol to a string.
++    ///
++    /// ### Why is this bad?
++    /// It's faster use symbols directly instead of strings.
++    ///
++    /// ### Example
++    /// ```rust,ignore
++    /// symbol.as_str() == "clippy";
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust,ignore
++    /// symbol == sym::clippy;
++    /// ```
++    pub UNNECESSARY_SYMBOL_STR,
++    internal,
++    "unnecessary conversion between Symbol and string"
++}
++
++#[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, None).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
++            },
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..25532dd4e2681e6455921480339e937b536e3e92
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,108 @@@
++use clippy_utils::consts::{constant_simple, Constant};
++use clippy_utils::def_path_res;
++use clippy_utils::diagnostics::span_lint;
++use if_chain::if_chain;
++use rustc_hir as hir;
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::Item;
++use rustc_hir_analysis::hir_ty_to_ty;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, FloatTy};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::symbol::Symbol;
++
++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_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");
++            }
++        }
++    }
++}
++
++// 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, None) != 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
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0dac64376b06539109482966d66918fb66f7efab
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,342 @@@
++use crate::utils::internal_lints::metadata_collector::is_deprecated_lint;
++use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
++use clippy_utils::macros::root_macro_call_first_node;
++use clippy_utils::{is_lint_allowed, match_def_path, paths};
++use if_chain::if_chain;
++use rustc_ast as ast;
++use rustc_ast::ast::LitKind;
++use rustc_data_structures::fx::{FxHashMap, FxHashSet};
++use rustc_hir as hir;
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::hir_id::CRATE_HIR_ID;
++use rustc_hir::intravisit::Visitor;
++use rustc_hir::{ExprKind, HirId, Item, MutTy, Mutability, Path, TyKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::nested_filter;
++use rustc_semver::RustcVersion;
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Spanned;
++use rustc_span::symbol::Symbol;
++use rustc_span::{sym, Span};
++
++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 cases of an auto-generated lint without an updated description,
++    /// i.e. `default lint description`.
++    ///
++    /// ### Why is this bad?
++    /// Indicates that the lint is not finished.
++    ///
++    /// ### Example
++    /// ```rust,ignore
++    /// declare_lint! { pub COOL_LINT, nursery, "default lint description" }
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust,ignore
++    /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" }
++    /// ```
++    pub DEFAULT_LINT,
++    internal,
++    "found 'default lint description' in a lint declaration"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// 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
++    /// Checks for cases of an auto-generated deprecated lint without an updated reason,
++    /// i.e. `"default deprecation note"`.
++    ///
++    /// ### Why is this bad?
++    /// Indicates that the documentation is incomplete.
++    ///
++    /// ### Example
++    /// ```rust,ignore
++    /// declare_deprecated_lint! {
++    ///     /// ### What it does
++    ///     /// Nothing. This lint has been deprecated.
++    ///     ///
++    ///     /// ### Deprecation reason
++    ///     /// TODO
++    ///     #[clippy::version = "1.63.0"]
++    ///     pub COOL_LINT,
++    ///     "default deprecation note"
++    /// }
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust,ignore
++    /// declare_deprecated_lint! {
++    ///     /// ### What it does
++    ///     /// Nothing. This lint has been deprecated.
++    ///     ///
++    ///     /// ### Deprecation reason
++    ///     /// This lint has been replaced by `cooler_lint`
++    ///     #[clippy::version = "1.63.0"]
++    ///     pub COOL_LINT,
++    ///     "this lint has been replaced by `cooler_lint`"
++    /// }
++    /// ```
++    pub DEFAULT_DEPRECATION_REASON,
++    internal,
++    "found 'default deprecation note' in a deprecated lint declaration"
++}
++
++#[derive(Clone, Debug, Default)]
++pub struct LintWithoutLintPass {
++    declared_lints: FxHashMap<Symbol, Span>,
++    registered_lints: FxHashSet<Symbol>,
++}
++
++impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]);
++
++impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
++    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
++        if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id())
++            || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id())
++        {
++            return;
++        }
++
++        if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
++            let is_lint_ref_ty = is_lint_ref_type(cx, ty);
++            if is_deprecated_lint(cx, ty) || is_lint_ref_ty {
++                check_invalid_clippy_version_attribute(cx, item);
++
++                let expr = &cx.tcx.hir().body(body_id).value;
++                let fields;
++                if is_lint_ref_ty {
++                    if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind
++                        && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind {
++                            fields = struct_fields;
++                    } else {
++                        return;
++                    }
++                } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind {
++                    fields = struct_fields;
++                } else {
++                    return;
++                }
++
++                let field = fields
++                    .iter()
++                    .find(|f| f.ident.as_str() == "desc")
++                    .expect("lints must have a description field");
++
++                if let ExprKind::Lit(Spanned {
++                    node: LitKind::Str(ref sym, _),
++                    ..
++                }) = field.expr.kind
++                {
++                    let sym_str = sym.as_str();
++                    if is_lint_ref_ty {
++                        if sym_str == "default lint description" {
++                            span_lint(
++                                cx,
++                                DEFAULT_LINT,
++                                item.span,
++                                &format!("the lint `{}` has the default lint description", item.ident.name),
++                            );
++                        }
++
++                        self.declared_lints.insert(item.ident.name, item.span);
++                    } else if sym_str == "default deprecation note" {
++                        span_lint(
++                            cx,
++                            DEFAULT_DEPRECATION_REASON,
++                            item.span,
++                            &format!("the lint `{}` has the default deprecation reason", item.ident.name),
++                        );
++                    }
++                }
++            }
++        } else if let Some(macro_call) = root_macro_call_first_node(cx, item) {
++            if !matches!(
++                cx.tcx.item_name(macro_call.def_id).as_str(),
++                "impl_lint_pass" | "declare_lint_pass"
++            ) {
++                return;
++            }
++            if let hir::ItemKind::Impl(hir::Impl {
++                of_trait: None,
++                items: impl_item_refs,
++                ..
++            }) = item.kind
++            {
++                let mut collector = LintCollector {
++                    output: &mut self.registered_lints,
++                    cx,
++                };
++                let body_id = cx.tcx.hir().body_owned_by(
++                    cx.tcx.hir().local_def_id(
++                        impl_item_refs
++                            .iter()
++                            .find(|iiref| iiref.ident.as_str() == "get_lints")
++                            .expect("LintPass needs to implement get_lints")
++                            .id
++                            .hir_id(),
++                    ),
++                );
++                collector.visit_expr(cx.tcx.hir().body(body_id).value);
++            }
++        }
++    }
++
++    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
++        if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) {
++            return;
++        }
++
++        for (lint_name, &lint_span) in &self.declared_lints {
++            // When using the `declare_tool_lint!` macro, the original `lint_span`'s
++            // file points to "<rustc macros>".
++            // `compiletest-rs` thinks that's an error in a different file and
++            // just ignores it. This causes the test in compile-fail/lint_pass
++            // not able to capture the error.
++            // Therefore, we need to climb the macro expansion tree and find the
++            // actual span that invoked `declare_tool_lint!`:
++            let lint_span = lint_span.ctxt().outer_expn_data().call_site;
++
++            if !self.registered_lints.contains(lint_name) {
++                span_lint(
++                    cx,
++                    LINT_WITHOUT_LINT_PASS,
++                    lint_span,
++                    &format!("the lint `{lint_name}` is not added to any `LintPass`"),
++                );
++            }
++        }
++    }
++}
++
++pub(super) fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool {
++    if let TyKind::Rptr(
++        _,
++        MutTy {
++            ty: inner,
++            mutbl: Mutability::Not,
++        },
++    ) = ty.kind
++    {
++        if let TyKind::Path(ref path) = inner.kind {
++            if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) {
++                return match_def_path(cx, def_id, &paths::LINT);
++            }
++        }
++    }
++
++    false
++}
++
++fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
++    if let Some(value) = extract_clippy_version_value(cx, item) {
++        // The `sym!` macro doesn't work as it only expects a single token.
++        // It's better to keep it this way and have a direct `Symbol::intern` call here.
++        if value == Symbol::intern("pre 1.29.0") {
++            return;
++        }
++
++        if RustcVersion::parse(value.as_str()).is_err() {
++            span_lint_and_help(
++                cx,
++                INVALID_CLIPPY_VERSION_ATTRIBUTE,
++                item.span,
++                "this item has an invalid `clippy::version` attribute",
++                None,
++                "please use a valid semantic version, see `doc/adding_lints.md`",
++            );
++        }
++    } else {
++        span_lint_and_help(
++            cx,
++            MISSING_CLIPPY_VERSION_ATTRIBUTE,
++            item.span,
++            "this lint is missing the `clippy::version` attribute or version value",
++            None,
++            "please use a `clippy::version` attribute, see `doc/adding_lints.md`",
++        );
++    }
++}
++
++/// This function extracts the version value of a `clippy::version` attribute if the given value has
++/// one
++pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> {
++    let attrs = cx.tcx.hir().attrs(item.hir_id());
++    attrs.iter().find_map(|attr| {
++        if_chain! {
++            // Identify attribute
++            if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind;
++            if let [tool_name, attr_name] = &attr_kind.item.path.segments[..];
++            if tool_name.ident.name == sym::clippy;
++            if attr_name.ident.name == sym::version;
++            if let Some(version) = attr.value_str();
++            then { Some(version) } else { None }
++        }
++    })
++}
++
++struct LintCollector<'a, 'tcx> {
++    output: &'a mut FxHashSet<Symbol>,
++    cx: &'a LateContext<'tcx>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
++    type NestedFilter = nested_filter::All;
++
++    fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
++        if path.segments.len() == 1 {
++            self.output.insert(path.segments[0].ident.name);
++        }
++    }
++
++    fn nested_visit_map(&mut self) -> Self::Map {
++        self.cx.tcx.hir()
++    }
++}
index c84191bb01034bf0c18b426af5e35805e2d9b2c4,0000000000000000000000000000000000000000..d06a616e4b30b2d8ee796e9cc5fe5d967d99f6d3
mode 100644,000000..100644
--- /dev/null
@@@ -1,1138 -1,0 +1,1142 @@@
- use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type};
 +//! This lint is used to collect metadata about clippy lints. This metadata is exported as a json
 +//! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html)
 +//!
 +//! This module and therefore the entire lint is guarded by a feature flag called `internal`
 +//!
 +//! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches
 +//! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such
 +//! a simple mistake)
 +
 +use crate::renamed_lints::RENAMED_LINTS;
-             let lints: Vec<String> = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect();
++use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type};
 +
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
 +use clippy_utils::{last_path_segment, match_def_path, match_function_call, match_path, paths};
 +use if_chain::if_chain;
 +use rustc_ast as ast;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::{
 +    self as hir, def::DefKind, intravisit, intravisit::Visitor, Closure, ExprKind, Item, ItemKind, Mutability, QPath,
 +};
 +use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
 +use rustc_middle::hir::nested_filter;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::Ident;
 +use rustc_span::{sym, Loc, Span, Symbol};
 +use serde::{ser::SerializeStruct, Serialize, Serializer};
 +use std::collections::BinaryHeap;
 +use std::fmt;
 +use std::fmt::Write as _;
 +use std::fs::{self, OpenOptions};
 +use std::io::prelude::*;
 +use std::path::Path;
 +use std::path::PathBuf;
 +use std::process::Command;
 +
 +/// This is the output file of the lint collector.
 +const OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
 +/// These lints are excluded from the export.
 +const BLACK_LISTED_LINTS: &[&str] = &["lint_author", "dump_hir", "internal_metadata_collector"];
 +/// These groups will be ignored by the lint group matcher. This is useful for collections like
 +/// `clippy::all`
 +const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"];
 +/// Lints within this group will be excluded from the collection. These groups
 +/// have to be defined without the `clippy::` prefix.
 +const EXCLUDED_LINT_GROUPS: [&str; 1] = ["internal"];
 +/// Collected deprecated lint will be assigned to this group in the JSON output
 +const DEPRECATED_LINT_GROUP_STR: &str = "deprecated";
 +/// This is the lint level for deprecated lints that will be displayed in the lint list
 +const DEPRECATED_LINT_LEVEL: &str = "none";
 +/// This array holds Clippy's lint groups with their corresponding default lint level. The
 +/// lint level for deprecated lints is set in `DEPRECATED_LINT_LEVEL`.
 +const DEFAULT_LINT_LEVELS: &[(&str, &str)] = &[
 +    ("correctness", "deny"),
 +    ("suspicious", "warn"),
 +    ("restriction", "allow"),
 +    ("style", "warn"),
 +    ("pedantic", "allow"),
 +    ("complexity", "warn"),
 +    ("perf", "warn"),
 +    ("cargo", "allow"),
 +    ("nursery", "allow"),
 +];
 +/// This prefix is in front of the lint groups in the lint store. The prefix will be trimmed
 +/// to only keep the actual lint group in the output.
 +const CLIPPY_LINT_GROUP_PREFIX: &str = "clippy::";
 +const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [
 +    &["clippy_utils", "diagnostics", "span_lint"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_help"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_note"],
 +    &["clippy_utils", "diagnostics", "span_lint_hir"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_sugg"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_then"],
 +    &["clippy_utils", "diagnostics", "span_lint_hir_and_then"],
 +];
 +const SUGGESTION_DIAGNOSTIC_BUILDER_METHODS: [(&str, bool); 9] = [
 +    ("span_suggestion", false),
 +    ("span_suggestion_short", false),
 +    ("span_suggestion_verbose", false),
 +    ("span_suggestion_hidden", false),
 +    ("tool_only_span_suggestion", false),
 +    ("multipart_suggestion", true),
 +    ("multipart_suggestions", true),
 +    ("tool_only_multipart_suggestion", true),
 +    ("span_suggestions", true),
 +];
 +const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [
 +    &["clippy_utils", "diagnostics", "multispan_sugg"],
 +    &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"],
 +];
 +const DEPRECATED_LINT_TYPE: [&str; 3] = ["clippy_lints", "deprecated_lints", "ClippyDeprecatedLint"];
 +
 +/// The index of the applicability name of `paths::APPLICABILITY_VALUES`
 +const APPLICABILITY_NAME_INDEX: usize = 2;
 +/// This applicability will be set for unresolved applicability values.
 +const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
 +/// The version that will be displayed if none has been defined
 +const VERSION_DEFAULT_STR: &str = "Unknown";
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Collects metadata about clippy lints for the website.
 +    ///
 +    /// This lint will be used to report problems of syntax parsing. You should hopefully never
 +    /// see this but never say never I guess ^^
 +    ///
 +    /// ### Why is this bad?
 +    /// This is not a bad thing but definitely a hacky way to do it. See
 +    /// issue [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) for a discussion
 +    /// about the implementation.
 +    ///
 +    /// ### Known problems
 +    /// Hopefully none. It would be pretty uncool to have a problem here :)
 +    ///
 +    /// ### Example output
 +    /// ```json,ignore
 +    /// {
 +    ///     "id": "internal_metadata_collector",
 +    ///     "id_span": {
 +    ///         "path": "clippy_lints/src/utils/internal_lints/metadata_collector.rs",
 +    ///         "line": 1
 +    ///     },
 +    ///     "group": "clippy::internal",
 +    ///     "docs": " ### What it does\nCollects metadata about clippy lints for the website. [...] "
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.56.0"]
 +    pub INTERNAL_METADATA_COLLECTOR,
 +    internal_warn,
 +    "A busy bee collection metadata about lints"
 +}
 +
 +impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]);
 +
 +#[allow(clippy::module_name_repetitions)]
 +#[derive(Debug, Clone)]
 +pub struct MetadataCollector {
 +    /// All collected lints
 +    ///
 +    /// We use a Heap here to have the lints added in alphabetic order in the export
 +    lints: BinaryHeap<LintMetadata>,
 +    applicability_info: FxHashMap<String, ApplicabilityInfo>,
 +    config: Vec<ClippyConfiguration>,
 +    clippy_project_root: PathBuf,
 +}
 +
 +impl MetadataCollector {
 +    pub fn new() -> Self {
 +        Self {
 +            lints: BinaryHeap::<LintMetadata>::default(),
 +            applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(),
 +            config: collect_configs(),
 +            clippy_project_root: std::env::current_dir()
 +                .expect("failed to get current dir")
 +                .ancestors()
 +                .nth(1)
 +                .expect("failed to get project root")
 +                .to_path_buf(),
 +        }
 +    }
 +
 +    fn get_lint_configs(&self, lint_name: &str) -> Option<String> {
 +        self.config
 +            .iter()
 +            .filter(|config| config.lints.iter().any(|lint| lint == lint_name))
 +            .map(ToString::to_string)
 +            .reduce(|acc, x| acc + &x)
 +            .map(|configurations| {
 +                format!(
 +                    r#"
 +### Configuration
 +This lint has the following configuration variables:
 +
 +{configurations}
 +"#
 +                )
 +            })
 +    }
 +}
 +
 +impl Drop for MetadataCollector {
 +    /// You might ask: How hacky is this?
 +    /// My answer:     YES
 +    fn drop(&mut self) {
 +        // The metadata collector gets dropped twice, this makes sure that we only write
 +        // when the list is full
 +        if self.lints.is_empty() {
 +            return;
 +        }
 +
 +        let mut applicability_info = std::mem::take(&mut self.applicability_info);
 +
 +        // Mapping the final data
 +        let mut lints = std::mem::take(&mut self.lints).into_sorted_vec();
 +        for x in &mut lints {
 +            x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default());
 +            replace_produces(&x.id, &mut x.docs, &self.clippy_project_root);
 +        }
 +
 +        collect_renames(&mut lints);
 +
 +        // Outputting
 +        if Path::new(OUTPUT_FILE).exists() {
 +            fs::remove_file(OUTPUT_FILE).unwrap();
 +        }
 +        let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap();
 +        writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap();
 +    }
 +}
 +
 +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
 +struct LintMetadata {
 +    id: String,
 +    id_span: SerializableSpan,
 +    group: String,
 +    level: String,
 +    docs: String,
 +    version: String,
 +    /// This field is only used in the output and will only be
 +    /// mapped shortly before the actual output.
 +    applicability: Option<ApplicabilityInfo>,
 +}
 +
 +impl LintMetadata {
 +    fn new(
 +        id: String,
 +        id_span: SerializableSpan,
 +        group: String,
 +        level: &'static str,
 +        version: String,
 +        docs: String,
 +    ) -> Self {
 +        Self {
 +            id,
 +            id_span,
 +            group,
 +            level: level.to_string(),
 +            version,
 +            docs,
 +            applicability: None,
 +        }
 +    }
 +}
 +
 +fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Path) {
 +    let mut doc_lines = docs.lines().map(ToString::to_string).collect::<Vec<_>>();
 +    let mut lines = doc_lines.iter_mut();
 +
 +    'outer: loop {
 +        // Find the start of the example
 +
 +        // ```rust
 +        loop {
 +            match lines.next() {
 +                Some(line) if line.trim_start().starts_with("```rust") => {
 +                    if line.contains("ignore") || line.contains("no_run") {
 +                        // A {{produces}} marker may have been put on a ignored code block by mistake,
 +                        // just seek to the end of the code block and continue checking.
 +                        if lines.any(|line| line.trim_start().starts_with("```")) {
 +                            continue;
 +                        }
 +
 +                        panic!("lint `{lint_name}` has an unterminated code block")
 +                    }
 +
 +                    break;
 +                },
 +                Some(line) if line.trim_start() == "{{produces}}" => {
 +                    panic!("lint `{lint_name}` has marker {{{{produces}}}} with an ignored or missing code block")
 +                },
 +                Some(line) => {
 +                    let line = line.trim();
 +                    // These are the two most common markers of the corrections section
 +                    if line.eq_ignore_ascii_case("Use instead:") || line.eq_ignore_ascii_case("Could be written as:") {
 +                        break 'outer;
 +                    }
 +                },
 +                None => break 'outer,
 +            }
 +        }
 +
 +        // Collect the example
 +        let mut example = Vec::new();
 +        loop {
 +            match lines.next() {
 +                Some(line) if line.trim_start() == "```" => break,
 +                Some(line) => example.push(line),
 +                None => panic!("lint `{lint_name}` has an unterminated code block"),
 +            }
 +        }
 +
 +        // Find the {{produces}} and attempt to generate the output
 +        loop {
 +            match lines.next() {
 +                Some(line) if line.is_empty() => {},
 +                Some(line) if line.trim() == "{{produces}}" => {
 +                    let output = get_lint_output(lint_name, &example, clippy_project_root);
 +                    line.replace_range(
 +                        ..,
 +                        &format!(
 +                            "<details>\
 +                            <summary>Produces</summary>\n\
 +                            \n\
 +                            ```text\n\
 +                            {output}\n\
 +                            ```\n\
 +                        </details>"
 +                        ),
 +                    );
 +
 +                    break;
 +                },
 +                // No {{produces}}, we can move on to the next example
 +                Some(_) => break,
 +                None => break 'outer,
 +            }
 +        }
 +    }
 +
 +    *docs = cleanup_docs(&doc_lines);
 +}
 +
 +fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root: &Path) -> String {
 +    let dir = tempfile::tempdir().unwrap_or_else(|e| panic!("failed to create temp dir: {e}"));
 +    let file = dir.path().join("lint_example.rs");
 +
 +    let mut source = String::new();
 +    let unhidden = example
 +        .iter()
 +        .map(|line| line.trim_start().strip_prefix("# ").unwrap_or(line));
 +
 +    // Get any attributes
 +    let mut lines = unhidden.peekable();
 +    while let Some(line) = lines.peek() {
 +        if line.starts_with("#!") {
 +            source.push_str(line);
 +            source.push('\n');
 +            lines.next();
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    let needs_main = !example.iter().any(|line| line.contains("fn main"));
 +    if needs_main {
 +        source.push_str("fn main() {\n");
 +    }
 +
 +    for line in lines {
 +        source.push_str(line);
 +        source.push('\n');
 +    }
 +
 +    if needs_main {
 +        source.push_str("}\n");
 +    }
 +
 +    if let Err(e) = fs::write(&file, &source) {
 +        panic!("failed to write to `{}`: {e}", file.as_path().to_string_lossy());
 +    }
 +
 +    let prefixed_name = format!("{CLIPPY_LINT_GROUP_PREFIX}{lint_name}");
 +
 +    let mut cmd = Command::new("cargo");
 +
 +    cmd.current_dir(clippy_project_root)
 +        .env("CARGO_INCREMENTAL", "0")
 +        .env("CLIPPY_ARGS", "")
 +        .env("CLIPPY_DISABLE_DOCS_LINKS", "1")
 +        // We need to disable this to enable all lints
 +        .env("ENABLE_METADATA_COLLECTION", "0")
 +        .args(["run", "--bin", "clippy-driver"])
 +        .args(["--target-dir", "./clippy_lints/target"])
 +        .args(["--", "--error-format=json"])
 +        .args(["--edition", "2021"])
 +        .arg("-Cdebuginfo=0")
 +        .args(["-A", "clippy::all"])
 +        .args(["-W", &prefixed_name])
 +        .args(["-L", "./target/debug"])
 +        .args(["-Z", "no-codegen"]);
 +
 +    let output = cmd
 +        .arg(file.as_path())
 +        .output()
 +        .unwrap_or_else(|e| panic!("failed to run `{cmd:?}`: {e}"));
 +
 +    let tmp_file_path = file.to_string_lossy();
 +    let stderr = std::str::from_utf8(&output.stderr).unwrap();
 +    let msgs = stderr
 +        .lines()
 +        .filter(|line| line.starts_with('{'))
 +        .map(|line| serde_json::from_str(line).unwrap())
 +        .collect::<Vec<serde_json::Value>>();
 +
 +    let mut rendered = String::new();
 +    let iter = msgs
 +        .iter()
 +        .filter(|msg| matches!(&msg["code"]["code"], serde_json::Value::String(s) if s == &prefixed_name));
 +
 +    for message in iter {
 +        let rendered_part = message["rendered"].as_str().expect("rendered field should exist");
 +        rendered.push_str(rendered_part);
 +    }
 +
 +    if rendered.is_empty() {
 +        let rendered: Vec<&str> = msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect();
 +        let non_json: Vec<&str> = stderr.lines().filter(|line| !line.starts_with('{')).collect();
 +        panic!(
 +            "did not find lint `{lint_name}` in output of example, got:\n{}\n{}",
 +            non_json.join("\n"),
 +            rendered.join("\n")
 +        );
 +    }
 +
 +    // The reader doesn't need to see `/tmp/.tmpfiy2Qd/lint_example.rs` :)
 +    rendered.trim_end().replace(&*tmp_file_path, "lint_example.rs")
 +}
 +
 +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
 +struct SerializableSpan {
 +    path: String,
 +    line: usize,
 +}
 +
 +impl fmt::Display for SerializableSpan {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line)
 +    }
 +}
 +
 +impl SerializableSpan {
 +    fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self {
 +        Self::from_span(cx, item.ident.span)
 +    }
 +
 +    fn from_span(cx: &LateContext<'_>, span: Span) -> Self {
 +        let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo());
 +
 +        Self {
 +            path: format!("{}", loc.file.name.prefer_remapped()),
 +            line: loc.line,
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
 +struct ApplicabilityInfo {
 +    /// Indicates if any of the lint emissions uses multiple spans. This is related to
 +    /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can
 +    /// currently not be applied automatically.
 +    is_multi_part_suggestion: bool,
 +    applicability: Option<usize>,
 +}
 +
 +impl Serialize for ApplicabilityInfo {
 +    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 +    where
 +        S: Serializer,
 +    {
 +        let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?;
 +        s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?;
 +        if let Some(index) = self.applicability {
 +            s.serialize_field(
 +                "applicability",
 +                &paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX],
 +            )?;
 +        } else {
 +            s.serialize_field("applicability", APPLICABILITY_UNRESOLVED_STR)?;
 +        }
 +        s.end()
 +    }
 +}
 +
 +// ==================================================================
 +// Configuration
 +// ==================================================================
 +#[derive(Debug, Clone, Default)]
 +pub struct ClippyConfiguration {
 +    name: String,
 +    config_type: &'static str,
 +    default: String,
 +    lints: Vec<String>,
 +    doc: String,
 +    #[allow(dead_code)]
 +    deprecation_reason: Option<&'static str>,
 +}
 +
 +impl ClippyConfiguration {
 +    pub fn new(
 +        name: &'static str,
 +        config_type: &'static str,
 +        default: String,
 +        doc_comment: &'static str,
 +        deprecation_reason: Option<&'static str>,
 +    ) -> Self {
 +        let (lints, doc) = parse_config_field_doc(doc_comment)
 +            .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
 +
 +        Self {
 +            name: to_kebab(name),
 +            lints,
 +            doc,
 +            config_type,
 +            default,
 +            deprecation_reason,
 +        }
 +    }
 +}
 +
 +fn collect_configs() -> Vec<ClippyConfiguration> {
 +    crate::utils::conf::metadata::get_configuration_metadata()
 +}
 +
 +/// This parses the field documentation of the config struct.
 +///
 +/// ```rust, ignore
 +/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
 +/// ```
 +///
 +/// Would yield:
 +/// ```rust, ignore
 +/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
 +/// ```
 +fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
 +    const DOC_START: &str = " Lint: ";
 +    if_chain! {
 +        if doc_comment.starts_with(DOC_START);
 +        if let Some(split_pos) = doc_comment.find('.');
 +        then {
 +            let mut doc_comment = doc_comment.to_string();
 +            let mut documentation = doc_comment.split_off(split_pos);
 +
 +            // Extract lints
 +            doc_comment.make_ascii_lowercase();
++            let lints: Vec<String> = doc_comment
++                .split_off(DOC_START.len())
++                .split(", ")
++                .map(str::to_string)
++                .collect();
 +
 +            // Format documentation correctly
 +            // split off leading `.` from lint name list and indent for correct formatting
 +            documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n    ");
 +
 +            Some((lints, documentation))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
 +fn to_kebab(config_name: &str) -> String {
 +    config_name.replace('_', "-")
 +}
 +
 +impl fmt::Display for ClippyConfiguration {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
 +        writeln!(
 +            f,
 +            "* `{}`: `{}`: {} (defaults to `{}`)",
 +            self.name, self.config_type, self.doc, self.default
 +        )
 +    }
 +}
 +
 +// ==================================================================
 +// Lint pass
 +// ==================================================================
 +impl<'hir> LateLintPass<'hir> for MetadataCollector {
 +    /// Collecting lint declarations like:
 +    /// ```rust, ignore
 +    /// declare_clippy_lint! {
 +    ///     /// ### What it does
 +    ///     /// Something IDK.
 +    ///     pub SOME_LINT,
 +    ///     internal,
 +    ///     "Who am I?"
 +    /// }
 +    /// ```
 +    fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) {
 +        if let ItemKind::Static(ty, Mutability::Not, _) = item.kind {
 +            // Normal lint
 +            if_chain! {
 +                // item validation
 +                if is_lint_ref_type(cx, ty);
 +                // disallow check
 +                let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
 +                if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
 +                // metadata extraction
 +                if let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item);
 +                if let Some(mut raw_docs) = extract_attr_docs_or_lint(cx, item);
 +                then {
 +                    if let Some(configuration_section) = self.get_lint_configs(&lint_name) {
 +                        raw_docs.push_str(&configuration_section);
 +                    }
 +                    let version = get_lint_version(cx, item);
 +
 +                    self.lints.push(LintMetadata::new(
 +                        lint_name,
 +                        SerializableSpan::from_item(cx, item),
 +                        group,
 +                        level,
 +                        version,
 +                        raw_docs,
 +                    ));
 +                }
 +            }
 +
 +            if_chain! {
 +                if is_deprecated_lint(cx, ty);
 +                // disallow check
 +                let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
 +                if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
 +                // Metadata the little we can get from a deprecated lint
 +                if let Some(raw_docs) = extract_attr_docs_or_lint(cx, item);
 +                then {
 +                    let version = get_lint_version(cx, item);
 +
 +                    self.lints.push(LintMetadata::new(
 +                        lint_name,
 +                        SerializableSpan::from_item(cx, item),
 +                        DEPRECATED_LINT_GROUP_STR.to_string(),
 +                        DEPRECATED_LINT_LEVEL,
 +                        version,
 +                        raw_docs,
 +                    ));
 +                }
 +            }
 +        }
 +    }
 +
 +    /// Collecting constant applicability from the actual lint emissions
 +    ///
 +    /// Example:
 +    /// ```rust, ignore
 +    /// span_lint_and_sugg(
 +    ///     cx,
 +    ///     SOME_LINT,
 +    ///     item.span,
 +    ///     "Le lint message",
 +    ///     "Here comes help:",
 +    ///     "#![allow(clippy::all)]",
 +    ///     Applicability::MachineApplicable, // <-- Extracts this constant value
 +    /// );
 +    /// ```
 +    fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) {
 +        if let Some(args) = match_lint_emission(cx, expr) {
 +            let emission_info = extract_emission_info(cx, args);
 +            if emission_info.is_empty() {
 +                // See:
 +                // - src/misc.rs:734:9
 +                // - src/methods/mod.rs:3545:13
 +                // - src/methods/mod.rs:3496:13
 +                // We are basically unable to resolve the lint name itself.
 +                return;
 +            }
 +
 +            for (lint_name, applicability, is_multi_part) in emission_info {
 +                let app_info = self.applicability_info.entry(lint_name).or_default();
 +                app_info.applicability = applicability;
 +                app_info.is_multi_part_suggestion = is_multi_part;
 +            }
 +        }
 +    }
 +}
 +
 +// ==================================================================
 +// Lint definition extraction
 +// ==================================================================
 +fn sym_to_string(sym: Symbol) -> String {
 +    sym.as_str().to_string()
 +}
 +
 +fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
 +    extract_attr_docs(cx, item).or_else(|| {
 +        lint_collection_error_item(cx, item, "could not collect the lint documentation");
 +        None
 +    })
 +}
 +
 +/// This function collects all documentation that has been added to an item using
 +/// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks
 +///
 +/// ```ignore
 +/// #[doc = r"Hello world!"]
 +/// #[doc = r"=^.^="]
 +/// struct SomeItem {}
 +/// ```
 +///
 +/// Would result in `Hello world!\n=^.^=\n`
 +fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
 +    let attrs = cx.tcx.hir().attrs(item.hir_id());
 +    let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str);
 +
 +    if let Some(line) = lines.next() {
 +        let raw_docs = lines.fold(String::from(line.as_str()) + "\n", |s, line| s + line.as_str() + "\n");
 +        return Some(raw_docs);
 +    }
 +
 +    None
 +}
 +
 +/// This function may modify the doc comment to ensure that the string can be displayed using a
 +/// markdown viewer in Clippy's lint list. The following modifications could be applied:
 +/// * Removal of leading space after a new line. (Important to display tables)
 +/// * Ensures that code blocks only contain language information
 +fn cleanup_docs(docs_collection: &Vec<String>) -> String {
 +    let mut in_code_block = false;
 +    let mut is_code_block_rust = false;
 +
 +    let mut docs = String::new();
 +    for line in docs_collection {
 +        // Rustdoc hides code lines starting with `# ` and this removes them from Clippy's lint list :)
 +        if is_code_block_rust && line.trim_start().starts_with("# ") {
 +            continue;
 +        }
 +
 +        // The line should be represented in the lint list, even if it's just an empty line
 +        docs.push('\n');
 +        if let Some(info) = line.trim_start().strip_prefix("```") {
 +            in_code_block = !in_code_block;
 +            is_code_block_rust = false;
 +            if in_code_block {
 +                let lang = info
 +                    .trim()
 +                    .split(',')
 +                    // remove rustdoc directives
 +                    .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic"))
 +                    // if no language is present, fill in "rust"
 +                    .unwrap_or("rust");
 +                docs.push_str("```");
 +                docs.push_str(lang);
 +
 +                is_code_block_rust = lang == "rust";
 +                continue;
 +            }
 +        }
 +        // This removes the leading space that the macro translation introduces
 +        if let Some(stripped_doc) = line.strip_prefix(' ') {
 +            docs.push_str(stripped_doc);
 +        } else if !line.is_empty() {
 +            docs.push_str(line);
 +        }
 +    }
 +
 +    docs
 +}
 +
 +fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
 +    extract_clippy_version_value(cx, item).map_or_else(
 +        || VERSION_DEFAULT_STR.to_string(),
 +        |version| version.as_str().to_string(),
 +    )
 +}
 +
 +fn get_lint_group_and_level_or_lint(
 +    cx: &LateContext<'_>,
 +    lint_name: &str,
 +    item: &Item<'_>,
 +) -> Option<(String, &'static str)> {
 +    let result = cx.lint_store.check_lint_name(
 +        lint_name,
 +        Some(sym::clippy),
 +        &std::iter::once(Ident::with_dummy_span(sym::clippy)).collect(),
 +    );
 +    if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
 +        if let Some(group) = get_lint_group(cx, lint_lst[0]) {
 +            if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
 +                return None;
 +            }
 +
 +            if let Some(level) = get_lint_level_from_group(&group) {
 +                Some((group, level))
 +            } else {
 +                lint_collection_error_item(
 +                    cx,
 +                    item,
 +                    &format!("Unable to determine lint level for found group `{group}`"),
 +                );
 +                None
 +            }
 +        } else {
 +            lint_collection_error_item(cx, item, "Unable to determine lint group");
 +            None
 +        }
 +    } else {
 +        lint_collection_error_item(cx, item, "Unable to find lint in lint_store");
 +        None
 +    }
 +}
 +
 +fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
 +    for (group_name, lints, _) in cx.lint_store.get_lint_groups() {
 +        if IGNORED_LINT_GROUPS.contains(&group_name) {
 +            continue;
 +        }
 +
 +        if lints.iter().any(|group_lint| *group_lint == lint_id) {
 +            let group = group_name.strip_prefix(CLIPPY_LINT_GROUP_PREFIX).unwrap_or(group_name);
 +            return Some((*group).to_string());
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
 +    DEFAULT_LINT_LEVELS
 +        .iter()
 +        .find_map(|(group_name, group_level)| (*group_name == lint_group).then_some(*group_level))
 +}
 +
 +pub(super) fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
 +    if let hir::TyKind::Path(ref path) = ty.kind {
 +        if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) {
 +            return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE);
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn collect_renames(lints: &mut Vec<LintMetadata>) {
 +    for lint in lints {
 +        let mut collected = String::new();
 +        let mut names = vec![lint.id.clone()];
 +
 +        loop {
 +            if let Some(lint_name) = names.pop() {
 +                for (k, v) in RENAMED_LINTS {
 +                    if_chain! {
 +                        if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX);
 +                        if name == lint_name;
 +                        if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX);
 +                        then {
 +                            writeln!(collected, "* `{past_name}`").unwrap();
 +                            names.push(past_name.to_string());
 +                        }
 +                    }
 +                }
 +
 +                continue;
 +            }
 +
 +            break;
 +        }
 +
 +        if !collected.is_empty() {
 +            write!(
 +                &mut lint.docs,
 +                r#"
 +### Past names
 +
 +{collected}
 +"#
 +            )
 +            .unwrap();
 +        }
 +    }
 +}
 +
 +// ==================================================================
 +// Lint emission
 +// ==================================================================
 +fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) {
 +    span_lint(
 +        cx,
 +        INTERNAL_METADATA_COLLECTOR,
 +        item.ident.span,
 +        &format!("metadata collection error for `{}`: {message}", item.ident.name),
 +    );
 +}
 +
 +// ==================================================================
 +// Applicability
 +// ==================================================================
 +/// This function checks if a given expression is equal to a simple lint emission function call.
 +/// It will return the function arguments if the emission matched any function.
 +fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) -> Option<&'hir [hir::Expr<'hir>]> {
 +    LINT_EMISSION_FUNCTIONS
 +        .iter()
 +        .find_map(|emission_fn| match_function_call(cx, expr, emission_fn))
 +}
 +
 +fn take_higher_applicability(a: Option<usize>, b: Option<usize>) -> Option<usize> {
 +    a.map_or(b, |a| a.max(b.unwrap_or_default()).into())
 +}
 +
 +fn extract_emission_info<'hir>(
 +    cx: &LateContext<'hir>,
 +    args: &'hir [hir::Expr<'hir>],
 +) -> Vec<(String, Option<usize>, bool)> {
 +    let mut lints = Vec::new();
 +    let mut applicability = None;
 +    let mut multi_part = false;
 +
 +    for arg in args {
 +        let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(arg));
 +
 +        if match_type(cx, arg_ty, &paths::LINT) {
 +            // If we found the lint arg, extract the lint name
 +            let mut resolved_lints = resolve_lints(cx, arg);
 +            lints.append(&mut resolved_lints);
 +        } else if match_type(cx, arg_ty, &paths::APPLICABILITY) {
 +            applicability = resolve_applicability(cx, arg);
 +        } else if arg_ty.is_closure() {
 +            multi_part |= check_is_multi_part(cx, arg);
 +            applicability = applicability.or_else(|| resolve_applicability(cx, arg));
 +        }
 +    }
 +
 +    lints
 +        .into_iter()
 +        .map(|lint_name| (lint_name, applicability, multi_part))
 +        .collect()
 +}
 +
 +/// Resolves the possible lints that this expression could reference
 +fn resolve_lints<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> {
 +    let mut resolver = LintResolver::new(cx);
 +    resolver.visit_expr(expr);
 +    resolver.lints
 +}
 +
 +/// This function tries to resolve the linked applicability to the given expression.
 +fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> {
 +    let mut resolver = ApplicabilityResolver::new(cx);
 +    resolver.visit_expr(expr);
 +    resolver.complete()
 +}
 +
 +fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
 +    if let ExprKind::Closure(&Closure { body, .. }) = closure_expr.kind {
 +        let mut scanner = IsMultiSpanScanner::new(cx);
 +        intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body));
 +        return scanner.is_multi_part();
 +    } else if let Some(local) = get_parent_local(cx, closure_expr) {
 +        if let Some(local_init) = local.init {
 +            return check_is_multi_part(cx, local_init);
 +        }
 +    }
 +
 +    false
 +}
 +
 +struct LintResolver<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    lints: Vec<String>,
 +}
 +
 +impl<'a, 'hir> LintResolver<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            lints: Vec::<String>::default(),
 +        }
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        if_chain! {
 +            if let ExprKind::Path(qpath) = &expr.kind;
 +            if let QPath::Resolved(_, path) = qpath;
 +
 +            let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
 +            if match_type(self.cx, expr_ty, &paths::LINT);
 +            then {
 +                if let hir::def::Res::Def(DefKind::Static(..), _) = path.res {
 +                    let lint_name = last_path_segment(qpath).ident.name;
 +                    self.lints.push(sym_to_string(lint_name).to_ascii_lowercase());
 +                } else if let Some(local) = get_parent_local(self.cx, expr) {
 +                    if let Some(local_init) = local.init {
 +                        intravisit::walk_expr(self, local_init);
 +                    }
 +                }
 +            }
 +        }
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
 +
 +/// This visitor finds the highest applicability value in the visited expressions
 +struct ApplicabilityResolver<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    /// This is the index of highest `Applicability` for `paths::APPLICABILITY_VALUES`
 +    applicability_index: Option<usize>,
 +}
 +
 +impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            applicability_index: None,
 +        }
 +    }
 +
 +    fn add_new_index(&mut self, new_index: usize) {
 +        self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index));
 +    }
 +
 +    fn complete(self) -> Option<usize> {
 +        self.applicability_index
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_path(&mut self, path: &'hir hir::Path<'hir>, _id: hir::HirId) {
 +        for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() {
 +            if match_path(path, enum_value) {
 +                self.add_new_index(index);
 +                return;
 +            }
 +        }
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
 +
 +        if_chain! {
 +            if match_type(self.cx, expr_ty, &paths::APPLICABILITY);
 +            if let Some(local) = get_parent_local(self.cx, expr);
 +            if let Some(local_init) = local.init;
 +            then {
 +                intravisit::walk_expr(self, local_init);
 +            }
 +        };
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
 +
 +/// This returns the parent local node if the expression is a reference one
 +fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> {
 +    if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
 +        if let hir::def::Res::Local(local_hir) = path.res {
 +            return get_parent_local_hir_id(cx, local_hir);
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> {
 +    let map = cx.tcx.hir();
 +
 +    match map.find(map.get_parent_node(hir_id)) {
 +        Some(hir::Node::Local(local)) => Some(local),
 +        Some(hir::Node::Pat(pattern)) => get_parent_local_hir_id(cx, pattern.hir_id),
 +        _ => None,
 +    }
 +}
 +
 +/// This visitor finds the highest applicability value in the visited expressions
 +struct IsMultiSpanScanner<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    suggestion_count: usize,
 +}
 +
 +impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            suggestion_count: 0,
 +        }
 +    }
 +
 +    /// Add a new single expression suggestion to the counter
 +    fn add_single_span_suggestion(&mut self) {
 +        self.suggestion_count += 1;
 +    }
 +
 +    /// Signals that a suggestion with possible multiple spans was found
 +    fn add_multi_part_suggestion(&mut self) {
 +        self.suggestion_count += 2;
 +    }
 +
 +    /// Checks if the suggestions include multiple spans
 +    fn is_multi_part(&self) -> bool {
 +        self.suggestion_count > 1
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        // Early return if the lint is already multi span
 +        if self.is_multi_part() {
 +            return;
 +        }
 +
 +        match &expr.kind {
 +            ExprKind::Call(fn_expr, _args) => {
 +                let found_function = SUGGESTION_FUNCTIONS
 +                    .iter()
 +                    .any(|func_path| match_function_call(self.cx, fn_expr, func_path).is_some());
 +                if found_function {
 +                    // These functions are all multi part suggestions
 +                    self.add_single_span_suggestion();
 +                }
 +            },
 +            ExprKind::MethodCall(path, recv, _, _arg_span) => {
 +                let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(recv));
 +                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 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1e994e3f2b1713b1c2cef1502479177d8f7566e0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet;
++use clippy_utils::ty::match_type;
++use clippy_utils::{match_def_path, paths};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir_analysis::hir_ty_to_ty;
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::ty::{self, subst::GenericArgKind};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++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!(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 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2b13fad80665c73ae93e82fd7d1d8115e6c95d1e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::ty::match_type;
++use clippy_utils::{is_lint_allowed, method_calls, paths};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::symbol::Symbol;
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for calls to `cx.outer().expn_data()` and suggests to use
++    /// the `cx.outer_expn_data()`
++    ///
++    /// ### Why is this bad?
++    /// `cx.outer_expn_data()` is faster and more concise.
++    ///
++    /// ### Example
++    /// ```rust,ignore
++    /// expr.span.ctxt().outer().expn_data()
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust,ignore
++    /// expr.span.ctxt().outer_expn_data()
++    /// ```
++    pub OUTER_EXPN_EXPN_DATA,
++    internal,
++    "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`"
++}
++
++declare_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 (self_arg, args) = arg_lists[1];
++            if args.is_empty();
++            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,
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5899b94e16ba241e078300d5760ca6537a525ec3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++use rustc_ast::ast::NodeId;
++use rustc_ast::visit::FnKind;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::Span;
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Not an actual lint. This lint is only meant for testing our customized internal compiler
++    /// error message by calling `panic`.
++    ///
++    /// ### Why is this bad?
++    /// ICE in large quantities can damage your teeth
++    ///
++    /// ### Example
++    /// ```rust,ignore
++    /// 🍦🍦🍦🍦🍦
++    /// ```
++    pub PRODUCE_ICE,
++    internal,
++    "this message should not appear anywhere as we ICE before and don't emit the lint"
++}
++
++declare_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,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4cf76f53625510fd7989b0a6aa450c202df14c0c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,343 @@@
++use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
++use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::{def_path_res, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_data_structures::fx::FxHashSet;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::def::{DefKind, Namespace, Res};
++use rustc_hir::def_id::DefId;
++use rustc_hir::{Expr, ExprKind, Local, Mutability, Node};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc};
++use rustc_middle::ty::{self, AssocKind, DefIdTree, Ty};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::symbol::{Ident, Symbol};
++use rustc_span::Span;
++
++use std::str;
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for usages of def paths when a diagnostic item or a `LangItem` could be used.
++    ///
++    /// ### Why is this bad?
++    /// The path for an item is subject to change and is less efficient to look up than a
++    /// diagnostic item or a `LangItem`.
++    ///
++    /// ### Example
++    /// ```rust,ignore
++    /// utils::match_type(cx, ty, &paths::VEC)
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust,ignore
++    /// utils::is_type_diagnostic_item(cx, ty, sym::Vec)
++    /// ```
++    pub UNNECESSARY_DEF_PATH,
++    internal,
++    "using a def path when a diagnostic item or a `LangItem` is available"
++}
++
++impl_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]);
++
++#[derive(Default)]
++pub struct UnnecessaryDefPath {
++    array_def_ids: FxHashSet<(DefId, Span)>,
++    linted_def_ids: FxHashSet<DefId>,
++}
++
++impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath {
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) {
++            return;
++        }
++
++        match expr.kind {
++            ExprKind::Call(func, args) => self.check_call(cx, func, args, expr.span),
++            ExprKind::Array(elements) => self.check_array(cx, elements, expr.span),
++            _ => {},
++        }
++    }
++
++    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
++        for &(def_id, span) in &self.array_def_ids {
++            if self.linted_def_ids.contains(&def_id) {
++                continue;
++            }
++
++            let (msg, sugg) = if let Some(sym) = cx.tcx.get_diagnostic_name(def_id) {
++                ("diagnostic item", format!("sym::{sym}"))
++            } else if let Some(sym) = get_lang_item_name(cx, def_id) {
++                ("language item", format!("LangItem::{sym}"))
++            } else {
++                continue;
++            };
++
++            span_lint_and_help(
++                cx,
++                UNNECESSARY_DEF_PATH,
++                span,
++                &format!("hardcoded path to a {msg}"),
++                None,
++                &format!("convert all references to use `{sugg}`"),
++            );
++        }
++    }
++}
++
++impl UnnecessaryDefPath {
++    #[allow(clippy::too_many_lines)]
++    fn check_call(&mut self, cx: &LateContext<'_>, func: &Expr<'_>, args: &[Expr<'_>], span: Span) {
++        enum Item {
++            LangItem(Symbol),
++            DiagnosticItem(Symbol),
++        }
++        static PATHS: &[&[&str]] = &[
++            &["clippy_utils", "match_def_path"],
++            &["clippy_utils", "match_trait_method"],
++            &["clippy_utils", "ty", "match_type"],
++            &["clippy_utils", "is_expr_path_def_path"],
++        ];
++
++        if_chain! {
++            if let [cx_arg, def_arg, args @ ..] = args;
++            if let ExprKind::Path(path) = &func.kind;
++            if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id();
++            if let Some(which_path) = match_any_def_paths(cx, id, PATHS);
++            let item_arg = if which_path == 4 { &args[1] } else { &args[0] };
++            // Extract the path to the matched type
++            if let Some(segments) = path_to_matched_type(cx, item_arg);
++            let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
++            if let Some(def_id) = inherent_def_path_res(cx, &segments[..]);
++            then {
++                // Check if the target item is a diagnostic item or LangItem.
++                #[rustfmt::skip]
++                let (msg, item) = if let Some(item_name)
++                    = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id)
++                {
++                    (
++                        "use of a def path to a diagnostic item",
++                        Item::DiagnosticItem(*item_name),
++                    )
++                } else if let Some(item_name) = get_lang_item_name(cx, def_id) {
++                    (
++                        "use of a def path to a `LangItem`",
++                        Item::LangItem(item_name),
++                    )
++                } else {
++                    return;
++                };
++
++                let has_ctor = match cx.tcx.def_kind(def_id) {
++                    DefKind::Struct => {
++                        let variant = cx.tcx.adt_def(def_id).non_enum_variant();
++                        variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public())
++                    },
++                    DefKind::Variant => {
++                        let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id);
++                        variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public())
++                    },
++                    _ => false,
++                };
++
++                let mut app = Applicability::MachineApplicable;
++                let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app);
++                let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app);
++                let (sugg, with_note) = match (which_path, item) {
++                    // match_def_path
++                    (0, Item::DiagnosticItem(item)) => (
++                        format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"),
++                        has_ctor,
++                    ),
++                    (0, Item::LangItem(item)) => (
++                        format!("{cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some({def_snip})"),
++                        has_ctor,
++                    ),
++                    // match_trait_method
++                    (1, Item::DiagnosticItem(item)) => {
++                        (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false)
++                    },
++                    // match_type
++                    (2, Item::DiagnosticItem(item)) => (
++                        format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"),
++                        false,
++                    ),
++                    (2, Item::LangItem(item)) => (
++                        format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"),
++                        false,
++                    ),
++                    // is_expr_path_def_path
++                    (3, Item::DiagnosticItem(item)) if has_ctor => (
++                        format!("is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",),
++                        false,
++                    ),
++                    (3, Item::LangItem(item)) if has_ctor => (
++                        format!("is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",),
++                        false,
++                    ),
++                    (3, Item::DiagnosticItem(item)) => (
++                        format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"),
++                        false,
++                    ),
++                    (3, Item::LangItem(item)) => (
++                        format!(
++                            "path_res({cx_snip}, {def_snip}).opt_def_id()\
++                                .map_or(false, |id| {cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some(id))",
++                        ),
++                        false,
++                    ),
++                    _ => return,
++                };
++
++                span_lint_and_then(cx, UNNECESSARY_DEF_PATH, span, msg, |diag| {
++                    diag.span_suggestion(span, "try", sugg, app);
++                    if with_note {
++                        diag.help(
++                            "if this `DefId` came from a constructor expression or pattern then the \
++                                    parent `DefId` should be used instead",
++                        );
++                    }
++                });
++
++                self.linted_def_ids.insert(def_id);
++            }
++        }
++    }
++
++    fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) {
++        let Some(path) = path_from_array(elements) else { return };
++
++        if let Some(def_id) = inherent_def_path_res(cx, &path.iter().map(AsRef::as_ref).collect::<Vec<_>>()) {
++            self.array_def_ids.insert((def_id, span));
++        }
++    }
++}
++
++fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<String>> {
++    match peel_hir_expr_refs(expr).0.kind {
++        ExprKind::Path(ref 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 { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) {
++                    path_to_matched_type(cx, init)
++                } else {
++                    None
++                }
++            },
++            Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path(
++                cx,
++                cx.tcx.eval_static_initializer(def_id).ok()?.inner(),
++                cx.tcx.type_of(def_id),
++            ),
++            Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? {
++                ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => {
++                    read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id))
++                },
++                _ => None,
++            },
++            _ => None,
++        },
++        ExprKind::Array(exprs) => path_from_array(exprs),
++        _ => None,
++    }
++}
++
++fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option<Vec<String>> {
++    let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() {
++        let &alloc = alloc.provenance().values().next()?;
++        if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) {
++            (alloc.inner(), ty)
++        } else {
++            return None;
++        }
++    } else {
++        (alloc, ty)
++    };
++
++    if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind()
++        && let ty::Ref(_, ty, Mutability::Not) = *ty.kind()
++        && ty.is_str()
++    {
++        alloc
++            .provenance()
++            .values()
++            .map(|&alloc| {
++                if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) {
++                    let alloc = alloc.inner();
++                    str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()))
++                        .ok().map(ToOwned::to_owned)
++                } else {
++                    None
++                }
++            })
++            .collect()
++    } else {
++        None
++    }
++}
++
++fn path_from_array(exprs: &[Expr<'_>]) -> Option<Vec<String>> {
++    exprs
++        .iter()
++        .map(|expr| {
++            if let ExprKind::Lit(lit) = &expr.kind {
++                if let LitKind::Str(sym, _) = lit.node {
++                    return Some((*sym.as_str()).to_owned());
++                }
++            }
++
++            None
++        })
++        .collect()
++}
++
++// def_path_res will match field names before anything else, but for this we want to match
++// inherent functions first.
++fn inherent_def_path_res(cx: &LateContext<'_>, segments: &[&str]) -> Option<DefId> {
++    def_path_res(cx, segments, None).opt_def_id().map(|def_id| {
++        if cx.tcx.def_kind(def_id) == DefKind::Field {
++            let method_name = *segments.last().unwrap();
++            cx.tcx
++                .def_key(def_id)
++                .parent
++                .and_then(|parent_idx| {
++                    cx.tcx
++                        .inherent_impls(DefId {
++                            index: parent_idx,
++                            krate: def_id.krate,
++                        })
++                        .iter()
++                        .find_map(|impl_id| {
++                            cx.tcx.associated_items(*impl_id).find_by_name_and_kind(
++                                cx.tcx,
++                                Ident::from_str(method_name),
++                                AssocKind::Fn,
++                                *impl_id,
++                            )
++                        })
++                })
++                .map_or(def_id, |item| item.def_id)
++        } else {
++            def_id
++        }
++    })
++}
++
++fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<Symbol> {
++    if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) {
++        let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id();
++        let item_name = cx
++            .tcx
++            .adt_def(lang_items)
++            .variants()
++            .iter()
++            .nth(lang_item)
++            .unwrap()
++            .name;
++        Some(item_name)
++    } else {
++        None
++    }
++}
index d9b22664fd25b5ce5d5d7956a6142022ddf597be,0000000000000000000000000000000000000000..cd8575c90e86caaad333ab8f76ecce98d82b3ae8
mode 100644,000000..100644
--- /dev/null
@@@ -1,159 -1,0 +1,159 @@@
-                 sess.span_err(attr.span, &format!("`{name}` cannot be an outer attribute"));
 +use rustc_ast::ast;
 +use rustc_ast::attr;
 +use rustc_errors::Applicability;
 +use rustc_session::Session;
 +use rustc_span::sym;
 +use std::str::FromStr;
 +
 +/// Deprecation status of attributes known by Clippy.
 +pub enum DeprecationStatus {
 +    /// Attribute is deprecated
 +    Deprecated,
 +    /// Attribute is deprecated and was replaced by the named attribute
 +    Replaced(&'static str),
 +    None,
 +}
 +
 +#[rustfmt::skip]
 +pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[
 +    ("author",                DeprecationStatus::None),
 +    ("version",               DeprecationStatus::None),
 +    ("cognitive_complexity",  DeprecationStatus::None),
 +    ("cyclomatic_complexity", DeprecationStatus::Replaced("cognitive_complexity")),
 +    ("dump",                  DeprecationStatus::None),
 +    ("msrv",                  DeprecationStatus::None),
 +    ("has_significant_drop",  DeprecationStatus::None),
 +];
 +
 +pub struct LimitStack {
 +    stack: Vec<u64>,
 +}
 +
 +impl Drop for LimitStack {
 +    fn drop(&mut self) {
 +        assert_eq!(self.stack.len(), 1);
 +    }
 +}
 +
 +impl LimitStack {
 +    #[must_use]
 +    pub fn new(limit: u64) -> Self {
 +        Self { stack: vec![limit] }
 +    }
 +    pub fn limit(&self) -> u64 {
 +        *self.stack.last().expect("there should always be a value in the stack")
 +    }
 +    pub fn push_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
 +        let stack = &mut self.stack;
 +        parse_attrs(sess, attrs, name, |val| stack.push(val));
 +    }
 +    pub fn pop_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
 +        let stack = &mut self.stack;
 +        parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val)));
 +    }
 +}
 +
 +pub fn get_attr<'a>(
 +    sess: &'a Session,
 +    attrs: &'a [ast::Attribute],
 +    name: &'static str,
 +) -> impl Iterator<Item = &'a ast::Attribute> {
 +    attrs.iter().filter(move |attr| {
 +        let attr = if let ast::AttrKind::Normal(ref normal) = attr.kind {
 +            &normal.item
 +        } else {
 +            return false;
 +        };
 +        let attr_segments = &attr.path.segments;
 +        if attr_segments.len() == 2 && attr_segments[0].ident.name == sym::clippy {
 +            BUILTIN_ATTRIBUTES
 +                .iter()
 +                .find_map(|&(builtin_name, ref deprecation_status)| {
 +                    if attr_segments[1].ident.name.as_str() == builtin_name {
 +                        Some(deprecation_status)
 +                    } else {
 +                        None
 +                    }
 +                })
 +                .map_or_else(
 +                    || {
 +                        sess.span_err(attr_segments[1].ident.span, "usage of unknown attribute");
 +                        false
 +                    },
 +                    |deprecation_status| {
 +                        let mut diag =
 +                            sess.struct_span_err(attr_segments[1].ident.span, "usage of deprecated attribute");
 +                        match *deprecation_status {
 +                            DeprecationStatus::Deprecated => {
 +                                diag.emit();
 +                                false
 +                            },
 +                            DeprecationStatus::Replaced(new_name) => {
 +                                diag.span_suggestion(
 +                                    attr_segments[1].ident.span,
 +                                    "consider using",
 +                                    new_name,
 +                                    Applicability::MachineApplicable,
 +                                );
 +                                diag.emit();
 +                                false
 +                            },
 +                            DeprecationStatus::None => {
 +                                diag.cancel();
 +                                attr_segments[1].ident.name.as_str() == name
 +                            },
 +                        }
 +                    },
 +                )
 +        } else {
 +            false
 +        }
 +    })
 +}
 +
 +fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'static str, mut f: F) {
 +    for attr in get_attr(sess, attrs, name) {
 +        if let Some(ref value) = attr.value_str() {
 +            if let Ok(value) = FromStr::from_str(value.as_str()) {
 +                f(value);
 +            } else {
 +                sess.span_err(attr.span, "not a number");
 +            }
 +        } else {
 +            sess.span_err(attr.span, "bad clippy attribute");
 +        }
 +    }
 +}
 +
 +pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option<ast::Attribute> {
 +    let mut unique_attr = None;
 +    for attr in get_attr(sess, attrs, name) {
 +        match attr.style {
 +            ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()),
 +            ast::AttrStyle::Inner => {
 +                sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times"))
 +                    .span_note(unique_attr.as_ref().unwrap().span, "first definition found here")
 +                    .emit();
 +            },
 +            ast::AttrStyle::Outer => {
++                sess.span_err(attr.span, format!("`{name}` cannot be an outer attribute"));
 +            },
 +        }
 +    }
 +    unique_attr
 +}
 +
 +/// Return true if the attributes contain any of `proc_macro`,
 +/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
 +pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool {
 +    attrs.iter().any(|attr| sess.is_proc_macro_attr(attr))
 +}
 +
 +/// Return true if the attributes contain `#[doc(hidden)]`
 +pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool {
 +    attrs
 +        .iter()
 +        .filter(|attr| attr.has_name(sym::doc))
 +        .filter_map(ast::Attribute::meta_item_list)
 +        .any(|l| attr::list_contains_name(&l, sym::hidden))
 +}
index fa6766f7cfe19ee1ca1c39d572f41e3145133b6b,0000000000000000000000000000000000000000..07e4ef6a2fef3c4c8f6ed67d9ad1cb80205cc36e
mode 100644,000000..100644
--- /dev/null
@@@ -1,650 -1,0 +1,682 @@@
-             (&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()))),
 +#![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;
 +use rustc_middle::mir::interpret::Scalar;
 +use rustc_middle::ty::SubstsRef;
 +use rustc_middle::ty::{self, EarlyBinder, FloatTy, ScalarInt, Ty, TyCtxt};
 +use rustc_middle::{bug, span_bug};
 +use rustc_span::symbol::Symbol;
 +use std::cmp::Ordering::{self, Equal};
 +use std::hash::{Hash, Hasher};
 +use std::iter;
 +
 +/// A `LitKind`-like enum to fold constant `Expr`s into.
 +#[derive(Debug, Clone)]
 +pub enum Constant {
 +    /// A `String` (e.g., "abc").
 +    Str(String),
 +    /// A binary string (e.g., `b"abc"`).
 +    Binary(Lrc<[u8]>),
 +    /// A single `char` (e.g., `'a'`).
 +    Char(char),
 +    /// An integer's bit representation.
 +    Int(u128),
 +    /// An `f32`.
 +    F32(f32),
 +    /// An `f64`.
 +    F64(f64),
 +    /// `true` or `false`.
 +    Bool(bool),
 +    /// An array of constants.
 +    Vec(Vec<Constant>),
 +    /// Also an array, but with only one constant, repeated N times.
 +    Repeat(Box<Constant>, u64),
 +    /// A tuple of constants.
 +    Tuple(Vec<Constant>),
 +    /// A raw pointer.
 +    RawPtr(u128),
 +    /// A reference
 +    Ref(Box<Constant>),
 +    /// A literal with syntax error.
 +    Err,
 +}
 +
 +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 => {},
 +        }
 +    }
 +}
 +
 +impl Constant {
 +    pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
 +        match (left, right) {
 +            (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
 +            (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
 +            (&Self::Int(l), &Self::Int(r)) => match *cmp_type.kind() {
 +                ty::Int(int_ty) => Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))),
 +                ty::Uint(_) => Some(l.cmp(&r)),
 +                _ => bug!("Not an int type"),
 +            },
 +            (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
 +            (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
 +            (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)),
-                 match Self::partial_cmp(tcx, cmp_type, lv, rv) {
++            (&Self::Tuple(ref l), &Self::Tuple(ref r)) if l.len() == r.len() => match *cmp_type.kind() {
++                ty::Tuple(tys) if tys.len() == l.len() => l
++                    .iter()
++                    .zip(r)
++                    .zip(tys)
++                    .map(|((li, ri), cmp_type)| 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()))),
++                _ => None,
++            },
++            (&Self::Vec(ref l), &Self::Vec(ref r)) => {
++                let cmp_type = match *cmp_type.kind() {
++                    ty::Array(ty, _) | ty::Slice(ty) => ty,
++                    _ => return None,
++                };
++                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)) => {
-             (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb),
++                match Self::partial_cmp(
++                    tcx,
++                    match *cmp_type.kind() {
++                        ty::Array(ty, _) => ty,
++                        _ => return None,
++                    },
++                    lv,
++                    rv,
++                ) {
 +                    Some(Equal) => Some(ls.cmp(rs)),
 +                    x => x,
 +                }
 +            },
++            (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(
++                tcx,
++                match *cmp_type.kind() {
++                    ty::Ref(_, ty, _) => ty,
++                    _ => return None,
++                },
++                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 => Constant::Err,
 +    }
 +}
 +
 +pub fn constant<'tcx>(
 +    lcx: &LateContext<'tcx>,
 +    typeck_results: &ty::TypeckResults<'tcx>,
 +    e: &Expr<'_>,
 +) -> Option<(Constant, bool)> {
 +    let mut cx = ConstEvalLateContext {
 +        lcx,
 +        typeck_results,
 +        param_env: lcx.param_env,
 +        needed_resolution: false,
 +        substs: lcx.tcx.intern_substs(&[]),
 +    };
 +    cx.expr(e).map(|cst| (cst, cx.needed_resolution))
 +}
 +
 +pub fn constant_simple<'tcx>(
 +    lcx: &LateContext<'tcx>,
 +    typeck_results: &ty::TypeckResults<'tcx>,
 +    e: &Expr<'_>,
 +) -> Option<Constant> {
 +    constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
 +}
 +
 +pub fn constant_full_int<'tcx>(
 +    lcx: &LateContext<'tcx>,
 +    typeck_results: &ty::TypeckResults<'tcx>,
 +    e: &Expr<'_>,
 +) -> Option<FullInt> {
 +    constant_simple(lcx, typeck_results, e)?.int_value(lcx, typeck_results.expr_ty(e))
 +}
 +
 +#[derive(Copy, Clone, Debug, Eq)]
 +pub enum FullInt {
 +    S(i128),
 +    U(u128),
 +}
 +
 +impl PartialEq for FullInt {
 +    #[must_use]
 +    fn eq(&self, other: &Self) -> bool {
 +        self.cmp(other) == Ordering::Equal
 +    }
 +}
 +
 +impl PartialOrd for FullInt {
 +    #[must_use]
 +    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
 +        Some(self.cmp(other))
 +    }
 +}
 +
 +impl Ord for FullInt {
 +    #[must_use]
 +    fn cmp(&self, other: &Self) -> Ordering {
 +        use FullInt::{S, U};
 +
 +        fn cmp_s_u(s: i128, u: u128) -> Ordering {
 +            u128::try_from(s).map_or(Ordering::Less, |x| x.cmp(&u))
 +        }
 +
 +        match (*self, *other) {
 +            (S(s), S(o)) => s.cmp(&o),
 +            (U(s), U(o)) => s.cmp(&o),
 +            (S(s), U(o)) => cmp_s_u(s, o),
 +            (U(s), S(o)) => cmp_s_u(o, s).reverse(),
 +        }
 +    }
 +}
 +
 +/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`.
 +pub fn constant_context<'a, 'tcx>(
 +    lcx: &'a LateContext<'tcx>,
 +    typeck_results: &'a ty::TypeckResults<'tcx>,
 +) -> ConstEvalLateContext<'a, 'tcx> {
 +    ConstEvalLateContext {
 +        lcx,
 +        typeck_results,
 +        param_env: lcx.param_env,
 +        needed_resolution: false,
 +        substs: lcx.tcx.intern_substs(&[]),
 +    }
 +}
 +
 +pub struct ConstEvalLateContext<'a, 'tcx> {
 +    lcx: &'a LateContext<'tcx>,
 +    typeck_results: &'a ty::TypeckResults<'tcx>,
 +    param_env: ty::ParamEnv<'tcx>,
 +    needed_resolution: bool,
 +    substs: SubstsRef<'tcx>,
 +}
 +
 +impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
 +    /// Simple constant folding: Insert an expression, get a constant or none.
 +    pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> {
 +        match e.kind {
 +            ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
 +            ExprKind::Block(block, _) => self.block(block),
 +            ExprKind::Lit(ref lit) => {
 +                if is_direct_expn_of(e.span, "cfg").is_some() {
 +                    None
 +                } else {
 +                    Some(lit_to_mir_constant(&lit.node, self.typeck_results.expr_ty_opt(e)))
 +                }
 +            },
 +            ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec),
 +            ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple),
 +            ExprKind::Repeat(value, _) => {
 +                let n = match self.typeck_results.expr_ty(e).kind() {
 +                    ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?,
 +                    _ => span_bug!(e.span, "typeck error"),
 +                };
 +                self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
 +            },
 +            ExprKind::Unary(op, operand) => self.expr(operand).and_then(|o| match op {
 +                UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)),
 +                UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)),
 +                UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
 +            }),
 +            ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
 +            ExprKind::Binary(op, left, right) => self.binop(op, left, right),
 +            ExprKind::Call(callee, args) => {
 +                // We only handle a few const functions for now.
 +                if_chain! {
 +                    if args.is_empty();
 +                    if let ExprKind::Path(qpath) = &callee.kind;
 +                    let res = self.typeck_results.qpath_res(qpath, callee.hir_id);
 +                    if let Some(def_id) = res.opt_def_id();
 +                    let def_path = self.lcx.get_def_path(def_id);
 +                    let def_path: Vec<&str> = def_path.iter().take(4).map(Symbol::as_str).collect();
 +                    if let ["core", "num", int_impl, "max_value"] = *def_path;
 +                    then {
 +                        let value = match int_impl {
 +                            "<impl i8>" => i8::MAX as u128,
 +                            "<impl i16>" => i16::MAX as u128,
 +                            "<impl i32>" => i32::MAX as u128,
 +                            "<impl i64>" => i64::MAX as u128,
 +                            "<impl i128>" => i128::MAX as u128,
 +                            _ => return None,
 +                        };
 +                        Some(Constant::Int(value))
 +                    } else {
 +                        None
 +                    }
 +                }
 +            },
 +            ExprKind::Index(arr, index) => self.index(arr, index),
 +            ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))),
 +            // TODO: add other expressions.
 +            _ => None,
 +        }
 +    }
 +
 +    #[expect(clippy::cast_possible_wrap)]
 +    fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
 +        use self::Constant::{Bool, Int};
 +        match *o {
 +            Bool(b) => Some(Bool(!b)),
 +            Int(value) => {
 +                let value = !value;
 +                match *ty.kind() {
 +                    ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))),
 +                    ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))),
 +                    _ => None,
 +                }
 +            },
 +            _ => None,
 +        }
 +    }
 +
 +    fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
 +        use self::Constant::{Int, F32, F64};
 +        match *o {
 +            Int(value) => {
 +                let ity = match *ty.kind() {
 +                    ty::Int(ity) => ity,
 +                    _ => return None,
 +                };
 +                // sign extend
 +                let value = sext(self.lcx.tcx, value, ity);
 +                let value = value.checked_neg()?;
 +                // clear unused bits
 +                Some(Int(unsext(self.lcx.tcx, value, ity)))
 +            },
 +            F32(f) => Some(F32(-f)),
 +            F64(f) => Some(F64(-f)),
 +            _ => None,
 +        }
 +    }
 +
 +    /// Create `Some(Vec![..])` of all constants, unless there is any
 +    /// non-constant part.
 +    fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant>> {
 +        vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
 +    }
 +
 +    /// Lookup a possibly constant expression from an `ExprKind::Path`.
 +    fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<Constant> {
 +        let res = self.typeck_results.qpath_res(qpath, id);
 +        match res {
 +            Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
 +                // Check if this constant is based on `cfg!(..)`,
 +                // which is NOT constant for our purposes.
 +                if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id) &&
 +                let Node::Item(&Item {
 +                    kind: ItemKind::Const(_, body_id),
 +                    ..
 +                }) = node &&
 +                let Node::Expr(&Expr {
 +                    kind: ExprKind::Lit(_),
 +                    span,
 +                    ..
 +                }) = self.lcx.tcx.hir().get(body_id.hir_id) &&
 +                is_direct_expn_of(span, "cfg").is_some() {
 +                    return None;
 +                }
 +
 +                let substs = self.typeck_results.node_substs(id);
 +                let substs = if self.substs.is_empty() {
 +                    substs
 +                } else {
 +                    EarlyBinder(substs).subst(self.lcx.tcx, self.substs)
 +                };
 +
 +                let result = self
 +                    .lcx
 +                    .tcx
 +                    .const_eval_resolve(
 +                        self.param_env,
 +                        mir::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs),
 +                        None,
 +                    )
 +                    .ok()
 +                    .map(|val| rustc_middle::mir::ConstantKind::from_value(val, ty))?;
 +                let result = miri_to_const(self.lcx.tcx, 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().ok()?).map(zext),
 +                        BinOpKind::Shl => l.checked_shl(r.try_into().ok()?).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().ok()?).map(Constant::Int),
 +                    BinOpKind::Shl => l.checked_shl(r.try_into().ok()?).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<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) -> Option<Constant> {
 +    use rustc_middle::mir::interpret::ConstValue;
 +    match result {
 +        mir::ConstantKind::Val(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,
 +            }
 +        },
 +        mir::ConstantKind::Val(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,
 +        },
 +        mir::ConstantKind::Val(ConstValue::ByRef { alloc, offset: _ }, _) => match result.ty().kind() {
 +            ty::Array(sub_type, len) => match sub_type.kind() {
 +                ty::Float(FloatTy::F32) => match len.kind().try_to_machine_usize(tcx) {
 +                    Some(len) => alloc
 +                        .inner()
 +                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap()))
 +                        .to_owned()
 +                        .array_chunks::<4>()
 +                        .map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk))))
 +                        .collect::<Option<Vec<Constant>>>()
 +                        .map(Constant::Vec),
 +                    _ => None,
 +                },
 +                ty::Float(FloatTy::F64) => match len.kind().try_to_machine_usize(tcx) {
 +                    Some(len) => alloc
 +                        .inner()
 +                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap()))
 +                        .to_owned()
 +                        .array_chunks::<8>()
 +                        .map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk))))
 +                        .collect::<Option<Vec<Constant>>>()
 +                        .map(Constant::Vec),
 +                    _ => None,
 +                },
 +                // FIXME: implement other array type conversions.
 +                _ => None,
 +            },
 +            _ => None,
 +        },
 +        // FIXME: implement other conversions.
 +        _ => None,
 +    }
 +}
index 8724a4cd651debb4103aeb8f9541807ee204b953,0000000000000000000000000000000000000000..95b3e651e2b539db6d3ba8f27426df10ae759636
mode 100644,000000..100644
--- /dev/null
@@@ -1,240 -1,0 +1,240 @@@
-                             self.eagerness = Lazy;
 +//! Utilities for evaluating whether eagerly evaluated expressions can be made lazy and vice versa.
 +//!
 +//! Things to consider:
 +//!  - has the expression side-effects?
 +//!  - is the expression computationally expensive?
 +//!
 +//! See lints:
 +//!  - unnecessary-lazy-evaluations
 +//!  - or-fun-call
 +//!  - option-if-let-else
 +
 +use crate::ty::{all_predicates_of, is_copy};
 +use crate::visitors::is_const_evaluatable;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{def_id::DefId, Block, Expr, ExprKind, QPath, UnOp};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, PredicateKind};
 +use rustc_span::{sym, Symbol};
 +use std::cmp;
 +use std::ops;
 +
 +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
 +enum EagernessSuggestion {
 +    // The expression is cheap and should be evaluated eagerly
 +    Eager,
 +    // The expression may be cheap, so don't suggested lazy evaluation; or the expression may not be safe to switch to
 +    // eager evaluation.
 +    NoChange,
 +    // The expression is likely expensive and should be evaluated lazily.
 +    Lazy,
 +    // The expression cannot be placed into a closure.
 +    ForceNoChange,
 +}
 +impl ops::BitOr for EagernessSuggestion {
 +    type Output = Self;
 +    fn bitor(self, rhs: Self) -> Self {
 +        cmp::max(self, rhs)
 +    }
 +}
 +impl ops::BitOrAssign for EagernessSuggestion {
 +    fn bitor_assign(&mut self, rhs: Self) {
 +        *self = *self | rhs;
 +    }
 +}
 +
 +/// Determine the eagerness of the given function call.
 +fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion {
 +    use EagernessSuggestion::{Eager, Lazy, NoChange};
 +    let name = name.as_str();
 +
 +    let ty = match cx.tcx.impl_of_method(fn_id) {
 +        Some(id) => cx.tcx.type_of(id),
 +        None => return Lazy,
 +    };
 +
 +    if (name.starts_with("as_") || name == "len" || name == "is_empty") && have_one_arg {
 +        if matches!(
 +            cx.tcx.crate_name(fn_id.krate),
 +            sym::std | sym::core | sym::alloc | sym::proc_macro
 +        ) {
 +            Eager
 +        } else {
 +            NoChange
 +        }
 +    } else if let ty::Adt(def, subs) = ty.kind() {
 +        // Types where the only fields are generic types (or references to) with no trait bounds other
 +        // than marker traits.
 +        // Due to the limited operations on these types functions should be fairly cheap.
 +        if def
 +            .variants()
 +            .iter()
 +            .flat_map(|v| v.fields.iter())
 +            .any(|x| matches!(cx.tcx.type_of(x.did).peel_refs().kind(), ty::Param(_)))
 +            && all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() {
 +                PredicateKind::Trait(pred) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker,
 +                _ => true,
 +            })
 +            && subs.types().all(|x| matches!(x.peel_refs().kind(), ty::Param(_)))
 +        {
 +            // Limit the function to either `(self) -> bool` or `(&self) -> bool`
 +            match &**cx.tcx.fn_sig(fn_id).skip_binder().inputs_and_output {
 +                [arg, res] if !arg.is_mutable_ptr() && arg.peel_refs() == ty && res.is_bool() => NoChange,
 +                _ => Lazy,
 +            }
 +        } else {
 +            Lazy
 +        }
 +    } else {
 +        Lazy
 +    }
 +}
 +
 +#[expect(clippy::too_many_lines)]
 +fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion {
 +    struct V<'cx, 'tcx> {
 +        cx: &'cx LateContext<'tcx>,
 +        eagerness: EagernessSuggestion,
 +    }
 +
 +    impl<'cx, 'tcx> Visitor<'tcx> for V<'cx, 'tcx> {
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            use EagernessSuggestion::{ForceNoChange, Lazy, NoChange};
 +            if self.eagerness == ForceNoChange {
 +                return;
 +            }
 +            match e.kind {
 +                ExprKind::Call(
 +                    &Expr {
 +                        kind: ExprKind::Path(ref path),
 +                        hir_id,
 +                        ..
 +                    },
 +                    args,
 +                ) => match self.cx.qpath_res(path, hir_id) {
 +                    Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => {
 +                        if self
 +                            .cx
 +                            .typeck_results()
 +                            .expr_ty(e)
 +                            .has_significant_drop(self.cx.tcx, self.cx.param_env)
 +                        {
++                            self.eagerness = ForceNoChange;
 +                            return;
 +                        }
 +                    },
 +                    Res::Def(_, id) if self.cx.tcx.is_promotable_const_fn(id) => (),
 +                    // No need to walk the arguments here, `is_const_evaluatable` already did
 +                    Res::Def(..) if is_const_evaluatable(self.cx, e) => {
 +                        self.eagerness |= NoChange;
 +                        return;
 +                    },
 +                    Res::Def(_, id) => match path {
 +                        QPath::Resolved(_, p) => {
 +                            self.eagerness |=
 +                                fn_eagerness(self.cx, id, p.segments.last().unwrap().ident.name, !args.is_empty());
 +                        },
 +                        QPath::TypeRelative(_, name) => {
 +                            self.eagerness |= fn_eagerness(self.cx, id, name.ident.name, !args.is_empty());
 +                        },
 +                        QPath::LangItem(..) => self.eagerness = Lazy,
 +                    },
 +                    _ => self.eagerness = Lazy,
 +                },
 +                // No need to walk the arguments here, `is_const_evaluatable` already did
 +                ExprKind::MethodCall(..) if is_const_evaluatable(self.cx, e) => {
 +                    self.eagerness |= NoChange;
 +                    return;
 +                },
 +                ExprKind::MethodCall(name, ..) => {
 +                    self.eagerness |= self
 +                        .cx
 +                        .typeck_results()
 +                        .type_dependent_def_id(e.hir_id)
 +                        .map_or(Lazy, |id| fn_eagerness(self.cx, id, name.ident.name, true));
 +                },
 +                ExprKind::Index(_, e) => {
 +                    let ty = self.cx.typeck_results().expr_ty_adjusted(e);
 +                    if is_copy(self.cx, ty) && !ty.is_ref() {
 +                        self.eagerness |= NoChange;
 +                    } else {
 +                        self.eagerness = Lazy;
 +                    }
 +                },
 +
 +                // Dereferences should be cheap, but dereferencing a raw pointer earlier may not be safe.
 +                ExprKind::Unary(UnOp::Deref, e) if !self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => (),
 +                ExprKind::Unary(UnOp::Deref, _) => self.eagerness |= NoChange,
 +
 +                ExprKind::Unary(_, e)
 +                    if matches!(
 +                        self.cx.typeck_results().expr_ty(e).kind(),
 +                        ty::Bool | ty::Int(_) | ty::Uint(_),
 +                    ) => {},
 +                ExprKind::Binary(_, lhs, rhs)
 +                    if self.cx.typeck_results().expr_ty(lhs).is_primitive()
 +                        && self.cx.typeck_results().expr_ty(rhs).is_primitive() => {},
 +
 +                // Can't be moved into a closure
 +                ExprKind::Break(..)
 +                | ExprKind::Continue(_)
 +                | ExprKind::Ret(_)
 +                | ExprKind::InlineAsm(_)
 +                | ExprKind::Yield(..)
 +                | ExprKind::Err => {
 +                    self.eagerness = ForceNoChange;
 +                    return;
 +                },
 +
 +                // Memory allocation, custom operator, loop, or call to an unknown function
 +                ExprKind::Box(_)
 +                | ExprKind::Unary(..)
 +                | ExprKind::Binary(..)
 +                | ExprKind::Loop(..)
 +                | ExprKind::Call(..) => self.eagerness = Lazy,
 +
 +                ExprKind::ConstBlock(_)
 +                | ExprKind::Array(_)
 +                | ExprKind::Tup(_)
 +                | ExprKind::Lit(_)
 +                | ExprKind::Cast(..)
 +                | ExprKind::Type(..)
 +                | ExprKind::DropTemps(_)
 +                | ExprKind::Let(..)
 +                | ExprKind::If(..)
 +                | ExprKind::Match(..)
 +                | ExprKind::Closure { .. }
 +                | ExprKind::Field(..)
 +                | ExprKind::Path(_)
 +                | ExprKind::AddrOf(..)
 +                | ExprKind::Struct(..)
 +                | ExprKind::Repeat(..)
 +                | ExprKind::Block(Block { stmts: [], .. }, _) => (),
 +
 +                // Assignment might be to a local defined earlier, so don't eagerly evaluate.
 +                // Blocks with multiple statements might be expensive, so don't eagerly evaluate.
 +                // TODO: Actually check if either of these are true here.
 +                ExprKind::Assign(..) | ExprKind::AssignOp(..) | ExprKind::Block(..) => self.eagerness |= NoChange,
 +            }
 +            walk_expr(self, e);
 +        }
 +    }
 +
 +    let mut v = V {
 +        cx,
 +        eagerness: EagernessSuggestion::Eager,
 +    };
 +    v.visit_expr(e);
 +    v.eagerness
 +}
 +
 +/// Whether the given expression should be changed to evaluate eagerly
 +pub fn switch_to_eager_eval<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
 +    expr_eagerness(cx, expr) == EagernessSuggestion::Eager
 +}
 +
 +/// Whether the given expression should be changed to evaluate lazily
 +pub fn switch_to_lazy_eval<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
 +    expr_eagerness(cx, expr) == EagernessSuggestion::Lazy
 +}
index 7e42fcc6569b692dd86c65f9d6f5a987241d4735,0000000000000000000000000000000000000000..052db3f3a039182533c8d521d864ddf0f3767e2a
mode 100644,000000..100644
--- /dev/null
@@@ -1,2425 -1,0 +1,2469 @@@
-             sess.span_err(span, &format!("`{msrv}` is not a valid Rust version"));
 +#![feature(array_chunks)]
 +#![feature(box_patterns)]
 +#![feature(control_flow_enum)]
 +#![feature(let_chains)]
 +#![feature(lint_reasons)]
 +#![feature(never_type)]
 +#![feature(once_cell)]
 +#![feature(rustc_private)]
 +#![recursion_limit = "512"]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
 +// warn on the same lints as `clippy_lints`
 +#![warn(trivial_casts, trivial_numeric_casts)]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_ast;
 +extern crate rustc_ast_pretty;
 +extern crate rustc_attr;
 +extern crate rustc_data_structures;
 +extern crate rustc_errors;
 +extern crate rustc_hir;
 +extern crate rustc_hir_typeck;
++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_format;
 +extern crate rustc_session;
 +extern crate rustc_span;
 +extern crate rustc_target;
 +extern crate rustc_trait_selection;
 +
 +#[macro_use]
 +pub mod sym_helper;
 +
 +pub mod ast_utils;
 +pub mod attrs;
 +mod check_proc_macro;
 +pub mod comparisons;
 +pub mod consts;
 +pub mod diagnostics;
 +pub mod eager_or_lazy;
 +pub mod higher;
 +mod hir_utils;
 +pub mod macros;
++pub mod mir;
 +pub mod msrvs;
 +pub mod numeric_literal;
 +pub mod paths;
 +pub mod ptr;
 +pub mod qualify_min_const_fn;
 +pub mod source;
 +pub mod str_utils;
 +pub mod sugg;
 +pub mod ty;
 +pub mod usage;
 +pub mod visitors;
 +
 +pub use self::attrs::*;
 +pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
 +pub use self::hir_utils::{
 +    both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
 +};
 +
 +use core::ops::ControlFlow;
 +use std::collections::hash_map::Entry;
 +use std::hash::BuildHasherDefault;
 +use std::sync::OnceLock;
 +use std::sync::{Mutex, MutexGuard};
 +
 +use if_chain::if_chain;
 +use rustc_ast::ast::{self, LitKind};
 +use rustc_ast::Attribute;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_data_structures::unhash::UnhashMap;
 +use rustc_hir as hir;
 +use rustc_hir::def::{DefKind, Namespace, Res};
 +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
 +use rustc_hir::hir_id::{HirIdMap, HirIdSet};
 +use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 +use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
 +use rustc_hir::{
 +    def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination, Expr,
 +    ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource,
 +    Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind,
 +    TraitRef, TyKind, UnOp,
 +};
 +use rustc_lexer::{tokenize, TokenKind};
 +use rustc_lint::{LateContext, Level, Lint, LintContext};
 +use rustc_middle::hir::place::PlaceBase;
 +use rustc_middle::ty as rustc_ty;
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 +use rustc_middle::ty::binding::BindingMode;
 +use rustc_middle::ty::fast_reject::SimplifiedTypeGen::{
 +    ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType,
 +    PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType,
 +};
 +use rustc_middle::ty::{
 +    layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture,
 +};
 +use rustc_middle::ty::{FloatTy, IntTy, UintTy};
 +use rustc_semver::RustcVersion;
 +use rustc_session::Session;
 +use rustc_span::hygiene::{ExpnKind, MacroKind};
 +use rustc_span::source_map::original_sp;
 +use rustc_span::source_map::SourceMap;
 +use rustc_span::sym;
 +use rustc_span::symbol::{kw, Symbol};
 +use rustc_span::{Span, DUMMY_SP};
 +use rustc_target::abi::Integer;
 +
 +use crate::consts::{constant, Constant};
 +use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
 +use crate::visitors::for_each_expr;
 +
 +pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
 +    if let Ok(version) = RustcVersion::parse(msrv) {
 +        return Some(version);
 +    } else if let Some(sess) = sess {
 +        if let Some(span) = span {
-         ExprKind::Call(repl_func, _) => is_default_equivalent_call(cx, repl_func),
++            sess.span_err(span, format!("`{msrv}` is not a valid Rust version"));
 +        }
 +    }
 +    None
 +}
 +
 +pub fn meets_msrv(msrv: Option<RustcVersion>, lint_msrv: RustcVersion) -> bool {
 +    msrv.map_or(true, |msrv| msrv.meets(lint_msrv))
 +}
 +
 +#[macro_export]
 +macro_rules! extract_msrv_attr {
 +    ($context:ident) => {
 +        fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
 +            let sess = rustc_lint::LintContext::sess(cx);
 +            match $crate::get_unique_inner_attr(sess, attrs, "msrv") {
 +                Some(msrv_attr) => {
 +                    if let Some(msrv) = msrv_attr.value_str() {
 +                        self.msrv = $crate::parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
 +                    } else {
 +                        sess.span_err(msrv_attr.span, "bad clippy attribute");
 +                    }
 +                },
 +                _ => (),
 +            }
 +        }
 +    };
 +}
 +
 +/// If the given expression is a local binding, find the initializer expression.
 +/// If that initializer expression is another local binding, find its initializer again.
 +/// This process repeats as long as possible (but usually no more than once). Initializer
 +/// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
 +/// instead.
 +///
 +/// Examples:
 +/// ```
 +/// let abc = 1;
 +/// //        ^ output
 +/// let def = abc;
 +/// dbg!(def);
 +/// //   ^^^ input
 +///
 +/// // or...
 +/// let abc = 1;
 +/// let def = abc + 2;
 +/// //        ^^^^^^^ output
 +/// dbg!(def);
 +/// //   ^^^ input
 +/// ```
 +pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
 +    while let Some(init) = path_to_local(expr)
 +        .and_then(|id| find_binding_init(cx, id))
 +        .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
 +    {
 +        expr = init;
 +    }
 +    expr
 +}
 +
 +/// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
 +/// By only considering immutable bindings, we guarantee that the returned expression represents the
 +/// value of the binding wherever it is referenced.
 +///
 +/// Example: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned.
 +/// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the
 +/// canonical binding `HirId`.
 +pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
 +    let hir = cx.tcx.hir();
 +    if_chain! {
 +        if let Some(Node::Pat(pat)) = hir.find(hir_id);
 +        if matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..));
 +        let parent = hir.get_parent_node(hir_id);
 +        if let Some(Node::Local(local)) = hir.find(parent);
 +        then {
 +            return local.init;
 +        }
 +    }
 +    None
 +}
 +
 +/// Returns `true` if the given `NodeId` is inside a constant context
 +///
 +/// # Example
 +///
 +/// ```rust,ignore
 +/// if in_constant(cx, expr.hir_id) {
 +///     // Do something
 +/// }
 +/// ```
 +pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
 +    let parent_id = cx.tcx.hir().get_parent_item(id).def_id;
 +    match cx.tcx.hir().get_by_def_id(parent_id) {
 +        Node::Item(&Item {
 +            kind: ItemKind::Const(..) | ItemKind::Static(..),
 +            ..
 +        })
 +        | Node::TraitItem(&TraitItem {
 +            kind: TraitItemKind::Const(..),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Const(..),
 +            ..
 +        })
 +        | Node::AnonConst(_) => true,
 +        Node::Item(&Item {
 +            kind: ItemKind::Fn(ref sig, ..),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Fn(ref sig, _),
 +            ..
 +        }) => sig.header.constness == Constness::Const,
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if a `Res` refers to a constructor of a `LangItem`
 +/// For example, use this to check whether a function call or a pattern is `Some(..)`.
 +pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool {
 +    if let Res::Def(DefKind::Ctor(..), id) = res
 +        && let Ok(lang_id) = cx.tcx.lang_items().require(lang_item)
 +        && let Some(id) = cx.tcx.opt_parent(id)
 +    {
 +        id == lang_id
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn is_res_diagnostic_ctor(cx: &LateContext<'_>, res: Res, diag_item: Symbol) -> bool {
 +    if let Res::Def(DefKind::Ctor(..), id) = res
 +        && let Some(id) = cx.tcx.opt_parent(id)
 +    {
 +        cx.tcx.is_diagnostic_item(diag_item, id)
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Checks if a `QPath` resolves to a constructor of a diagnostic item.
 +pub fn is_diagnostic_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, diagnostic_item: Symbol) -> bool {
 +    if let QPath::Resolved(_, path) = qpath {
 +        if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
 +            return cx.tcx.is_diagnostic_item(diagnostic_item, cx.tcx.parent(ctor_id));
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks if the `DefId` matches the given diagnostic item or it's constructor.
 +pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
 +    let did = match cx.tcx.def_kind(did) {
 +        DefKind::Ctor(..) => cx.tcx.parent(did),
 +        // Constructors for types in external crates seem to have `DefKind::Variant`
 +        DefKind::Variant => match cx.tcx.opt_parent(did) {
 +            Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
 +            _ => did,
 +        },
 +        _ => did,
 +    };
 +
 +    cx.tcx.is_diagnostic_item(item, did)
 +}
 +
 +/// Checks if the `DefId` matches the given `LangItem` or it's constructor.
 +pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
 +    let did = match cx.tcx.def_kind(did) {
 +        DefKind::Ctor(..) => cx.tcx.parent(did),
 +        // Constructors for types in external crates seem to have `DefKind::Variant`
 +        DefKind::Variant => match cx.tcx.opt_parent(did) {
 +            Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
 +            _ => did,
 +        },
 +        _ => did,
 +    };
 +
 +    cx.tcx.lang_items().require(item).map_or(false, |id| id == did)
 +}
 +
 +pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
 +    matches!(
 +        expr.kind,
 +        ExprKind::Block(
 +            Block {
 +                stmts: [],
 +                expr: None,
 +                ..
 +            },
 +            _
 +        ) | ExprKind::Tup([])
 +    )
 +}
 +
 +/// Checks if given pattern is a wildcard (`_`)
 +pub fn is_wild(pat: &Pat<'_>) -> bool {
 +    matches!(pat.kind, PatKind::Wild)
 +}
 +
 +/// Checks if the method call given in `expr` belongs to the given trait.
 +/// This is a deprecated function, consider using [`is_trait_method`].
 +pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
 +    let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
 +    let trt_id = cx.tcx.trait_of_item(def_id);
 +    trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
 +}
 +
 +/// Checks if a method is defined in an impl of a diagnostic item
 +pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
 +    if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
 +        if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
 +            return cx.tcx.is_diagnostic_item(diag_item, adt.did());
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks if a method is in a diagnostic item trait
 +pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
 +    if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
 +        return cx.tcx.is_diagnostic_item(diag_item, trait_did);
 +    }
 +    false
 +}
 +
 +/// Checks if the method call given in `expr` belongs to the given trait.
 +pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
 +    cx.typeck_results()
 +        .type_dependent_def_id(expr.hir_id)
 +        .map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
 +}
 +
 +/// Checks if the given expression is a path referring an item on the trait
 +/// that is marked with the given diagnostic item.
 +///
 +/// For checking method call expressions instead of path expressions, use
 +/// [`is_trait_method`].
 +///
 +/// For example, this can be used to find if an expression like `u64::default`
 +/// refers to an item of the trait `Default`, which is associated with the
 +/// `diag_item` of `sym::Default`.
 +pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
 +    if let hir::ExprKind::Path(ref qpath) = expr.kind {
 +        cx.qpath_res(qpath, expr.hir_id)
 +            .opt_def_id()
 +            .map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item))
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
 +    match *path {
 +        QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
 +        QPath::TypeRelative(_, seg) => seg,
 +        QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
 +    }
 +}
 +
 +pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
 +    last_path_segment(qpath)
 +        .args
 +        .map_or(&[][..], |a| a.args)
 +        .iter()
 +        .filter_map(|a| match a {
 +            hir::GenericArg::Type(ty) => Some(*ty),
 +            _ => None,
 +        })
 +}
 +
 +/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
 +/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
 +/// `QPath::Resolved.1.res.opt_def_id()`.
 +///
 +/// Matches a `QPath` against a slice of segment string literals.
 +///
 +/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
 +/// `rustc_hir::QPath`.
 +///
 +/// # Examples
 +/// ```rust,ignore
 +/// match_qpath(path, &["std", "rt", "begin_unwind"])
 +/// ```
 +pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
 +    match *path {
 +        QPath::Resolved(_, path) => match_path(path, segments),
 +        QPath::TypeRelative(ty, segment) => match ty.kind {
 +            TyKind::Path(ref inner_path) => {
 +                if let [prefix @ .., end] = segments {
 +                    if match_qpath(inner_path, prefix) {
 +                        return segment.ident.name.as_str() == *end;
 +                    }
 +                }
 +                false
 +            },
 +            _ => false,
 +        },
 +        QPath::LangItem(..) => false,
 +    }
 +}
 +
 +/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
 +///
 +/// Please use `is_path_diagnostic_item` if the target is a diagnostic item.
 +pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
 +    path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments))
 +}
 +
 +/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
 +/// it matches the given diagnostic item.
 +pub fn is_path_diagnostic_item<'tcx>(
 +    cx: &LateContext<'_>,
 +    maybe_path: &impl MaybePath<'tcx>,
 +    diag_item: Symbol,
 +) -> bool {
 +    path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
 +}
 +
 +/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
 +/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
 +/// `QPath::Resolved.1.res.opt_def_id()`.
 +///
 +/// Matches a `Path` against a slice of segment string literals.
 +///
 +/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
 +/// `rustc_hir::Path`.
 +///
 +/// # Examples
 +///
 +/// ```rust,ignore
 +/// if match_path(&trait_ref.path, &paths::HASH) {
 +///     // This is the `std::hash::Hash` trait.
 +/// }
 +///
 +/// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
 +///     // This is a `rustc_middle::lint::Lint`.
 +/// }
 +/// ```
 +pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
 +    path.segments
 +        .iter()
 +        .rev()
 +        .zip(segments.iter().rev())
 +        .all(|(a, b)| a.ident.name.as_str() == *b)
 +}
 +
 +/// If the expression is a path to a local, returns the canonical `HirId` of the local.
 +pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
 +    if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
 +        if let Res::Local(id) = path.res {
 +            return Some(id);
 +        }
 +    }
 +    None
 +}
 +
 +/// Returns true if the expression is a path to a local with the specified `HirId`.
 +/// Use this function to see if an expression matches a function argument or a match binding.
 +pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
 +    path_to_local(expr) == Some(id)
 +}
 +
 +pub trait MaybePath<'hir> {
 +    fn hir_id(&self) -> HirId;
 +    fn qpath_opt(&self) -> Option<&QPath<'hir>>;
 +}
 +
 +macro_rules! maybe_path {
 +    ($ty:ident, $kind:ident) => {
 +        impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
 +            fn hir_id(&self) -> HirId {
 +                self.hir_id
 +            }
 +            fn qpath_opt(&self) -> Option<&QPath<'hir>> {
 +                match &self.kind {
 +                    hir::$kind::Path(qpath) => Some(qpath),
 +                    _ => None,
 +                }
 +            }
 +        }
 +    };
 +}
 +maybe_path!(Expr, ExprKind);
 +maybe_path!(Pat, PatKind);
 +maybe_path!(Ty, TyKind);
 +
 +/// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err`
 +pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
 +    match maybe_path.qpath_opt() {
 +        None => Res::Err,
 +        Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
 +    }
 +}
 +
 +/// If `maybe_path` is a path node which resolves to an item, retrieves the item ID
 +pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
 +    path_res(cx, maybe_path).opt_def_id()
 +}
 +
 +fn find_primitive<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
 +    let single = |ty| tcx.incoherent_impls(ty).iter().copied();
 +    let empty = || [].iter().copied();
 +    match name {
 +        "bool" => single(BoolSimplifiedType),
 +        "char" => single(CharSimplifiedType),
 +        "str" => single(StrSimplifiedType),
 +        "array" => single(ArraySimplifiedType),
 +        "slice" => single(SliceSimplifiedType),
 +        // FIXME: rustdoc documents these two using just `pointer`.
 +        //
 +        // Maybe this is something we should do here too.
 +        "const_ptr" => single(PtrSimplifiedType(Mutability::Not)),
 +        "mut_ptr" => single(PtrSimplifiedType(Mutability::Mut)),
 +        "isize" => single(IntSimplifiedType(IntTy::Isize)),
 +        "i8" => single(IntSimplifiedType(IntTy::I8)),
 +        "i16" => single(IntSimplifiedType(IntTy::I16)),
 +        "i32" => single(IntSimplifiedType(IntTy::I32)),
 +        "i64" => single(IntSimplifiedType(IntTy::I64)),
 +        "i128" => single(IntSimplifiedType(IntTy::I128)),
 +        "usize" => single(UintSimplifiedType(UintTy::Usize)),
 +        "u8" => single(UintSimplifiedType(UintTy::U8)),
 +        "u16" => single(UintSimplifiedType(UintTy::U16)),
 +        "u32" => single(UintSimplifiedType(UintTy::U32)),
 +        "u64" => single(UintSimplifiedType(UintTy::U64)),
 +        "u128" => single(UintSimplifiedType(UintTy::U128)),
 +        "f32" => single(FloatSimplifiedType(FloatTy::F32)),
 +        "f64" => single(FloatSimplifiedType(FloatTy::F64)),
 +        _ => empty(),
 +    }
 +}
 +
 +/// Resolves a def path like `std::vec::Vec`. `namespace_hint` can be supplied to disambiguate
 +/// between `std::vec` the module and `std::vec` the macro
 +///
 +/// This function is expensive and should be used sparingly.
 +pub fn def_path_res(cx: &LateContext<'_>, path: &[&str], namespace_hint: Option<Namespace>) -> Res {
 +    fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str, matches_ns: impl Fn(Res) -> bool) -> Option<Res> {
 +        match tcx.def_kind(def_id) {
 +            DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
 +                .module_children(def_id)
 +                .iter()
 +                .find(|item| item.ident.name.as_str() == name && matches_ns(item.res.expect_non_local()))
 +                .map(|child| child.res.expect_non_local()),
 +            DefKind::Impl => tcx
 +                .associated_item_def_ids(def_id)
 +                .iter()
 +                .copied()
 +                .find(|assoc_def_id| tcx.item_name(*assoc_def_id).as_str() == name)
 +                .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)),
 +            DefKind::Struct | DefKind::Union => tcx
 +                .adt_def(def_id)
 +                .non_enum_variant()
 +                .fields
 +                .iter()
 +                .find(|f| f.name.as_str() == name)
 +                .map(|f| Res::Def(DefKind::Field, f.did)),
 +            _ => None,
 +        }
 +    }
 +
 +    fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> {
 +        tcx.crates(())
 +            .iter()
 +            .copied()
 +            .find(|&num| tcx.crate_name(num).as_str() == name)
 +            .map(CrateNum::as_def_id)
 +    }
 +
 +    let (base, path) = match *path {
 +        [primitive] => {
 +            return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy);
 +        },
 +        [base, ref path @ ..] => (base, path),
 +        _ => return Res::Err,
 +    };
 +    let tcx = cx.tcx;
 +    let starts = find_primitive(tcx, base)
 +        .chain(find_crate(tcx, base))
 +        .map(|id| Res::Def(tcx.def_kind(id), id));
 +
 +    for first in starts {
 +        let last = path
 +            .iter()
 +            .copied()
 +            .enumerate()
 +            // for each segment, find the child item
 +            .try_fold(first, |res, (idx, segment)| {
 +                let matches_ns = |res: Res| {
 +                    // If at the last segment in the path, respect the namespace hint
 +                    if idx == path.len() - 1 {
 +                        match namespace_hint {
 +                            Some(ns) => res.matches_ns(ns),
 +                            None => true,
 +                        }
 +                    } else {
 +                        res.matches_ns(Namespace::TypeNS)
 +                    }
 +                };
 +
 +                let def_id = res.def_id();
 +                if let Some(item) = item_child_by_name(tcx, def_id, segment, matches_ns) {
 +                    Some(item)
 +                } else if matches!(res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
 +                    // it is not a child item so check inherent impl items
 +                    tcx.inherent_impls(def_id)
 +                        .iter()
 +                        .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment, matches_ns))
 +                } else {
 +                    None
 +                }
 +            });
 +
 +        if let Some(last) = last {
 +            return last;
 +        }
 +    }
 +
 +    Res::Err
 +}
 +
 +/// Convenience function to get the `DefId` of a trait by path.
 +/// It could be a trait or trait alias.
 +///
 +/// This function is expensive and should be used sparingly.
 +pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
 +    match def_path_res(cx, path, Some(Namespace::TypeNS)) {
 +        Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
 +        _ => None,
 +    }
 +}
 +
 +/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
 +///
 +/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
 +///
 +/// ```rust
 +/// struct Point(isize, isize);
 +///
 +/// impl std::ops::Add for Point {
 +///     type Output = Self;
 +///
 +///     fn add(self, other: Self) -> Self {
 +///         Point(0, 0)
 +///     }
 +/// }
 +/// ```
 +pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
 +    // Get the implemented trait for the current function
 +    let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
 +    let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
 +    if_chain! {
 +        if parent_impl != hir::CRATE_OWNER_ID;
 +        if let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent_impl.def_id);
 +        if let hir::ItemKind::Impl(impl_) = &item.kind;
 +        then {
 +            return impl_.of_trait.as_ref();
 +        }
 +    }
 +    None
 +}
 +
 +/// This method will return tuple of projection stack and root of the expression,
 +/// used in `can_mut_borrow_both`.
 +///
 +/// For example, if `e` represents the `v[0].a.b[x]`
 +/// this method will return a tuple, composed of a `Vec`
 +/// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]`
 +/// and an `Expr` for root of them, `v`
 +fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
 +    let mut result = vec![];
 +    let root = loop {
 +        match e.kind {
 +            ExprKind::Index(ep, _) | ExprKind::Field(ep, _) => {
 +                result.push(e);
 +                e = ep;
 +            },
 +            _ => break e,
 +        };
 +    };
 +    result.reverse();
 +    (result, root)
 +}
 +
 +/// Gets the mutability of the custom deref adjustment, if any.
 +pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
 +    cx.typeck_results()
 +        .expr_adjustments(e)
 +        .iter()
 +        .find_map(|a| match a.kind {
 +            Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
 +            Adjust::Deref(None) => None,
 +            _ => Some(None),
 +        })
 +        .and_then(|x| x)
 +}
 +
 +/// Checks if two expressions can be mutably borrowed simultaneously
 +/// and they aren't dependent on borrowing same thing twice
 +pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
 +    let (s1, r1) = projection_stack(e1);
 +    let (s2, r2) = projection_stack(e2);
 +    if !eq_expr_value(cx, r1, r2) {
 +        return true;
 +    }
 +    if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
 +        return false;
 +    }
 +
 +    for (x1, x2) in s1.iter().zip(s2.iter()) {
 +        if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
 +            return false;
 +        }
 +
 +        match (&x1.kind, &x2.kind) {
 +            (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
 +                if i1 != i2 {
 +                    return true;
 +                }
 +            },
 +            (ExprKind::Index(_, i1), ExprKind::Index(_, i2)) => {
 +                if !eq_expr_value(cx, i1, i2) {
 +                    return false;
 +                }
 +            },
 +            _ => return false,
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"
 +/// constructor from the std library
 +fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
 +    let std_types_symbols = &[
 +        sym::String,
 +        sym::Vec,
 +        sym::VecDeque,
 +        sym::LinkedList,
 +        sym::HashMap,
 +        sym::BTreeMap,
 +        sym::HashSet,
 +        sym::BTreeSet,
 +        sym::BinaryHeap,
 +    ];
 +
 +    if let QPath::TypeRelative(_, method) = path {
 +        if method.ident.name == sym::new {
 +            if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
 +                if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
 +                    return std_types_symbols
 +                        .iter()
 +                        .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did()));
 +                }
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +/// Return true if the expr is equal to `Default::default` when evaluated.
 +pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool {
 +    if_chain! {
 +        if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
 +        if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
 +        if is_diag_trait_item(cx, repl_def_id, sym::Default)
 +            || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
 +        then { true } else { false }
 +    }
 +}
 +
 +/// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
 +/// It doesn't cover all cases, for example indirect function calls (some of std
 +/// functions are supported) but it is the best we have.
 +pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    match &e.kind {
 +        ExprKind::Lit(lit) => match lit.node {
 +            LitKind::Bool(false) | LitKind::Int(0, _) => true,
 +            LitKind::Str(s, _) => s.is_empty(),
 +            _ => false,
 +        },
 +        ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
 +        ExprKind::Repeat(x, ArrayLen::Body(len)) => if_chain! {
 +            if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind;
 +            if let LitKind::Int(v, _) = const_lit.node;
 +            if v <= 32 && is_default_equivalent(cx, x);
 +            then {
 +                true
 +            }
 +            else {
 +                false
 +            }
 +        },
++        ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func),
++        ExprKind::Call(from_func, [ref arg]) => is_default_equivalent_from(cx, from_func, arg),
 +        ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
 +        ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
 +        _ => false,
 +    }
 +}
 +
++fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
++    if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind &&
++        seg.ident.name == sym::from
++    {
++        match arg.kind {
++            ExprKind::Lit(hir::Lit {
++                node: LitKind::Str(ref sym, _),
++                ..
++            }) => return sym.is_empty() && is_path_diagnostic_item(cx, ty, sym::String),
++            ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
++            ExprKind::Repeat(_, ArrayLen::Body(len)) => {
++                if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind &&
++                    let LitKind::Int(v, _) = const_lit.node
++                {
++                        return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
++                }
++            }
++            _ => (),
++        }
++    }
++    false
++}
++
 +/// Checks if the top level expression can be moved into a closure as is.
 +/// Currently checks for:
 +/// * Break/Continue outside the given loop HIR ids.
 +/// * Yield/Return statements.
 +/// * Inline assembly.
 +/// * Usages of a field of a local where the type of the local can be partially moved.
 +///
 +/// For example, given the following function:
 +///
 +/// ```
 +/// fn f<'a>(iter: &mut impl Iterator<Item = (usize, &'a mut String)>) {
 +///     for item in iter {
 +///         let s = item.1;
 +///         if item.0 > 10 {
 +///             continue;
 +///         } else {
 +///             s.clear();
 +///         }
 +///     }
 +/// }
 +/// ```
 +///
 +/// When called on the expression `item.0` this will return false unless the local `item` is in the
 +/// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it
 +/// isn't always safe to move into a closure when only a single field is needed.
 +///
 +/// When called on the `continue` expression this will return false unless the outer loop expression
 +/// is in the `loop_ids` set.
 +///
 +/// Note that this check is not recursive, so passing the `if` expression will always return true
 +/// even though sub-expressions might return false.
 +pub fn can_move_expr_to_closure_no_visit<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    loop_ids: &[HirId],
 +    ignore_locals: &HirIdSet,
 +) -> bool {
 +    match expr.kind {
 +        ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
 +        | ExprKind::Continue(Destination { target_id: Ok(id), .. })
 +            if loop_ids.contains(&id) =>
 +        {
 +            true
 +        },
 +        ExprKind::Break(..)
 +        | ExprKind::Continue(_)
 +        | ExprKind::Ret(_)
 +        | ExprKind::Yield(..)
 +        | ExprKind::InlineAsm(_) => false,
 +        // Accessing a field of a local value can only be done if the type isn't
 +        // partially moved.
 +        ExprKind::Field(
 +            &Expr {
 +                hir_id,
 +                kind:
 +                    ExprKind::Path(QPath::Resolved(
 +                        _,
 +                        Path {
 +                            res: Res::Local(local_id),
 +                            ..
 +                        },
 +                    )),
 +                ..
 +            },
 +            _,
 +        ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
 +            // TODO: check if the local has been partially moved. Assume it has for now.
 +            false
 +        },
 +        _ => true,
 +    }
 +}
 +
 +/// How a local is captured by a closure
 +#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 +pub enum CaptureKind {
 +    Value,
 +    Ref(Mutability),
 +}
 +impl CaptureKind {
 +    pub fn is_imm_ref(self) -> bool {
 +        self == Self::Ref(Mutability::Not)
 +    }
 +}
 +impl std::ops::BitOr for CaptureKind {
 +    type Output = Self;
 +    fn bitor(self, rhs: Self) -> Self::Output {
 +        match (self, rhs) {
 +            (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
 +            (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
 +            | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
 +            (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
 +        }
 +    }
 +}
 +impl std::ops::BitOrAssign for CaptureKind {
 +    fn bitor_assign(&mut self, rhs: Self) {
 +        *self = *self | rhs;
 +    }
 +}
 +
 +/// Given an expression referencing a local, determines how it would be captured in a closure.
 +/// Note as this will walk up to parent expressions until the capture can be determined it should
 +/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
 +/// function argument (other than a receiver).
 +pub fn capture_local_usage<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind {
 +    fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
 +        let mut capture = CaptureKind::Ref(Mutability::Not);
 +        pat.each_binding_or_first(&mut |_, id, span, _| match cx
 +            .typeck_results()
 +            .extract_binding_mode(cx.sess(), id, span)
 +            .unwrap()
 +        {
 +            BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => {
 +                capture = CaptureKind::Value;
 +            },
 +            BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => {
 +                capture = CaptureKind::Ref(Mutability::Mut);
 +            },
 +            _ => (),
 +        });
 +        capture
 +    }
 +
 +    debug_assert!(matches!(
 +        e.kind,
 +        ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
 +    ));
 +
 +    let mut child_id = e.hir_id;
 +    let mut capture = CaptureKind::Value;
 +    let mut capture_expr_ty = e;
 +
 +    for (parent_id, parent) in cx.tcx.hir().parent_iter(e.hir_id) {
 +        if let [
 +            Adjustment {
 +                kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
 +                target,
 +            },
 +            ref adjust @ ..,
 +        ] = *cx
 +            .typeck_results()
 +            .adjustments()
 +            .get(child_id)
 +            .map_or(&[][..], |x| &**x)
 +        {
 +            if let rustc_ty::RawPtr(TypeAndMut { mutbl: mutability, .. }) | rustc_ty::Ref(_, _, mutability) =
 +                *adjust.last().map_or(target, |a| a.target).kind()
 +            {
 +                return CaptureKind::Ref(mutability);
 +            }
 +        }
 +
 +        match parent {
 +            Node::Expr(e) => match e.kind {
 +                ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
 +                ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
 +                ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
 +                    return CaptureKind::Ref(Mutability::Mut);
 +                },
 +                ExprKind::Field(..) => {
 +                    if capture == CaptureKind::Value {
 +                        capture_expr_ty = e;
 +                    }
 +                },
 +                ExprKind::Let(let_expr) => {
 +                    let mutability = match pat_capture_kind(cx, let_expr.pat) {
 +                        CaptureKind::Value => Mutability::Not,
 +                        CaptureKind::Ref(m) => m,
 +                    };
 +                    return CaptureKind::Ref(mutability);
 +                },
 +                ExprKind::Match(_, arms, _) => {
 +                    let mut mutability = Mutability::Not;
 +                    for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
 +                        match capture {
 +                            CaptureKind::Value => break,
 +                            CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
 +                            CaptureKind::Ref(Mutability::Not) => (),
 +                        }
 +                    }
 +                    return CaptureKind::Ref(mutability);
 +                },
 +                _ => break,
 +            },
 +            Node::Local(l) => match pat_capture_kind(cx, l.pat) {
 +                CaptureKind::Value => break,
 +                capture @ CaptureKind::Ref(_) => return capture,
 +            },
 +            _ => break,
 +        }
 +
 +        child_id = parent_id;
 +    }
 +
 +    if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
 +        // Copy types are never automatically captured by value.
 +        CaptureKind::Ref(Mutability::Not)
 +    } else {
 +        capture
 +    }
 +}
 +
 +/// Checks if the expression can be moved into a closure as is. This will return a list of captures
 +/// if so, otherwise, `None`.
 +pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
 +    struct V<'cx, 'tcx> {
 +        cx: &'cx LateContext<'tcx>,
 +        // Stack of potential break targets contained in the expression.
 +        loops: Vec<HirId>,
 +        /// Local variables created in the expression. These don't need to be captured.
 +        locals: HirIdSet,
 +        /// Whether this expression can be turned into a closure.
 +        allow_closure: bool,
 +        /// Locals which need to be captured, and whether they need to be by value, reference, or
 +        /// mutable reference.
 +        captures: HirIdMap<CaptureKind>,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if !self.allow_closure {
 +                return;
 +            }
 +
 +            match e.kind {
 +                ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
 +                    if !self.locals.contains(&l) {
 +                        let cap = capture_local_usage(self.cx, e);
 +                        self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
 +                    }
 +                },
 +                ExprKind::Closure { .. } => {
 +                    let closure_id = self.cx.tcx.hir().local_def_id(e.hir_id);
 +                    for capture in self.cx.typeck_results().closure_min_captures_flattened(closure_id) {
 +                        let local_id = match capture.place.base {
 +                            PlaceBase::Local(id) => id,
 +                            PlaceBase::Upvar(var) => var.var_path.hir_id,
 +                            _ => continue,
 +                        };
 +                        if !self.locals.contains(&local_id) {
 +                            let capture = match capture.info.capture_kind {
 +                                UpvarCapture::ByValue => CaptureKind::Value,
 +                                UpvarCapture::ByRef(kind) => match kind {
 +                                    BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
 +                                    BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
 +                                        CaptureKind::Ref(Mutability::Mut)
 +                                    },
 +                                },
 +                            };
 +                            self.captures
 +                                .entry(local_id)
 +                                .and_modify(|e| *e |= capture)
 +                                .or_insert(capture);
 +                        }
 +                    }
 +                },
 +                ExprKind::Loop(b, ..) => {
 +                    self.loops.push(e.hir_id);
 +                    self.visit_block(b);
 +                    self.loops.pop();
 +                },
 +                _ => {
 +                    self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
 +                    walk_expr(self, e);
 +                },
 +            }
 +        }
 +
 +        fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
 +            p.each_binding_or_first(&mut |_, id, _, _| {
 +                self.locals.insert(id);
 +            });
 +        }
 +    }
 +
 +    let mut v = V {
 +        cx,
 +        allow_closure: true,
 +        loops: Vec::new(),
 +        locals: HirIdSet::default(),
 +        captures: HirIdMap::default(),
 +    };
 +    v.visit_expr(expr);
 +    v.allow_closure.then_some(v.captures)
 +}
 +
 +/// Arguments of a method: the receiver and all the additional arguments.
 +pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
 +
 +/// Returns the method names and argument list of nested method call expressions that make up
 +/// `expr`. method/span lists are sorted with the most recent call first.
 +pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
 +    let mut method_names = Vec::with_capacity(max_depth);
 +    let mut arg_lists = Vec::with_capacity(max_depth);
 +    let mut spans = Vec::with_capacity(max_depth);
 +
 +    let mut current = expr;
 +    for _ in 0..max_depth {
 +        if let ExprKind::MethodCall(path, receiver, args, _) = &current.kind {
 +            if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
 +                break;
 +            }
 +            method_names.push(path.ident.name);
 +            arg_lists.push((*receiver, &**args));
 +            spans.push(path.ident.span);
 +            current = receiver;
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    (method_names, arg_lists, spans)
 +}
 +
 +/// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
 +///
 +/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
 +/// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec`
 +/// containing the `Expr`s for
 +/// `.bar()` and `.baz()`
 +pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
 +    let mut current = expr;
 +    let mut matched = Vec::with_capacity(methods.len());
 +    for method_name in methods.iter().rev() {
 +        // method chains are stored last -> first
 +        if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
 +            if path.ident.name.as_str() == *method_name {
 +                if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
 +                    return None;
 +                }
 +                matched.push((receiver, args)); // build up `matched` backwards
 +                current = receiver; // go to parent expression
 +            } else {
 +                return None;
 +            }
 +        } else {
 +            return None;
 +        }
 +    }
 +    // Reverse `matched` so that it is in the same order as `methods`.
 +    matched.reverse();
 +    Some(matched)
 +}
 +
 +/// Returns `true` if the provided `def_id` is an entrypoint to a program.
 +pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    cx.tcx
 +        .entry_fn(())
 +        .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id)
 +}
 +
 +/// Returns `true` if the expression is in the program's `#[panic_handler]`.
 +pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    let parent = cx.tcx.hir().get_parent_item(e.hir_id);
 +    Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
 +}
 +
 +/// Gets the name of the item the expression is in, if available.
 +pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
 +    let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id).def_id;
 +    match cx.tcx.hir().find_by_def_id(parent_id) {
 +        Some(
 +            Node::Item(Item { ident, .. })
 +            | Node::TraitItem(TraitItem { ident, .. })
 +            | Node::ImplItem(ImplItem { ident, .. }),
 +        ) => Some(ident.name),
 +        _ => None,
 +    }
 +}
 +
 +pub struct ContainsName {
 +    pub name: Symbol,
 +    pub result: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for ContainsName {
 +    fn visit_name(&mut self, name: Symbol) {
 +        if self.name == name {
 +            self.result = true;
 +        }
 +    }
 +}
 +
 +/// Checks if an `Expr` contains a certain name.
 +pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
 +    let mut cn = ContainsName { name, result: false };
 +    cn.visit_expr(expr);
 +    cn.result
 +}
 +
 +/// Returns `true` if `expr` contains a return expression
 +pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
 +    for_each_expr(expr, |e| {
 +        if matches!(e.kind, hir::ExprKind::Ret(..)) {
 +            ControlFlow::Break(())
 +        } else {
 +            ControlFlow::Continue(())
 +        }
 +    })
 +    .is_some()
 +}
 +
 +/// Extends the span to the beginning of the spans line, incl. whitespaces.
 +///
 +/// ```rust
 +///        let x = ();
 +/// //             ^^
 +/// // will be converted to
 +///        let x = ();
 +/// // ^^^^^^^^^^^^^^
 +/// ```
 +fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
 +    let span = original_sp(span, DUMMY_SP);
 +    let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap();
 +    let line_no = source_map_and_line.line;
 +    let line_start = source_map_and_line.sf.lines(|lines| lines[line_no]);
 +    span.with_lo(line_start)
 +}
 +
 +/// Gets the parent node, if any.
 +pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
 +    tcx.hir().parent_iter(id).next().map(|(_, node)| node)
 +}
 +
 +/// Gets the parent expression, if any –- this is useful to constrain a lint.
 +pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    get_parent_expr_for_hir(cx, e.hir_id)
 +}
 +
 +/// This retrieves the parent for the given `HirId` if it's an expression. This is useful for
 +/// constraint lints
 +pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> {
 +    match get_parent_node(cx.tcx, hir_id) {
 +        Some(Node::Expr(parent)) => Some(parent),
 +        _ => None,
 +    }
 +}
 +
 +pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
 +    let map = &cx.tcx.hir();
 +    let enclosing_node = map
 +        .get_enclosing_scope(hir_id)
 +        .and_then(|enclosing_id| map.find(enclosing_id));
 +    enclosing_node.and_then(|node| match node {
 +        Node::Block(block) => Some(block),
 +        Node::Item(&Item {
 +            kind: ItemKind::Fn(_, _, eid),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Fn(_, eid),
 +            ..
 +        }) => match cx.tcx.hir().body(eid).value.kind {
 +            ExprKind::Block(block, _) => Some(block),
 +            _ => None,
 +        },
 +        _ => None,
 +    })
 +}
 +
 +/// Gets the loop or closure enclosing the given expression, if any.
 +pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &Expr<'_>,
 +) -> Option<&'tcx Expr<'tcx>> {
 +    for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
 +        match node {
 +            Node::Expr(e) => match e.kind {
 +                ExprKind::Closure { .. } => {
 +                    if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
 +                        && subs.as_closure().kind() == ClosureKind::FnOnce
 +                    {
 +                        continue;
 +                    }
 +                    let is_once = walk_to_expr_usage(cx, e, |node, id| {
 +                        let Node::Expr(e) = node else {
 +                            return None;
 +                        };
 +                        match e.kind {
 +                            ExprKind::Call(f, _) if f.hir_id == id => Some(()),
 +                            ExprKind::Call(f, args) => {
 +                                let i = args.iter().position(|arg| arg.hir_id == id)?;
 +                                let sig = expr_sig(cx, f)?;
 +                                let predicates = sig
 +                                    .predicates_id()
 +                                    .map_or(cx.param_env, |id| cx.tcx.param_env(id))
 +                                    .caller_bounds();
 +                                sig.input(i).and_then(|ty| {
 +                                    ty_is_fn_once_param(cx.tcx, ty.skip_binder(), predicates).then_some(())
 +                                })
 +                            },
 +                            ExprKind::MethodCall(_, receiver, args, _) => {
 +                                let i = std::iter::once(receiver)
 +                                    .chain(args.iter())
 +                                    .position(|arg| arg.hir_id == id)?;
 +                                let id = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
 +                                let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
 +                                ty_is_fn_once_param(cx.tcx, ty, cx.tcx.param_env(id).caller_bounds()).then_some(())
 +                            },
 +                            _ => None,
 +                        }
 +                    })
 +                    .is_some();
 +                    if !is_once {
 +                        return Some(e);
 +                    }
 +                },
 +                ExprKind::Loop(..) => return Some(e),
 +                _ => (),
 +            },
 +            Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
 +            _ => break,
 +        }
 +    }
 +    None
 +}
 +
 +/// Gets the parent node if it's an impl block.
 +pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
 +    match tcx.hir().parent_iter(id).next() {
 +        Some((
 +            _,
 +            Node::Item(Item {
 +                kind: ItemKind::Impl(imp),
 +                ..
 +            }),
 +        )) => Some(imp),
 +        _ => None,
 +    }
 +}
 +
 +/// Removes blocks around an expression, only if the block contains just one expression
 +/// and no statements. Unsafe blocks are not removed.
 +///
 +/// Examples:
 +///  * `{}`               -> `{}`
 +///  * `{ x }`            -> `x`
 +///  * `{{ x }}`          -> `x`
 +///  * `{ x; }`           -> `{ x; }`
 +///  * `{ x; y }`         -> `{ x; y }`
 +///  * `{ unsafe { x } }` -> `unsafe { x }`
 +pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
 +    while let ExprKind::Block(
 +        Block {
 +            stmts: [],
 +            expr: Some(inner),
 +            rules: BlockCheckMode::DefaultBlock,
 +            ..
 +        },
 +        _,
 +    ) = expr.kind
 +    {
 +        expr = inner;
 +    }
 +    expr
 +}
 +
 +/// Removes blocks around an expression, only if the block contains just one expression
 +/// or just one expression statement with a semicolon. Unsafe blocks are not removed.
 +///
 +/// Examples:
 +///  * `{}`               -> `{}`
 +///  * `{ x }`            -> `x`
 +///  * `{ x; }`           -> `x`
 +///  * `{{ x; }}`         -> `x`
 +///  * `{ x; y }`         -> `{ x; y }`
 +///  * `{ unsafe { x } }` -> `unsafe { x }`
 +pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
 +    while let ExprKind::Block(
 +        Block {
 +            stmts: [],
 +            expr: Some(inner),
 +            rules: BlockCheckMode::DefaultBlock,
 +            ..
 +        }
 +        | Block {
 +            stmts:
 +                [
 +                    Stmt {
 +                        kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
 +                        ..
 +                    },
 +                ],
 +            expr: None,
 +            rules: BlockCheckMode::DefaultBlock,
 +            ..
 +        },
 +        _,
 +    ) = expr.kind
 +    {
 +        expr = inner;
 +    }
 +    expr
 +}
 +
 +/// Checks if the given expression is the else clause of either an `if` or `if let` expression.
 +pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    let mut iter = tcx.hir().parent_iter(expr.hir_id);
 +    match iter.next() {
 +        Some((
 +            _,
 +            Node::Expr(Expr {
 +                kind: ExprKind::If(_, _, Some(else_expr)),
 +                ..
 +            }),
 +        )) => else_expr.hir_id == expr.hir_id,
 +        _ => false,
 +    }
 +}
 +
 +/// Checks whether the given expression is a constant integer of the given value.
 +/// unlike `is_integer_literal`, this version does const folding
 +pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
 +    if is_integer_literal(e, value) {
 +        return true;
 +    }
 +    let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id);
 +    if let Some((Constant::Int(v), _)) = constant(cx, cx.tcx.typeck(enclosing_body), e) {
 +        return value == v;
 +    }
 +    false
 +}
 +
 +/// Checks whether the given expression is a constant literal of the given value.
 +pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
 +    // FIXME: use constant folding
 +    if let ExprKind::Lit(ref spanned) = expr.kind {
 +        if let LitKind::Int(v, _) = spanned.node {
 +            return v == value;
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns `true` if the given `Expr` has been coerced before.
 +///
 +/// Examples of coercions can be found in the Nomicon at
 +/// <https://doc.rust-lang.org/nomicon/coercions.html>.
 +///
 +/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_hir_analysis::check::coercion` for
 +/// more information on adjustments and coercions.
 +pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    cx.typeck_results().adjustments().get(e.hir_id).is_some()
 +}
 +
 +/// Returns the pre-expansion span if this comes from an expansion of the
 +/// macro `name`.
 +/// See also [`is_direct_expn_of`].
 +#[must_use]
 +pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
 +    loop {
 +        if span.from_expansion() {
 +            let data = span.ctxt().outer_expn_data();
 +            let new_span = data.call_site;
 +
 +            if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
 +                if mac_name.as_str() == name {
 +                    return Some(new_span);
 +                }
 +            }
 +
 +            span = new_span;
 +        } else {
 +            return None;
 +        }
 +    }
 +}
 +
 +/// Returns the pre-expansion span if the span directly comes from an expansion
 +/// of the macro `name`.
 +/// The difference with [`is_expn_of`] is that in
 +/// ```rust
 +/// # macro_rules! foo { ($name:tt!$args:tt) => { $name!$args } }
 +/// # macro_rules! bar { ($e:expr) => { $e } }
 +/// foo!(bar!(42));
 +/// ```
 +/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
 +/// from `bar!` by `is_direct_expn_of`.
 +#[must_use]
 +pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
 +    if span.from_expansion() {
 +        let data = span.ctxt().outer_expn_data();
 +        let new_span = data.call_site;
 +
 +        if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
 +            if mac_name.as_str() == name {
 +                return Some(new_span);
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +/// Convenience function to get the return type of a function.
 +pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
 +    let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
 +    let ret_ty = cx.tcx.fn_sig(fn_def_id).output();
 +    cx.tcx.erase_late_bound_regions(ret_ty)
 +}
 +
 +/// Convenience function to get the nth argument type of a function.
 +pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId, nth: usize) -> Ty<'tcx> {
 +    let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
 +    let arg = cx.tcx.fn_sig(fn_def_id).input(nth);
 +    cx.tcx.erase_late_bound_regions(arg)
 +}
 +
 +/// Checks if an expression is constructing a tuple-like enum variant or struct
 +pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Call(fun, _) = expr.kind {
 +        if let ExprKind::Path(ref qp) = fun.kind {
 +            let res = cx.qpath_res(qp, fun.hir_id);
 +            return match res {
 +                def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
 +                def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
 +                _ => false,
 +            };
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns `true` if a pattern is refutable.
 +// TODO: should be implemented using rustc/mir_build/thir machinery
 +pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
 +    fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
 +        matches!(
 +            cx.qpath_res(qpath, id),
 +            def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
 +        )
 +    }
 +
 +    fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
 +        i.into_iter().any(|pat| is_refutable(cx, pat))
 +    }
 +
 +    match pat.kind {
 +        PatKind::Wild => false,
 +        PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
 +        PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
 +        PatKind::Lit(..) | PatKind::Range(..) => true,
 +        PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
 +        PatKind::Or(pats) => {
 +            // TODO: should be the honest check, that pats is exhaustive set
 +            are_refutable(cx, pats)
 +        },
 +        PatKind::Tuple(pats, _) => are_refutable(cx, pats),
 +        PatKind::Struct(ref qpath, fields, _) => {
 +            is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
 +        },
 +        PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
 +        PatKind::Slice(head, middle, tail) => {
 +            match &cx.typeck_results().node_type(pat.hir_id).kind() {
 +                rustc_ty::Slice(..) => {
 +                    // [..] is the only irrefutable slice pattern.
 +                    !head.is_empty() || middle.is_none() || !tail.is_empty()
 +                },
 +                rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
 +                _ => {
 +                    // unreachable!()
 +                    true
 +                },
 +            }
 +        },
 +    }
 +}
 +
 +/// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call
 +/// the function once on the given pattern.
 +pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
 +    if let PatKind::Or(pats) = pat.kind {
 +        pats.iter().for_each(f);
 +    } else {
 +        f(pat);
 +    }
 +}
 +
 +pub fn is_self(slf: &Param<'_>) -> bool {
 +    if let PatKind::Binding(.., name, _) = slf.pat.kind {
 +        name.name == kw::SelfLower
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
 +    if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind {
 +        if let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res {
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
 +    (0..decl.inputs.len()).map(move |i| &body.params[i])
 +}
 +
 +/// Checks if a given expression is a match expression expanded from the `?`
 +/// operator or the `try` macro.
 +pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +    fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +        if_chain! {
 +            if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind;
 +            if ddpos.as_opt_usize().is_none();
 +            if is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk);
 +            if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
 +            if path_to_local_id(arm.body, hir_id);
 +            then {
 +                return true;
 +            }
 +        }
 +        false
 +    }
 +
 +    fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +        if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
 +            is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr)
 +        } else {
 +            false
 +        }
 +    }
 +
 +    if let ExprKind::Match(_, arms, ref source) = expr.kind {
 +        // desugared from a `?` operator
 +        if *source == MatchSource::TryDesugar {
 +            return Some(expr);
 +        }
 +
 +        if_chain! {
 +            if arms.len() == 2;
 +            if arms[0].guard.is_none();
 +            if arms[1].guard.is_none();
 +            if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
 +            then {
 +                return Some(expr);
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +/// Returns `true` if the lint is allowed in the current context. This is useful for
 +/// skipping long running code when it's unnecessary
 +///
 +/// This function should check the lint level for the same node, that the lint will
 +/// be emitted at. If the information is buffered to be emitted at a later point, please
 +/// make sure to use `span_lint_hir` functions to emit the lint. This ensures that
 +/// expectations at the checked nodes will be fulfilled.
 +pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
 +    cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
 +}
 +
 +pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
 +    while let PatKind::Ref(subpat, _) = pat.kind {
 +        pat = subpat;
 +    }
 +    pat
 +}
 +
 +pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 {
 +    Integer::from_int_ty(&tcx, ity).size().bits()
 +}
 +
 +#[expect(clippy::cast_possible_wrap)]
 +/// Turn a constant int byte representation into an i128
 +pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 {
 +    let amt = 128 - int_bits(tcx, ity);
 +    ((u as i128) << amt) >> amt
 +}
 +
 +#[expect(clippy::cast_sign_loss)]
 +/// clip unused bytes
 +pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 {
 +    let amt = 128 - int_bits(tcx, ity);
 +    ((u as u128) << amt) >> amt
 +}
 +
 +/// clip unused bytes
 +pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 {
 +    let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
 +    let amt = 128 - bits;
 +    (u << amt) >> amt
 +}
 +
 +pub fn has_attr(attrs: &[ast::Attribute], symbol: Symbol) -> bool {
 +    attrs.iter().any(|attr| attr.has_name(symbol))
 +}
 +
 +pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
 +    let map = &tcx.hir();
 +    let mut prev_enclosing_node = None;
 +    let mut enclosing_node = node;
 +    while Some(enclosing_node) != prev_enclosing_node {
 +        if has_attr(map.attrs(enclosing_node), symbol) {
 +            return true;
 +        }
 +        prev_enclosing_node = Some(enclosing_node);
 +        enclosing_node = map.get_parent_item(enclosing_node).into();
 +    }
 +
 +    false
 +}
 +
 +pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
 +    any_parent_has_attr(tcx, node, sym::automatically_derived)
 +}
 +
 +/// Matches a function call with the given path and returns the arguments.
 +///
 +/// Usage:
 +///
 +/// ```rust,ignore
 +/// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX);
 +/// ```
++/// This function is deprecated. Use [`match_function_call_with_def_id`].
 +pub fn match_function_call<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    path: &[&str],
 +) -> Option<&'tcx [Expr<'tcx>]> {
 +    if_chain! {
 +        if let ExprKind::Call(fun, args) = expr.kind;
 +        if let ExprKind::Path(ref qpath) = fun.kind;
 +        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
 +        if match_def_path(cx, fun_def_id, path);
 +        then {
 +            return Some(args);
 +        }
 +    };
 +    None
 +}
 +
++pub fn match_function_call_with_def_id<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    fun_def_id: DefId,
++) -> Option<&'tcx [Expr<'tcx>]> {
++    if_chain! {
++        if let ExprKind::Call(fun, args) = expr.kind;
++        if let ExprKind::Path(ref qpath) = fun.kind;
++        if cx.qpath_res(qpath, fun.hir_id).opt_def_id() == Some(fun_def_id);
++        then {
++            return Some(args);
++        }
++    };
++    None
++}
++
 +/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
 +/// any.
 +///
 +/// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items.
 +pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
 +    let search_path = cx.get_def_path(did);
 +    paths
 +        .iter()
 +        .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
 +}
 +
 +/// Checks if the given `DefId` matches the path.
 +pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
 +    // We should probably move to Symbols in Clippy as well rather than interning every time.
 +    let path = cx.get_def_path(did);
 +    syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
 +}
 +
 +/// Checks if the given `DefId` matches the `libc` item.
 +pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
 +    let path = cx.get_def_path(did);
 +    // libc is meant to be used as a flat list of names, but they're all actually defined in different
 +    // modules based on the target platform. Ignore everything but crate name and the item name.
 +    path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name)
 +}
 +
 +/// Returns the list of condition expressions and the list of blocks in a
 +/// sequence of `if/else`.
 +/// E.g., this returns `([a, b], [c, d, e])` for the expression
 +/// `if a { c } else if b { d } else { e }`.
 +pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
 +    let mut conds = Vec::new();
 +    let mut blocks: Vec<&Block<'_>> = Vec::new();
 +
 +    while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
 +        conds.push(cond);
 +        if let ExprKind::Block(block, _) = then.kind {
 +            blocks.push(block);
 +        } else {
 +            panic!("ExprKind::If node is not an ExprKind::Block");
 +        }
 +
 +        if let Some(else_expr) = r#else {
 +            expr = else_expr;
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    // final `else {..}`
 +    if !blocks.is_empty() {
 +        if let ExprKind::Block(block, _) = expr.kind {
 +            blocks.push(block);
 +        }
 +    }
 +
 +    (conds, blocks)
 +}
 +
 +/// Checks if the given function kind is an async function.
 +pub fn is_async_fn(kind: FnKind<'_>) -> bool {
 +    matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness == IsAsync::Async)
 +}
 +
 +/// Peels away all the compiler generated code surrounding the body of an async function,
 +pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    if let ExprKind::Call(
 +        _,
 +        &[
 +            Expr {
 +                kind: ExprKind::Closure(&Closure { body, .. }),
 +                ..
 +            },
 +        ],
 +    ) = body.value.kind
 +    {
 +        if let ExprKind::Block(
 +            Block {
 +                stmts: [],
 +                expr:
 +                    Some(Expr {
 +                        kind: ExprKind::DropTemps(expr),
 +                        ..
 +                    }),
 +                ..
 +            },
 +            _,
 +        ) = tcx.hir().body(body).value.kind
 +        {
 +            return Some(expr);
 +        }
 +    };
 +    None
 +}
 +
 +// check if expr is calling method or function with #[must_use] attribute
 +pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    let did = match expr.kind {
 +        ExprKind::Call(path, _) => if_chain! {
 +            if let ExprKind::Path(ref qpath) = path.kind;
 +            if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id);
 +            then {
 +                Some(did)
 +            } else {
 +                None
 +            }
 +        },
 +        ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
 +        _ => None,
 +    };
 +
 +    did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use))
 +}
 +
 +/// Checks if an expression represents the identity function
 +/// Only examines closures and `std::convert::identity`
 +pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    /// Checks if a function's body represents the identity function. Looks for bodies of the form:
 +    /// * `|x| x`
 +    /// * `|x| return x`
 +    /// * `|x| { return x }`
 +    /// * `|x| { return x; }`
 +    fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
 +        let id = if_chain! {
 +            if let [param] = func.params;
 +            if let PatKind::Binding(_, id, _, _) = param.pat.kind;
 +            then {
 +                id
 +            } else {
 +                return false;
 +            }
 +        };
 +
 +        let mut expr = func.value;
 +        loop {
 +            match expr.kind {
 +                #[rustfmt::skip]
 +                ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
 +                | ExprKind::Ret(Some(e)) => expr = e,
 +                #[rustfmt::skip]
 +                ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
 +                    if_chain! {
 +                        if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
 +                        if let ExprKind::Ret(Some(ret_val)) = e.kind;
 +                        then {
 +                            expr = ret_val;
 +                        } else {
 +                            return false;
 +                        }
 +                    }
 +                },
 +                _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
 +            }
 +        }
 +    }
 +
 +    match expr.kind {
 +        ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)),
 +        _ => path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, &paths::CONVERT_IDENTITY)),
 +    }
 +}
 +
 +/// Gets the node where an expression is either used, or it's type is unified with another branch.
 +/// Returns both the node and the `HirId` of the closest child node.
 +pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
 +    let mut child_id = expr.hir_id;
 +    let mut iter = tcx.hir().parent_iter(child_id);
 +    loop {
 +        match iter.next() {
 +            None => break None,
 +            Some((id, Node::Block(_))) => child_id = id,
 +            Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
 +            Some((_, Node::Expr(expr))) => match expr.kind {
 +                ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
 +                ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
 +                ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
 +                _ => break Some((Node::Expr(expr), child_id)),
 +            },
 +            Some((_, node)) => break Some((node, child_id)),
 +        }
 +    }
 +}
 +
 +/// Checks if the result of an expression is used, or it's type is unified with another branch.
 +pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    !matches!(
 +        get_expr_use_or_unification_node(tcx, expr),
 +        None | Some((
 +            Node::Stmt(Stmt {
 +                kind: StmtKind::Expr(_)
 +                    | StmtKind::Semi(_)
 +                    | StmtKind::Local(Local {
 +                        pat: Pat {
 +                            kind: PatKind::Wild,
 +                            ..
 +                        },
 +                        ..
 +                    }),
 +                ..
 +            }),
 +            _
 +        ))
 +    )
 +}
 +
 +/// Checks if the expression is the final expression returned from a block.
 +pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
 +}
 +
 +pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
 +    if !is_no_std_crate(cx) {
 +        Some("std")
 +    } else if !is_no_core_crate(cx) {
 +        Some("core")
 +    } else {
 +        None
 +    }
 +}
 +
 +pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
 +    cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
 +        if let ast::AttrKind::Normal(ref normal) = attr.kind {
 +            normal.item.path == sym::no_std
 +        } else {
 +            false
 +        }
 +    })
 +}
 +
 +pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
 +    cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
 +        if let ast::AttrKind::Normal(ref normal) = attr.kind {
 +            normal.item.path == sym::no_core
 +        } else {
 +            false
 +        }
 +    })
 +}
 +
 +/// Check if parent of a hir node is a trait implementation block.
 +/// For example, `f` in
 +/// ```rust
 +/// # struct S;
 +/// # trait Trait { fn f(); }
 +/// impl Trait for S {
 +///     fn f() {}
 +/// }
 +/// ```
 +pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
 +    if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
 +        matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Check if it's even possible to satisfy the `where` clause for the item.
 +///
 +/// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example:
 +///
 +/// ```ignore
 +/// fn foo() where i32: Iterator {
 +///     for _ in 2i32 {}
 +/// }
 +/// ```
 +pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
 +    use rustc_trait_selection::traits;
 +    let predicates = cx
 +        .tcx
 +        .predicates_of(did)
 +        .predicates
 +        .iter()
 +        .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
 +    traits::impossible_predicates(
 +        cx.tcx,
 +        traits::elaborate_predicates(cx.tcx, predicates)
 +            .map(|o| o.predicate)
 +            .collect::<Vec<_>>(),
 +    )
 +}
 +
 +/// Returns the `DefId` of the callee if the given expression is a function or method call.
 +pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
 +    match &expr.kind {
 +        ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
 +        ExprKind::Call(
 +            Expr {
 +                kind: ExprKind::Path(qpath),
 +                hir_id: path_hir_id,
 +                ..
 +            },
 +            ..,
 +        ) => {
 +            // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or
 +            // deref to fn pointers, dyn Fn, impl Fn - #8850
 +            if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
 +                cx.typeck_results().qpath_res(qpath, *path_hir_id)
 +            {
 +                Some(id)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +/// Returns `Option<String>` where String is a textual representation of the type encapsulated in
 +/// the slice iff the given expression is a slice of primitives (as defined in the
 +/// `is_recursively_primitive_type` function) and `None` otherwise.
 +pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
 +    let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
 +    let expr_kind = expr_type.kind();
 +    let is_primitive = match expr_kind {
 +        rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
 +        rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
 +            if let rustc_ty::Slice(element_type) = inner_ty.kind() {
 +                is_recursively_primitive_type(*element_type)
 +            } else {
 +                unreachable!()
 +            }
 +        },
 +        _ => false,
 +    };
 +
 +    if is_primitive {
 +        // if we have wrappers like Array, Slice or Tuple, print these
 +        // and get the type enclosed in the slice ref
 +        match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
 +            rustc_ty::Slice(..) => return Some("slice".into()),
 +            rustc_ty::Array(..) => return Some("array".into()),
 +            rustc_ty::Tuple(..) => return Some("tuple".into()),
 +            _ => {
 +                // is_recursively_primitive_type() should have taken care
 +                // of the rest and we can rely on the type that is found
 +                let refs_peeled = expr_type.peel_refs();
 +                return Some(refs_peeled.walk().last().unwrap().to_string());
 +            },
 +        }
 +    }
 +    None
 +}
 +
 +/// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
 +/// `hash` must be comformed with `eq`
 +pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
 +where
 +    Hash: Fn(&T) -> u64,
 +    Eq: Fn(&T, &T) -> bool,
 +{
 +    match exprs {
 +        [a, b] if eq(a, b) => return vec![(a, b)],
 +        _ if exprs.len() <= 2 => return vec![],
 +        _ => {},
 +    }
 +
 +    let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
 +
 +    let mut map: UnhashMap<u64, Vec<&_>> =
 +        UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
 +
 +    for expr in exprs {
 +        match map.entry(hash(expr)) {
 +            Entry::Occupied(mut o) => {
 +                for o in o.get() {
 +                    if eq(o, expr) {
 +                        match_expr_list.push((o, expr));
 +                    }
 +                }
 +                o.get_mut().push(expr);
 +            },
 +            Entry::Vacant(v) => {
 +                v.insert(vec![expr]);
 +            },
 +        }
 +    }
 +
 +    match_expr_list
 +}
 +
 +/// Peels off all references on the pattern. Returns the underlying pattern and the number of
 +/// references removed.
 +pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
 +    fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
 +        if let PatKind::Ref(pat, _) = pat.kind {
 +            peel(pat, count + 1)
 +        } else {
 +            (pat, count)
 +        }
 +    }
 +    peel(pat, 0)
 +}
 +
 +/// Peels of expressions while the given closure returns `Some`.
 +pub fn peel_hir_expr_while<'tcx>(
 +    mut expr: &'tcx Expr<'tcx>,
 +    mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
 +) -> &'tcx Expr<'tcx> {
 +    while let Some(e) = f(expr) {
 +        expr = e;
 +    }
 +    expr
 +}
 +
 +/// Peels off up to the given number of references on the expression. Returns the underlying
 +/// expression and the number of references removed.
 +pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
 +    let mut remaining = count;
 +    let e = peel_hir_expr_while(expr, |e| match e.kind {
 +        ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
 +            remaining -= 1;
 +            Some(e)
 +        },
 +        _ => None,
 +    });
 +    (e, count - remaining)
 +}
 +
 +/// Peels off all references on the expression. Returns the underlying expression and the number of
 +/// references removed.
 +pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
 +    let mut count = 0;
 +    let e = peel_hir_expr_while(expr, |e| match e.kind {
 +        ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
 +            count += 1;
 +            Some(e)
 +        },
 +        _ => None,
 +    });
 +    (e, count)
 +}
 +
 +/// Peels off all references on the type. Returns the underlying type and the number of references
 +/// removed.
 +pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
 +    let mut count = 0;
 +    loop {
 +        match &ty.kind {
 +            TyKind::Rptr(_, ref_ty) => {
 +                ty = ref_ty.ty;
 +                count += 1;
 +            },
 +            _ => break (ty, count),
 +        }
 +    }
 +}
 +
 +/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
 +/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
 +pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
 +    loop {
 +        match expr.kind {
 +            ExprKind::AddrOf(_, _, e) => expr = e,
 +            ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
 +            _ => break,
 +        }
 +    }
 +    expr
 +}
 +
 +pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
 +    if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
 +        if let Res::Def(_, def_id) = path.res {
 +            return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
 +        }
 +    }
 +    false
 +}
 +
 +static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = OnceLock::new();
 +
 +fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
 +    let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
 +    let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
 +    let value = map.entry(module);
 +    match value {
 +        Entry::Occupied(entry) => f(entry.get()),
 +        Entry::Vacant(entry) => {
 +            let mut names = Vec::new();
 +            for id in tcx.hir().module_items(module) {
 +                if matches!(tcx.def_kind(id.def_id), DefKind::Const)
 +                    && let item = tcx.hir().item(id)
 +                    && let ItemKind::Const(ty, _body) = item.kind {
 +                    if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
 +                        // We could also check for the type name `test::TestDescAndFn`
 +                        if let Res::Def(DefKind::Struct, _) = path.res {
 +                            let has_test_marker = tcx
 +                                .hir()
 +                                .attrs(item.hir_id())
 +                                .iter()
 +                                .any(|a| a.has_name(sym::rustc_test_marker));
 +                            if has_test_marker {
 +                                names.push(item.ident.name);
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +            names.sort_unstable();
 +            f(entry.insert(names))
 +        },
 +    }
 +}
 +
 +/// Checks if the function containing the given `HirId` is a `#[test]` function
 +///
 +/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
 +pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
 +    with_test_item_names(tcx, tcx.parent_module(id), |names| {
 +        tcx.hir()
 +            .parent_iter(id)
 +            // Since you can nest functions we need to collect all until we leave
 +            // function scope
 +            .any(|(_id, node)| {
 +                if let Node::Item(item) = node {
 +                    if let ItemKind::Fn(_, _, _) = item.kind {
 +                        // Note that we have sorted the item names in the visitor,
 +                        // so the binary_search gets the same as `contains`, but faster.
 +                        return names.binary_search(&item.ident.name).is_ok();
 +                    }
 +                }
 +                false
 +            })
 +    })
 +}
 +
 +/// Checks if the item containing the given `HirId` has `#[cfg(test)]` attribute applied
 +///
 +/// Note: Add `// compile-flags: --test` to UI tests with a `#[cfg(test)]` function
 +pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
 +    fn is_cfg_test(attr: &Attribute) -> bool {
 +        if attr.has_name(sym::cfg)
 +            && let Some(items) = attr.meta_item_list()
 +            && let [item] = &*items
 +            && item.has_name(sym::test)
 +        {
 +            true
 +        } else {
 +            false
 +        }
 +    }
 +    tcx.hir()
 +        .parent_iter(id)
 +        .flat_map(|(parent_id, _)| tcx.hir().attrs(parent_id))
 +        .any(is_cfg_test)
 +}
 +
 +/// Checks whether item either has `test` attribute applied, or
 +/// is a module with `test` in its name.
 +///
 +/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
 +pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
 +    is_in_test_function(tcx, item.hir_id())
 +        || matches!(item.kind, ItemKind::Mod(..))
 +            && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests")
 +}
 +
 +/// Walks the HIR tree from the given expression, up to the node where the value produced by the
 +/// expression is consumed. Calls the function for every node encountered this way until it returns
 +/// `Some`.
 +///
 +/// This allows walking through `if`, `match`, `break`, block expressions to find where the value
 +/// produced by the expression is consumed.
 +pub fn walk_to_expr_usage<'tcx, T>(
 +    cx: &LateContext<'tcx>,
 +    e: &Expr<'tcx>,
 +    mut f: impl FnMut(Node<'tcx>, HirId) -> Option<T>,
 +) -> Option<T> {
 +    let map = cx.tcx.hir();
 +    let mut iter = map.parent_iter(e.hir_id);
 +    let mut child_id = e.hir_id;
 +
 +    while let Some((parent_id, parent)) = iter.next() {
 +        if let Some(x) = f(parent, child_id) {
 +            return Some(x);
 +        }
 +        let parent = match parent {
 +            Node::Expr(e) => e,
 +            Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
 +                child_id = parent_id;
 +                continue;
 +            },
 +            Node::Arm(a) if a.body.hir_id == child_id => {
 +                child_id = parent_id;
 +                continue;
 +            },
 +            _ => return None,
 +        };
 +        match parent.kind {
 +            ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
 +            ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
 +                child_id = id;
 +                iter = map.parent_iter(id);
 +            },
 +            ExprKind::Block(..) => child_id = parent_id,
 +            _ => return None,
 +        }
 +    }
 +    None
 +}
 +
 +/// Checks whether a given span has any comment token
 +/// This checks for all types of comment: line "//", block "/**", doc "///" "//!"
 +pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
 +    let Ok(snippet) = sm.span_to_snippet(span) else { return false };
 +    return tokenize(&snippet).any(|token| {
 +        matches!(
 +            token.kind,
 +            TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
 +        )
 +    });
 +}
 +
 +/// Return all the comments a given span contains
 +/// Comments are returned wrapped with their relevant delimiters
 +pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
 +    let snippet = sm.span_to_snippet(span).unwrap_or_default();
 +    let mut comments_buf: Vec<String> = Vec::new();
 +    let mut index: usize = 0;
 +
 +    for token in tokenize(&snippet) {
 +        let token_range = index..(index + token.len as usize);
 +        index += token.len as usize;
 +        match token.kind {
 +            TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => {
 +                if let Some(comment) = snippet.get(token_range) {
 +                    comments_buf.push(comment.to_string());
 +                }
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    comments_buf.join("\n")
 +}
 +
 +macro_rules! op_utils {
 +    ($($name:ident $assign:ident)*) => {
 +        /// Binary operation traits like `LangItem::Add`
 +        pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
 +
 +        /// Operator-Assign traits like `LangItem::AddAssign`
 +        pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
 +
 +        /// Converts `BinOpKind::Add` to `(LangItem::Add, LangItem::AddAssign)`, for example
 +        pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
 +            match kind {
 +                $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
 +                _ => None,
 +            }
 +        }
 +    };
 +}
 +
 +op_utils! {
 +    Add    AddAssign
 +    Sub    SubAssign
 +    Mul    MulAssign
 +    Div    DivAssign
 +    Rem    RemAssign
 +    BitXor BitXorAssign
 +    BitAnd BitAndAssign
 +    BitOr  BitOrAssign
 +    Shl    ShlAssign
 +    Shr    ShrAssign
 +}
index 5a63c290a315fccffd9fbcad2071c7280cbc5e19,0000000000000000000000000000000000000000..9a682fbe604ff7b3891a4f88c20e3cdaff38d52d
mode 100644,000000..100644
--- /dev/null
@@@ -1,1024 -1,0 +1,1055 @@@
-     Implied,
 +#![allow(clippy::similar_names)] // `expr` and `expn`
 +
 +use crate::is_path_diagnostic_item;
 +use crate::source::snippet_opt;
 +use crate::visitors::{for_each_expr, Descend};
 +
 +use arrayvec::ArrayVec;
 +use itertools::{izip, Either, Itertools};
 +use rustc_ast::ast::LitKind;
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, Node, QPath};
 +use rustc_lexer::unescape::unescape_literal;
 +use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
 +use rustc_lint::LateContext;
 +use rustc_parse_format::{self as rpf, Alignment};
 +use rustc_span::def_id::DefId;
 +use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
 +use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol};
 +use std::iter::{once, zip};
 +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;
 +    let _: Option<!> = for_each_expr(expr, |e| {
 +        if args.is_full() {
 +            if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() {
 +                panic_expn = PanicExpn::parse(cx, e);
 +            }
 +            ControlFlow::Continue(Descend::from(panic_expn.is_none()))
 +        } else if is_assert_arg(cx, e, expn) {
 +            args.push(e);
 +            ControlFlow::Continue(Descend::No)
 +        } else {
 +            ControlFlow::Continue(Descend::Yes)
 +        }
 +    });
 +    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)> {
 +    for_each_expr(expr, |e| {
 +        if !e.span.from_expansion() {
 +            return ControlFlow::Continue(Descend::No);
 +        }
 +        let e_expn = e.span.ctxt().outer_expn();
 +        if e_expn == expn {
 +            ControlFlow::Continue(Descend::Yes)
 +        } else if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) {
 +            ControlFlow::Break((e, e_expn))
 +        } else {
 +            ControlFlow::Continue(Descend::No)
 +        }
 +    })
 +}
 +
 +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,
 +    }
 +}
 +
 +/// The format string doesn't exist in the HIR, so we reassemble it from source code
 +#[derive(Debug)]
 +pub struct FormatString {
 +    /// Span of the whole format string literal, including `[r#]"`.
 +    pub span: Span,
 +    /// Snippet of the whole format string literal, including `[r#]"`.
 +    pub snippet: String,
 +    /// If the string is raw `r"..."`/`r#""#`, how many `#`s does it have on each side.
 +    pub style: Option<usize>,
 +    /// The unescaped value of the format string, e.g. `"val – {}"` for the literal
 +    /// `"val \u{2013} {}"`.
 +    pub unescaped: String,
 +    /// The format string split by format args like `{..}`.
 +    pub parts: Vec<Symbol>,
 +}
 +
 +impl FormatString {
 +    fn new(cx: &LateContext<'_>, pieces: &Expr<'_>) -> Option<Self> {
 +        // format_args!(r"a {} b \", 1);
 +        //
 +        // expands to
 +        //
 +        // ::core::fmt::Arguments::new_v1(&["a ", " b \\"],
 +        //      &[::core::fmt::ArgumentV1::new_display(&1)]);
 +        //
 +        // where `pieces` is the expression `&["a ", " b \\"]`. It has the span of `r"a {} b \"`
 +        let span = pieces.span;
 +        let snippet = snippet_opt(cx, span)?;
 +
 +        let (inner, style) = match tokenize(&snippet).next()?.kind {
 +            TokenKind::Literal { kind, .. } => {
 +                let style = match kind {
 +                    LiteralKind::Str { .. } => None,
 +                    LiteralKind::RawStr { n_hashes: Some(n), .. } => Some(n.into()),
 +                    _ => return None,
 +                };
 +
 +                let start = style.map_or(1, |n| 2 + n);
 +                let end = snippet.len() - style.map_or(1, |n| 1 + n);
 +
 +                (&snippet[start..end], style)
 +            },
 +            _ => return None,
 +        };
 +
 +        let mode = if style.is_some() {
 +            unescape::Mode::RawStr
 +        } else {
 +            unescape::Mode::Str
 +        };
 +
 +        let mut unescaped = String::with_capacity(inner.len());
 +        unescape_literal(inner, mode, &mut |_, ch| match ch {
 +            Ok(ch) => unescaped.push(ch),
 +            Err(e) if !e.is_fatal() => (),
 +            Err(e) => panic!("{e:?}"),
 +        });
 +
 +        let mut parts = Vec::new();
 +        let _: Option<!> = for_each_expr(pieces, |expr| {
 +            if let ExprKind::Lit(lit) = &expr.kind
 +                && let LitKind::Str(symbol, _) = lit.node
 +            {
 +                parts.push(symbol);
 +            }
 +            ControlFlow::Continue(())
 +        });
 +
 +        Some(Self {
 +            span,
 +            snippet,
 +            style,
 +            unescaped,
 +            parts,
 +        })
 +    }
 +}
 +
 +struct FormatArgsValues<'tcx> {
 +    /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for
 +    /// `format!("{x} {} {}", 1, z + 2)`.
 +    value_args: Vec<&'tcx Expr<'tcx>>,
 +    /// Maps an `rt::v1::Argument::position` or an `rt::v1::Count::Param` to its index in
 +    /// `value_args`
 +    pos_to_value_index: Vec<usize>,
 +    /// Used to check if a value is declared inline & to resolve `InnerSpan`s.
 +    format_string_span: SpanData,
 +}
 +
 +impl<'tcx> FormatArgsValues<'tcx> {
 +    fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self {
 +        let mut pos_to_value_index = Vec::new();
 +        let mut value_args = Vec::new();
 +        let _: Option<!> = for_each_expr(args, |expr| {
 +            if expr.span.ctxt() == args.span.ctxt() {
 +                // ArgumentV1::new_<format_trait>(<val>)
 +                // ArgumentV1::from_usize(<val>)
 +                if let ExprKind::Call(callee, [val]) = expr.kind
 +                    && let ExprKind::Path(QPath::TypeRelative(ty, _)) = callee.kind
 +                    && let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind
 +                    && path.segments.last().unwrap().ident.name == sym::ArgumentV1
 +                {
 +                    let val_idx = if val.span.ctxt() == expr.span.ctxt()
 +                        && let ExprKind::Field(_, field) = val.kind
 +                        && let Ok(idx) = field.name.as_str().parse()
 +                    {
 +                        // tuple index
 +                        idx
 +                    } else {
 +                        // assume the value expression is passed directly
 +                        pos_to_value_index.len()
 +                    };
 +
 +                    pos_to_value_index.push(val_idx);
 +                }
 +                ControlFlow::Continue(Descend::Yes)
 +            } else {
 +                // assume that any expr with a differing span is a value
 +                value_args.push(expr);
 +                ControlFlow::Continue(Descend::No)
 +            }
 +        });
 +
 +        Self {
 +            value_args,
 +            pos_to_value_index,
 +            format_string_span,
 +        }
 +    }
 +}
 +
 +/// The positions of a format argument's value, precision and width
 +///
 +/// A position is an index into the second argument of `Arguments::new_v1[_formatted]`
 +#[derive(Debug, Default, Copy, Clone)]
 +struct ParamPosition {
 +    /// The position stored in `rt::v1::Argument::position`.
 +    value: usize,
 +    /// The position stored in `rt::v1::FormatSpec::width` if it is a `Count::Param`.
 +    width: Option<usize>,
 +    /// The position stored in `rt::v1::FormatSpec::precision` if it is a `Count::Param`.
 +    precision: Option<usize>,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for ParamPosition {
 +    fn visit_expr_field(&mut self, field: &'tcx ExprField<'tcx>) {
 +        fn parse_count(expr: &Expr<'_>) -> Option<usize> {
 +            // ::core::fmt::rt::v1::Count::Param(1usize),
 +            if let ExprKind::Call(ctor, [val]) = expr.kind
 +                && let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind
 +                && path.segments.last()?.ident.name == sym::Param
 +                && let ExprKind::Lit(lit) = &val.kind
 +                && let LitKind::Int(pos, _) = lit.node
 +            {
 +                Some(pos as usize)
 +            } else {
 +                None
 +            }
 +        }
 +
 +        match field.ident.name {
 +            sym::position => {
 +                if let ExprKind::Lit(lit) = &field.expr.kind
 +                    && let LitKind::Int(pos, _) = lit.node
 +                {
 +                    self.value = pos as usize;
 +                }
 +            },
 +            sym::precision => {
 +                self.precision = parse_count(field.expr);
 +            },
 +            sym::width => {
 +                self.width = parse_count(field.expr);
 +            },
 +            _ => walk_expr(self, field.expr),
 +        }
 +    }
 +}
 +
 +/// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)`
 +fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option<impl Iterator<Item = ParamPosition> + 'tcx> {
 +    if let ExprKind::AddrOf(.., array) = fmt_arg.kind
 +        && let ExprKind::Array(specs) = array.kind
 +    {
 +        Some(specs.iter().map(|spec| {
 +            let mut position = ParamPosition::default();
 +            position.visit_expr(spec);
 +            position
 +        }))
 +    } else {
 +        None
 +    }
 +}
 +
 +/// `Span::from_inner`, but for `rustc_parse_format`'s `InnerSpan`
 +fn span_from_inner(base: SpanData, inner: rpf::InnerSpan) -> Span {
 +    Span::new(
 +        base.lo + BytePos::from_usize(inner.start),
 +        base.lo + BytePos::from_usize(inner.end),
 +        base.ctxt,
 +        base.parent,
 +    )
 +}
 +
 +/// How a format parameter is used in the format string
 +#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 +pub enum FormatParamKind {
 +    /// An implicit parameter , such as `{}` or `{:?}`.
 +    Implicit,
 +    /// A parameter with an explicit number, e.g. `{1}`, `{0:?}`, or `{:.0$}`
 +    Numbered,
 +    /// A parameter with an asterisk precision. e.g. `{:.*}`.
 +    Starred,
 +    /// A named parameter with a named `value_arg`, such as the `x` in `format!("{x}", x = 1)`.
 +    Named(Symbol),
 +    /// An implicit named parameter, such as the `y` in `format!("{y}")`.
 +    NamedInline(Symbol),
 +}
 +
 +/// Where a format parameter is being used in the format string
 +#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 +pub enum FormatParamUsage {
 +    /// Appears as an argument, e.g. `format!("{}", foo)`
 +    Argument,
 +    /// Appears as a width, e.g. `format!("{:width$}", foo, width = 1)`
 +    Width,
 +    /// Appears as a precision, e.g. `format!("{:.precision$}", foo, precision = 1)`
 +    Precision,
 +}
 +
 +/// A `FormatParam` is any place in a `FormatArgument` that refers to a supplied value, e.g.
 +///
 +/// ```
 +/// let precision = 2;
 +/// format!("{:.precision$}", 0.1234);
 +/// ```
 +///
 +/// has two `FormatParam`s, a [`FormatParamKind::Implicit`] `.kind` with a `.value` of `0.1234`
 +/// and a [`FormatParamKind::NamedInline("precision")`] `.kind` with a `.value` of `2`
 +#[derive(Debug, Copy, Clone)]
 +pub struct FormatParam<'tcx> {
 +    /// The expression this parameter refers to.
 +    pub value: &'tcx Expr<'tcx>,
 +    /// How this parameter refers to its `value`.
 +    pub kind: FormatParamKind,
 +    /// Where this format param is being used - argument/width/precision
 +    pub usage: FormatParamUsage,
 +    /// Span of the parameter, may be zero width. Includes the whitespace of implicit parameters.
 +    ///
 +    /// ```text
 +    /// format!("{}, {  }, {0}, {name}", ...);
 +    ///          ^    ~~    ~    ~~~~
 +    /// ```
 +    pub span: Span,
 +}
 +
 +impl<'tcx> FormatParam<'tcx> {
 +    fn new(
 +        mut kind: FormatParamKind,
 +        usage: FormatParamUsage,
 +        position: usize,
 +        inner: rpf::InnerSpan,
 +        values: &FormatArgsValues<'tcx>,
 +    ) -> Option<Self> {
 +        let value_index = *values.pos_to_value_index.get(position)?;
 +        let value = *values.value_args.get(value_index)?;
 +        let span = span_from_inner(values.format_string_span, inner);
 +
 +        // if a param is declared inline, e.g. `format!("{x}")`, the generated expr's span points
 +        // into the format string
 +        if let FormatParamKind::Named(name) = kind && values.format_string_span.contains(value.span.data()) {
 +            kind = FormatParamKind::NamedInline(name);
 +        }
 +
 +        Some(Self {
 +            value,
 +            kind,
 +            usage,
 +            span,
 +        })
 +    }
 +}
 +
 +/// Used by [width](https://doc.rust-lang.org/std/fmt/#width) and
 +/// [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers.
 +#[derive(Debug, Copy, Clone)]
 +pub enum Count<'tcx> {
 +    /// Specified with a literal number, stores the value.
 +    Is(usize, Span),
 +    /// Specified using `$` and `*` syntaxes. The `*` format is still considered to be
 +    /// `FormatParamKind::Numbered`.
 +    Param(FormatParam<'tcx>),
 +    /// Not specified.
-             rpf::Count::CountIs(val) => Self::Is(val, span_from_inner(values.format_string_span, inner?)),
++    Implied(Option<Span>),
 +}
 +
 +impl<'tcx> Count<'tcx> {
 +    fn new(
 +        usage: FormatParamUsage,
 +        count: rpf::Count<'_>,
 +        position: Option<usize>,
 +        inner: Option<rpf::InnerSpan>,
 +        values: &FormatArgsValues<'tcx>,
 +    ) -> Option<Self> {
++        let span = inner.map(|inner| span_from_inner(values.format_string_span, inner));
++
 +        Some(match count {
-             rpf::Count::CountImplied => Self::Implied,
++            rpf::Count::CountIs(val) => Self::Is(val, span?),
 +            rpf::Count::CountIsName(name, _) => Self::Param(FormatParam::new(
 +                FormatParamKind::Named(Symbol::intern(name)),
 +                usage,
 +                position?,
 +                inner?,
 +                values,
 +            )?),
 +            rpf::Count::CountIsParam(_) => Self::Param(FormatParam::new(
 +                FormatParamKind::Numbered,
 +                usage,
 +                position?,
 +                inner?,
 +                values,
 +            )?),
 +            rpf::Count::CountIsStar(_) => Self::Param(FormatParam::new(
 +                FormatParamKind::Starred,
 +                usage,
 +                position?,
 +                inner?,
 +                values,
 +            )?),
-         matches!(self, Count::Implied)
++            rpf::Count::CountImplied => Self::Implied(span),
 +        })
 +    }
 +
 +    pub fn is_implied(self) -> bool {
-         self.r#trait == sym::Display
-             && self.width.is_implied()
++        matches!(self, Count::Implied(_))
 +    }
 +
 +    pub fn param(self) -> Option<FormatParam<'tcx>> {
 +        match self {
 +            Count::Param(param) => Some(param),
 +            _ => None,
 +        }
 +    }
++
++    pub fn span(self) -> Option<Span> {
++        match self {
++            Count::Is(_, span) => Some(span),
++            Count::Param(param) => Some(param.span),
++            Count::Implied(span) => span,
++        }
++    }
 +}
 +
 +/// Specification for the formatting of an argument in the format string. See
 +/// <https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters> for the precise meanings.
 +#[derive(Debug)]
 +pub struct FormatSpec<'tcx> {
 +    /// Optionally specified character to fill alignment with.
 +    pub fill: Option<char>,
 +    /// Optionally specified alignment.
 +    pub align: Alignment,
 +    /// Packed version of various flags provided, see [`rustc_parse_format::Flag`].
 +    pub flags: u32,
 +    /// Represents either the maximum width or the integer precision.
 +    pub precision: Count<'tcx>,
 +    /// The minimum width, will be padded according to `width`/`align`
 +    pub width: Count<'tcx>,
 +    /// The formatting trait used by the argument, e.g. `sym::Display` for `{}`, `sym::Debug` for
 +    /// `{:?}`.
 +    pub r#trait: Symbol,
 +    pub trait_span: Option<Span>,
 +}
 +
 +impl<'tcx> FormatSpec<'tcx> {
 +    fn new(spec: rpf::FormatSpec<'_>, positions: ParamPosition, values: &FormatArgsValues<'tcx>) -> Option<Self> {
 +        Some(Self {
 +            fill: spec.fill,
 +            align: spec.align,
 +            flags: spec.flags,
 +            precision: Count::new(
 +                FormatParamUsage::Precision,
 +                spec.precision,
 +                positions.precision,
 +                spec.precision_span,
 +                values,
 +            )?,
 +            width: Count::new(
 +                FormatParamUsage::Width,
 +                spec.width,
 +                positions.width,
 +                spec.width_span,
 +                values,
 +            )?,
 +            r#trait: match spec.ty {
 +                "" => sym::Display,
 +                "?" => sym::Debug,
 +                "o" => sym!(Octal),
 +                "x" => sym!(LowerHex),
 +                "X" => sym!(UpperHex),
 +                "p" => sym::Pointer,
 +                "b" => sym!(Binary),
 +                "e" => sym!(LowerExp),
 +                "E" => sym!(UpperExp),
 +                _ => return None,
 +            },
 +            trait_span: spec
 +                .ty_span
 +                .map(|span| span_from_inner(values.format_string_span, span)),
 +        })
 +    }
 +
 +    /// Returns true if this format spec is unchanged from the default. e.g. returns true for `{}`,
 +    /// `{foo}` and `{2}`, but false for `{:?}`, `{foo:5}` and `{3:.5}`
 +    pub fn is_default(&self) -> bool {
++        self.r#trait == sym::Display && self.is_default_for_trait()
++    }
++
++    /// Has no other formatting specifiers than setting the format trait. returns true for `{}`,
++    /// `{foo}`, `{:?}`, but false for `{foo:5}`, `{3:.5?}`
++    pub fn is_default_for_trait(&self) -> bool {
++        self.width.is_implied()
 +            && self.precision.is_implied()
 +            && self.align == Alignment::AlignUnknown
 +            && self.flags == 0
 +    }
 +}
 +
 +/// A format argument, such as `{}`, `{foo:?}`.
 +#[derive(Debug)]
 +pub struct FormatArg<'tcx> {
 +    /// The parameter the argument refers to.
 +    pub param: FormatParam<'tcx>,
 +    /// How to format `param`.
 +    pub format: FormatSpec<'tcx>,
 +    /// span of the whole argument, `{..}`.
 +    pub span: Span,
 +}
 +
++impl<'tcx> FormatArg<'tcx> {
++    /// Span of the `:` and format specifiers
++    ///
++    /// ```ignore
++    /// format!("{:.}"), format!("{foo:.}")
++    ///           ^^                  ^^
++    /// ```
++    pub fn format_span(&self) -> Span {
++        let base = self.span.data();
++
++        // `base.hi` is `{...}|`, subtract 1 byte (the length of '}') so that it points before the closing
++        // brace `{...|}`
++        Span::new(self.param.span.hi(), base.hi - BytePos(1), base.ctxt, base.parent)
++    }
++}
++
 +/// A parsed `format_args!` expansion.
 +#[derive(Debug)]
 +pub struct FormatArgsExpn<'tcx> {
 +    /// The format string literal.
 +    pub format_string: FormatString,
 +    /// The format arguments, such as `{:?}`.
 +    pub args: Vec<FormatArg<'tcx>>,
 +    /// Has an added newline due to `println!()`/`writeln!()`/etc. The last format string part will
 +    /// include this added newline.
 +    pub newline: bool,
 +    /// Spans of the commas between the format string and explicit values, excluding any trailing
 +    /// comma
 +    ///
 +    /// ```ignore
 +    /// format!("..", 1, 2, 3,)
 +    /// //          ^  ^  ^
 +    /// ```
 +    comma_spans: Vec<Span>,
 +    /// Explicit values passed after the format string, ignoring implicit captures. `[1, z + 2]` for
 +    /// `format!("{x} {} {y}", 1, z + 2)`.
 +    explicit_values: Vec<&'tcx Expr<'tcx>>,
 +}
 +
 +impl<'tcx> FormatArgsExpn<'tcx> {
 +    /// Gets the spans of the commas inbetween the format string and explicit args, not including
 +    /// any trailing comma
 +    ///
 +    /// ```ignore
 +    /// format!("{} {}", a, b)
 +    /// //             ^  ^
 +    /// ```
 +    ///
 +    /// Ensures that the format string and values aren't coming from a proc macro that sets the
 +    /// output span to that of its input
 +    fn comma_spans(cx: &LateContext<'_>, explicit_values: &[&Expr<'_>], fmt_span: Span) -> Option<Vec<Span>> {
 +        // `format!("{} {} {c}", "one", "two", c = "three")`
 +        //                       ^^^^^  ^^^^^      ^^^^^^^
 +        let value_spans = explicit_values
 +            .iter()
 +            .map(|val| hygiene::walk_chain(val.span, fmt_span.ctxt()));
 +
 +        // `format!("{} {} {c}", "one", "two", c = "three")`
 +        //                     ^^     ^^     ^^^^^^
 +        let between_spans = once(fmt_span)
 +            .chain(value_spans)
 +            .tuple_windows()
 +            .map(|(start, end)| start.between(end));
 +
 +        let mut comma_spans = Vec::new();
 +        for between_span in between_spans {
 +            let mut offset = 0;
 +            let mut seen_comma = false;
 +
 +            for token in tokenize(&snippet_opt(cx, between_span)?) {
 +                match token.kind {
 +                    TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace => {},
 +                    TokenKind::Comma if !seen_comma => {
 +                        seen_comma = true;
 +
 +                        let base = between_span.data();
 +                        comma_spans.push(Span::new(
 +                            base.lo + BytePos(offset),
 +                            base.lo + BytePos(offset + 1),
 +                            base.ctxt,
 +                            base.parent,
 +                        ));
 +                    },
 +                    // named arguments, `start_val, name = end_val`
 +                    //                            ^^^^^^^^^ between_span
 +                    TokenKind::Ident | TokenKind::Eq if seen_comma => {},
 +                    // An unexpected token usually indicates the format string or a value came from a proc macro output
 +                    // that sets the span of its output to an input, e.g. `println!(some_proc_macro!("input"), ..)` that
 +                    // emits a string literal with the span set to that of `"input"`
 +                    _ => return None,
 +                }
 +                offset += token.len;
 +            }
 +
 +            if !seen_comma {
 +                return None;
 +            }
 +        }
 +
 +        Some(comma_spans)
 +    }
 +
 +    pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
 +        let macro_name = macro_backtrace(expr.span)
 +            .map(|macro_call| cx.tcx.item_name(macro_call.def_id))
 +            .find(|&name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))?;
 +        let newline = macro_name == sym::format_args_nl;
 +
 +        // ::core::fmt::Arguments::new_v1(pieces, args)
 +        // ::core::fmt::Arguments::new_v1_formatted(pieces, args, fmt, _unsafe_arg)
 +        if let ExprKind::Call(callee, [pieces, args, rest @ ..]) = expr.kind
 +            && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind
 +            && is_path_diagnostic_item(cx, ty, sym::Arguments)
 +            && matches!(seg.ident.as_str(), "new_v1" | "new_v1_formatted")
 +        {
 +            let format_string = FormatString::new(cx, pieces)?;
 +
 +            let mut parser = rpf::Parser::new(
 +                &format_string.unescaped,
 +                format_string.style,
 +                Some(format_string.snippet.clone()),
 +                // `format_string.unescaped` does not contain the appended newline
 +                false,
 +                rpf::ParseMode::Format,
 +            );
 +
 +            let parsed_args = parser
 +                .by_ref()
 +                .filter_map(|piece| match piece {
 +                    rpf::Piece::NextArgument(a) => Some(a),
 +                    rpf::Piece::String(_) => None,
 +                })
 +                .collect_vec();
 +            if !parser.errors.is_empty() {
 +                return None;
 +            }
 +
 +            let positions = if let Some(fmt_arg) = rest.first() {
 +                // If the argument contains format specs, `new_v1_formatted(_, _, fmt, _)`, parse
 +                // them.
 +
 +                Either::Left(parse_rt_fmt(fmt_arg)?)
 +            } else {
 +                // If no format specs are given, the positions are in the given order and there are
 +                // no `precision`/`width`s to consider.
 +
 +                Either::Right((0..).map(|n| ParamPosition {
 +                    value: n,
 +                    width: None,
 +                    precision: None,
 +                }))
 +            };
 +
 +            let values = FormatArgsValues::new(args, format_string.span.data());
 +
 +            let args = izip!(positions, parsed_args, parser.arg_places)
 +                .map(|(position, parsed_arg, arg_span)| {
 +                    Some(FormatArg {
 +                        param: FormatParam::new(
 +                            match parsed_arg.position {
 +                                rpf::Position::ArgumentImplicitlyIs(_) => FormatParamKind::Implicit,
 +                                rpf::Position::ArgumentIs(_) => FormatParamKind::Numbered,
 +                                // NamedInline is handled by `FormatParam::new()`
 +                                rpf::Position::ArgumentNamed(name) => FormatParamKind::Named(Symbol::intern(name)),
 +                            },
 +                            FormatParamUsage::Argument,
 +                            position.value,
 +                            parsed_arg.position_span,
 +                            &values,
 +                        )?,
 +                        format: FormatSpec::new(parsed_arg.format, position, &values)?,
 +                        span: span_from_inner(values.format_string_span, arg_span),
 +                    })
 +                })
 +                .collect::<Option<Vec<_>>>()?;
 +
 +            let mut explicit_values = values.value_args;
 +            // remove values generated for implicitly captured vars
 +            let len = explicit_values
 +                .iter()
 +                .take_while(|val| !format_string.span.contains(val.span))
 +                .count();
 +            explicit_values.truncate(len);
 +
 +            let comma_spans = Self::comma_spans(cx, &explicit_values, format_string.span)?;
 +
 +            Some(Self {
 +                format_string,
 +                args,
 +                newline,
 +                comma_spans,
 +                explicit_values,
 +            })
 +        } else {
 +            None
 +        }
 +    }
 +
 +    pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> {
 +        for_each_expr(expr, |e| {
 +            let e_ctxt = e.span.ctxt();
 +            if e_ctxt == expr.span.ctxt() {
 +                ControlFlow::Continue(Descend::Yes)
 +            } else if e_ctxt.outer_expn().is_descendant_of(expn_id) {
 +                if let Some(args) = FormatArgsExpn::parse(cx, e) {
 +                    ControlFlow::Break(args)
 +                } else {
 +                    ControlFlow::Continue(Descend::No)
 +                }
 +            } else {
 +                ControlFlow::Continue(Descend::No)
 +            }
 +        })
 +    }
 +
 +    /// Source callsite span of all inputs
 +    pub fn inputs_span(&self) -> Span {
 +        match *self.explicit_values {
 +            [] => self.format_string.span,
 +            [.., last] => self
 +                .format_string
 +                .span
 +                .to(hygiene::walk_chain(last.span, self.format_string.span.ctxt())),
 +        }
 +    }
 +
 +    /// Get the span of a value expanded to the previous comma, e.g. for the value `10`
 +    ///
 +    /// ```ignore
 +    /// format("{}.{}", 10, 11)
 +    /// //            ^^^^
 +    /// ```
 +    pub fn value_with_prev_comma_span(&self, value_id: HirId) -> Option<Span> {
 +        for (comma_span, value) in zip(&self.comma_spans, &self.explicit_values) {
 +            if value.hir_id == value_id {
 +                return Some(comma_span.to(hygiene::walk_chain(value.span, comma_span.ctxt())));
 +            }
 +        }
 +
 +        None
 +    }
 +
 +    /// Iterator of all format params, both values and those referenced by `width`/`precision`s.
 +    pub fn params(&'tcx self) -> impl Iterator<Item = FormatParam<'tcx>> {
 +        self.args
 +            .iter()
 +            .flat_map(|arg| [Some(arg.param), arg.format.precision.param(), arg.format.width.param()])
 +            .flatten()
 +    }
 +}
 +
 +/// A node with a `HirId` and a `Span`
 +pub trait HirNode {
 +    fn hir_id(&self) -> HirId;
 +    fn span(&self) -> Span;
 +}
 +
 +macro_rules! impl_hir_node {
 +    ($($t:ident),*) => {
 +        $(impl HirNode for hir::$t<'_> {
 +            fn hir_id(&self) -> HirId {
 +                self.hir_id
 +            }
 +            fn span(&self) -> Span {
 +                self.span
 +            }
 +        })*
 +    };
 +}
 +
 +impl_hir_node!(Expr, Pat);
 +
 +impl HirNode for hir::Item<'_> {
 +    fn hir_id(&self) -> HirId {
 +        self.hir_id()
 +    }
 +
 +    fn span(&self) -> Span {
 +        self.span
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d262b335d99d3adf782a59eaa02f88c84db50908
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++use rustc_index::bit_set::BitSet;
++use rustc_middle::mir;
++use rustc_mir_dataflow::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis};
++
++/// Determines liveness of each local purely based on `StorageLive`/`Dead`.
++#[derive(Copy, Clone)]
++pub(super) struct MaybeStorageLive;
++
++impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive {
++    type Domain = BitSet<mir::Local>;
++    const NAME: &'static str = "maybe_storage_live";
++
++    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
++        // bottom = dead
++        BitSet::new_empty(body.local_decls.len())
++    }
++
++    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
++        for arg in body.args_iter() {
++            state.insert(arg);
++        }
++    }
++}
++
++impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive {
++    type Idx = mir::Local;
++
++    fn statement_effect(&self, trans: &mut impl GenKill<Self::Idx>, stmt: &mir::Statement<'tcx>, _: mir::Location) {
++        match stmt.kind {
++            mir::StatementKind::StorageLive(l) => trans.gen(l),
++            mir::StatementKind::StorageDead(l) => trans.kill(l),
++            _ => (),
++        }
++    }
++
++    fn terminator_effect(
++        &self,
++        _trans: &mut impl GenKill<Self::Idx>,
++        _terminator: &mir::Terminator<'tcx>,
++        _loc: mir::Location,
++    ) {
++    }
++
++    fn call_return_effect(
++        &self,
++        _trans: &mut impl GenKill<Self::Idx>,
++        _block: mir::BasicBlock,
++        _return_places: CallReturnPlaces<'_, 'tcx>,
++    ) {
++        // Nothing to do when a call returns successfully
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..818e603f665e7d55422db9b3879a7dd1829a566a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,164 @@@
++use rustc_hir::{Expr, HirId};
++use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
++use rustc_middle::mir::{
++    traversal, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK,
++};
++use rustc_middle::ty::TyCtxt;
++
++mod maybe_storage_live;
++
++mod possible_borrower;
++pub use possible_borrower::PossibleBorrowerMap;
++
++mod possible_origin;
++
++mod transitive_relation;
++
++#[derive(Clone, Debug, Default)]
++pub struct LocalUsage {
++    /// The locations where the local is used, if any.
++    pub local_use_locs: Vec<Location>,
++    /// The locations where the local is consumed or mutated, if any.
++    pub local_consume_or_mutate_locs: Vec<Location>,
++}
++
++pub fn visit_local_usage(locals: &[Local], mir: &Body<'_>, location: Location) -> Option<Vec<LocalUsage>> {
++    let init = vec![
++        LocalUsage {
++            local_use_locs: Vec::new(),
++            local_consume_or_mutate_locs: Vec::new(),
++        };
++        locals.len()
++    ];
++
++    traversal::ReversePostorder::new(mir, location.block).try_fold(init, |usage, (tbb, tdata)| {
++        // Give up on loops
++        if tdata.terminator().successors().any(|s| s == location.block) {
++            return None;
++        }
++
++        let mut v = V {
++            locals,
++            location,
++            results: usage,
++        };
++        v.visit_basic_block_data(tbb, tdata);
++        Some(v.results)
++    })
++}
++
++struct V<'a> {
++    locals: &'a [Local],
++    location: Location,
++    results: Vec<LocalUsage>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for V<'a> {
++    fn visit_place(&mut self, place: &Place<'tcx>, ctx: PlaceContext, loc: Location) {
++        if loc.block == self.location.block && loc.statement_index <= self.location.statement_index {
++            return;
++        }
++
++        let local = place.local;
++
++        for (i, self_local) in self.locals.iter().enumerate() {
++            if local == *self_local {
++                if !matches!(
++                    ctx,
++                    PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)
++                ) {
++                    self.results[i].local_use_locs.push(loc);
++                }
++                if matches!(
++                    ctx,
++                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
++                        | PlaceContext::MutatingUse(MutatingUseContext::Borrow)
++                ) {
++                    self.results[i].local_consume_or_mutate_locs.push(loc);
++                }
++            }
++        }
++    }
++}
++
++/// Convenience wrapper around `visit_local_usage`.
++pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle::mir::Local) -> Option<bool> {
++    visit_local_usage(
++        &[local],
++        mir,
++        Location {
++            block: START_BLOCK,
++            statement_index: 0,
++        },
++    )
++    .map(|mut vec| {
++        let LocalUsage { local_use_locs, .. } = vec.remove(0);
++        local_use_locs
++            .into_iter()
++            .filter(|location| !is_local_assignment(mir, local, *location))
++            .count()
++            == 1
++    })
++}
++
++/// Returns the `mir::Body` containing the node associated with `hir_id`.
++#[allow(clippy::module_name_repetitions)]
++pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> &Body<'_> {
++    let body_owner_local_def_id = tcx.hir().enclosing_body_owner(hir_id);
++    tcx.optimized_mir(body_owner_local_def_id.to_def_id())
++}
++
++/// Tries to determine the `Local` corresponding to `expr`, if any.
++/// This function is expensive and should be used sparingly.
++pub fn expr_local(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option<Local> {
++    let mir = enclosing_mir(tcx, expr.hir_id);
++    mir.local_decls.iter_enumerated().find_map(|(local, local_decl)| {
++        if local_decl.source_info.span == expr.span {
++            Some(local)
++        } else {
++            None
++        }
++    })
++}
++
++/// Returns a vector of `mir::Location` where `local` is assigned.
++pub fn local_assignments(mir: &Body<'_>, local: Local) -> Vec<Location> {
++    let mut locations = Vec::new();
++    for (block, data) in mir.basic_blocks.iter_enumerated() {
++        for statement_index in 0..=data.statements.len() {
++            let location = Location { block, statement_index };
++            if is_local_assignment(mir, local, location) {
++                locations.push(location);
++            }
++        }
++    }
++    locations
++}
++
++// `is_local_assignment` is based on `is_place_assignment`:
++// https://github.com/rust-lang/rust/blob/b7413511dc85ec01ef4b91785f86614589ac6103/compiler/rustc_middle/src/mir/visit.rs#L1350
++fn is_local_assignment(mir: &Body<'_>, local: Local, location: Location) -> bool {
++    let Location { block, statement_index } = location;
++    let basic_block = &mir.basic_blocks[block];
++    if statement_index < basic_block.statements.len() {
++        let statement = &basic_block.statements[statement_index];
++        if let StatementKind::Assign(box (place, _)) = statement.kind {
++            place.as_local() == Some(local)
++        } else {
++            false
++        }
++    } else {
++        let terminator = basic_block.terminator();
++        match &terminator.kind {
++            TerminatorKind::Call { destination, .. } => destination.as_local() == Some(local),
++            TerminatorKind::InlineAsm { operands, .. } => operands.iter().any(|operand| {
++                if let InlineAsmOperand::Out { place: Some(place), .. } = operand {
++                    place.as_local() == Some(local)
++                } else {
++                    false
++                }
++            }),
++            _ => false,
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..25717bf3d2fee5329bffe8128ed66a95acdf3904
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,241 @@@
++use super::{
++    maybe_storage_live::MaybeStorageLive, possible_origin::PossibleOriginVisitor,
++    transitive_relation::TransitiveRelation,
++};
++use crate::ty::is_copy;
++use rustc_data_structures::fx::FxHashMap;
++use rustc_index::bit_set::{BitSet, HybridBitSet};
++use rustc_lint::LateContext;
++use rustc_middle::mir::{self, visit::Visitor as _, Mutability};
++use rustc_middle::ty::{self, visit::TypeVisitor};
++use rustc_mir_dataflow::{Analysis, ResultsCursor};
++use std::ops::ControlFlow;
++
++/// Collects the possible borrowers of each local.
++/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
++/// possible borrowers of `a`.
++#[allow(clippy::module_name_repetitions)]
++struct PossibleBorrowerVisitor<'a, 'b, 'tcx> {
++    possible_borrower: TransitiveRelation,
++    body: &'b mir::Body<'tcx>,
++    cx: &'a LateContext<'tcx>,
++    possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
++}
++
++impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> {
++    fn new(
++        cx: &'a LateContext<'tcx>,
++        body: &'b mir::Body<'tcx>,
++        possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
++    ) -> Self {
++        Self {
++            possible_borrower: TransitiveRelation::default(),
++            cx,
++            body,
++            possible_origin,
++        }
++    }
++
++    fn into_map(
++        self,
++        cx: &'a LateContext<'tcx>,
++        maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>,
++    ) -> PossibleBorrowerMap<'b, 'tcx> {
++        let mut map = FxHashMap::default();
++        for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
++            if is_copy(cx, self.body.local_decls[row].ty) {
++                continue;
++            }
++
++            let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len());
++            borrowers.remove(mir::Local::from_usize(0));
++            if !borrowers.is_empty() {
++                map.insert(row, borrowers);
++            }
++        }
++
++        let bs = BitSet::new_empty(self.body.local_decls.len());
++        PossibleBorrowerMap {
++            map,
++            maybe_live,
++            bitset: (bs.clone(), bs),
++        }
++    }
++}
++
++impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, 'tcx> {
++    fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
++        let lhs = place.local;
++        match rvalue {
++            mir::Rvalue::Ref(_, _, borrowed) => {
++                self.possible_borrower.add(borrowed.local, lhs);
++            },
++            other => {
++                if ContainsRegion
++                    .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty)
++                    .is_continue()
++                {
++                    return;
++                }
++                rvalue_locals(other, |rhs| {
++                    if lhs != rhs {
++                        self.possible_borrower.add(rhs, lhs);
++                    }
++                });
++            },
++        }
++    }
++
++    fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) {
++        if let mir::TerminatorKind::Call {
++            args,
++            destination: mir::Place { local: dest, .. },
++            ..
++        } = &terminator.kind
++        {
++            // TODO add doc
++            // If the call returns something with lifetimes,
++            // let's conservatively assume the returned value contains lifetime of all the arguments.
++            // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`.
++
++            let mut immutable_borrowers = vec![];
++            let mut mutable_borrowers = vec![];
++
++            for op in args {
++                match op {
++                    mir::Operand::Copy(p) | mir::Operand::Move(p) => {
++                        if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() {
++                            mutable_borrowers.push(p.local);
++                        } else {
++                            immutable_borrowers.push(p.local);
++                        }
++                    },
++                    mir::Operand::Constant(..) => (),
++                }
++            }
++
++            let mut mutable_variables: Vec<mir::Local> = mutable_borrowers
++                .iter()
++                .filter_map(|r| self.possible_origin.get(r))
++                .flat_map(HybridBitSet::iter)
++                .collect();
++
++            if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() {
++                mutable_variables.push(*dest);
++            }
++
++            for y in mutable_variables {
++                for x in &immutable_borrowers {
++                    self.possible_borrower.add(*x, y);
++                }
++                for x in &mutable_borrowers {
++                    self.possible_borrower.add(*x, y);
++                }
++            }
++        }
++    }
++}
++
++struct ContainsRegion;
++
++impl TypeVisitor<'_> for ContainsRegion {
++    type BreakTy = ();
++
++    fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow<Self::BreakTy> {
++        ControlFlow::BREAK
++    }
++}
++
++fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
++    use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use};
++
++    let mut visit_op = |op: &mir::Operand<'_>| match op {
++        mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local),
++        mir::Operand::Constant(..) => (),
++    };
++
++    match rvalue {
++        Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op),
++        Aggregate(_, ops) => ops.iter().for_each(visit_op),
++        BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => {
++            visit_op(lhs);
++            visit_op(rhs);
++        },
++        _ => (),
++    }
++}
++
++/// Result of `PossibleBorrowerVisitor`.
++#[allow(clippy::module_name_repetitions)]
++pub struct PossibleBorrowerMap<'b, 'tcx> {
++    /// Mapping `Local -> its possible borrowers`
++    pub map: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
++    maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>,
++    // Caches to avoid allocation of `BitSet` on every query
++    pub bitset: (BitSet<mir::Local>, BitSet<mir::Local>),
++}
++
++impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> {
++    pub fn new(cx: &'a LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self {
++        let possible_origin = {
++            let mut vis = PossibleOriginVisitor::new(mir);
++            vis.visit_body(mir);
++            vis.into_map(cx)
++        };
++        let maybe_storage_live_result = MaybeStorageLive
++            .into_engine(cx.tcx, mir)
++            .pass_name("redundant_clone")
++            .iterate_to_fixpoint()
++            .into_results_cursor(mir);
++        let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin);
++        vis.visit_body(mir);
++        vis.into_map(cx, maybe_storage_live_result)
++    }
++
++    /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`.
++    pub fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool {
++        self.bounded_borrowers(borrowers, borrowers, borrowed, at)
++    }
++
++    /// Returns true if the set of borrowers of `borrowed` living at `at` includes at least `below`
++    /// but no more than `above`.
++    pub fn bounded_borrowers(
++        &mut self,
++        below: &[mir::Local],
++        above: &[mir::Local],
++        borrowed: mir::Local,
++        at: mir::Location,
++    ) -> bool {
++        self.maybe_live.seek_after_primary_effect(at);
++
++        self.bitset.0.clear();
++        let maybe_live = &mut self.maybe_live;
++        if let Some(bitset) = self.map.get(&borrowed) {
++            for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) {
++                self.bitset.0.insert(b);
++            }
++        } else {
++            return false;
++        }
++
++        self.bitset.1.clear();
++        for b in below {
++            self.bitset.1.insert(*b);
++        }
++
++        if !self.bitset.0.superset(&self.bitset.1) {
++            return false;
++        }
++
++        for b in above {
++            self.bitset.0.remove(*b);
++        }
++
++        self.bitset.0.is_empty()
++    }
++
++    pub fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {
++        self.maybe_live.seek_after_primary_effect(at);
++        self.maybe_live.contains(local)
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8e7513d740ab397e3172f23bf31bbfec264009eb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,59 @@@
++use super::transitive_relation::TransitiveRelation;
++use crate::ty::is_copy;
++use rustc_data_structures::fx::FxHashMap;
++use rustc_index::bit_set::HybridBitSet;
++use rustc_lint::LateContext;
++use rustc_middle::mir;
++
++/// Collect possible borrowed for every `&mut` local.
++/// For example, `_1 = &mut _2` generate _1: {_2,...}
++/// Known Problems: not sure all borrowed are tracked
++#[allow(clippy::module_name_repetitions)]
++pub(super) struct PossibleOriginVisitor<'a, 'tcx> {
++    possible_origin: TransitiveRelation,
++    body: &'a mir::Body<'tcx>,
++}
++
++impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> {
++    pub fn new(body: &'a mir::Body<'tcx>) -> Self {
++        Self {
++            possible_origin: TransitiveRelation::default(),
++            body,
++        }
++    }
++
++    pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, HybridBitSet<mir::Local>> {
++        let mut map = FxHashMap::default();
++        for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
++            if is_copy(cx, self.body.local_decls[row].ty) {
++                continue;
++            }
++
++            let mut borrowers = self.possible_origin.reachable_from(row, self.body.local_decls.len());
++            borrowers.remove(mir::Local::from_usize(0));
++            if !borrowers.is_empty() {
++                map.insert(row, borrowers);
++            }
++        }
++        map
++    }
++}
++
++impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> {
++    fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
++        let lhs = place.local;
++        match rvalue {
++            // Only consider `&mut`, which can modify origin place
++            mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) |
++            // _2: &mut _;
++            // _3 = move _2
++            mir::Rvalue::Use(mir::Operand::Move(borrowed))  |
++            // _3 = move _2 as &mut _;
++            mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _)
++                => {
++                self.possible_origin.add(lhs, borrowed.local);
++            },
++            _ => {},
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7fe2960739fa252d0843cb7b9604198bdd442d41
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++use rustc_data_structures::fx::FxHashMap;
++use rustc_index::bit_set::HybridBitSet;
++use rustc_middle::mir;
++
++#[derive(Default)]
++pub(super) struct TransitiveRelation {
++    relations: FxHashMap<mir::Local, Vec<mir::Local>>,
++}
++
++impl TransitiveRelation {
++    pub fn add(&mut self, a: mir::Local, b: mir::Local) {
++        self.relations.entry(a).or_default().push(b);
++    }
++
++    pub fn reachable_from(&self, a: mir::Local, domain_size: usize) -> HybridBitSet<mir::Local> {
++        let mut seen = HybridBitSet::new_empty(domain_size);
++        let mut stack = vec![a];
++        while let Some(u) = stack.pop() {
++            if let Some(edges) = self.relations.get(&u) {
++                for &v in edges {
++                    if seen.insert(v) {
++                        stack.push(v);
++                    }
++                }
++            }
++        }
++        seen
++    }
++}
index 80098d9766c67b47c0e2e9604cbd76ac6e9ac3ce,0000000000000000000000000000000000000000..c5dcd7b31f58e7126b48d236669b5252d8b5ce03
mode 100644,000000..100644
--- /dev/null
@@@ -1,250 -1,0 +1,251 @@@
-         let radix = if lit.starts_with("0x") {
 +use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind};
 +use std::iter;
 +
 +#[derive(Debug, PartialEq, Eq, 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 {
++        let unsigned_lit = lit.trim_start_matches('-');
 +        // Determine delimiter for radix prefix, if present, and radix.
-         } else if lit.starts_with("0b") {
++        let radix = if unsigned_lit.starts_with("0x") {
 +            Radix::Hexadecimal
-         } else if lit.starts_with("0o") {
++        } else if unsigned_lit.starts_with("0b") {
 +            Radix::Binary
++        } else if unsigned_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)
 +        .and_then(|suffix_length| src.len().checked_sub(suffix_length))
 +        .map_or((src, None), |split_pos| {
 +            let (unsuffixed, suffix) = src.split_at(split_pos);
 +            (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 13938645fc3e500047b8dc5c0f384d823f0d8737,0000000000000000000000000000000000000000..bc851473430454d8b1dc0f4bdf7dece213606e4c
mode 100644,000000..100644
--- /dev/null
@@@ -1,193 -1,0 +1,157 @@@
- pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
- pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
 +//! 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 BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
 +pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
- pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
 +pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
 +pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"];
 +pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
- pub const CORE_ITER_INTO_ITER: [&str; 6] = ["core", "iter", "traits", "collect", "IntoIterator", "into_iter"];
 +pub const CORE_ITER_COLLECT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "collect"];
 +pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"];
 +pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"];
 +pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"];
- /// Preferably use the diagnostic item `sym::deref_method` where possible
- pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
- pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
 +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"];
- 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"];
 +#[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 FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
 +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 HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"];
 +pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"];
- pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
- pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
 +pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
 +pub const HASHSET_ITER: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "iter"];
 +#[cfg(feature = "internal")]
 +pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
 +#[cfg(feature = "internal")]
 +pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
- pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
 +pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
 +pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
 +pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
- pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"];
 +pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
 +#[cfg(feature = "internal")]
 +pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
 +#[cfg(feature = "internal")]
 +pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
 +#[cfg(feature = "internal")]
 +pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"];
 +#[cfg(feature = "internal")]
 +pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
 +pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"];
- /// 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 OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
- pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"];
- pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"];
 +pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
 +pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
 +pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"];
 +pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"];
 +pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"];
 +pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
 +pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
 +pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekable"];
 +pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
 +#[cfg_attr(not(unix), allow(clippy::invalid_paths))]
 +pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
 +pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
- #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"];
 +pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
 +pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
 +pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"];
 +pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"];
 +pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"];
 +pub const PTR_READ: [&str; 3] = ["core", "ptr", "read"];
 +pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"];
 +pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"];
 +pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"];
 +pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"];
 +pub const PTR_UNALIGNED_VOLATILE_LOAD: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_load"];
 +pub const PTR_UNALIGNED_VOLATILE_STORE: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_store"];
 +pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"];
 +pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
 +pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"];
 +pub const PTR_WRITE_VOLATILE: [&str; 3] = ["core", "ptr", "write_volatile"];
 +pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
 +pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
 +pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
 +pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"];
 +pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"];
- #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
- #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
- #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
- #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
- #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
- /// Preferably use the diagnostic item `sym::Result` where possible
- pub const RESULT: [&str; 3] = ["core", "result", "Result"];
- pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
- pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
 +pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
- pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"];
- pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
 +#[cfg(feature = "internal")]
 +pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"];
 +pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
 +pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
 +pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
 +pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"];
 +pub const SLICE_GET: [&str; 4] = ["core", "slice", "<impl [T]>", "get"];
 +pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
 +pub const SLICE_INTO: [&str; 4] = ["core", "slice", "<impl [T]>", "iter"];
 +pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
 +pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
 +pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
 +pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
 +pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
 +pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
 +pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
 +pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];
 +pub const STR_BYTES: [&str; 4] = ["core", "str", "<impl str>", "bytes"];
 +pub const STR_CHARS: [&str; 4] = ["core", "str", "<impl str>", "chars"];
 +pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
 +pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
 +pub const STR_FROM_UTF8_UNCHECKED: [&str; 4] = ["core", "str", "converts", "from_utf8_unchecked"];
 +pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
 +pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
 +#[cfg(feature = "internal")]
 +pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
 +#[cfg(feature = "internal")]
 +pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
 +pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
 +pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"];
 +pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
 +pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
 +pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
 +pub const VEC_DEQUE_ITER: [&str; 5] = ["alloc", "collections", "vec_deque", "VecDeque", "iter"];
 +pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
 +pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
 +pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
 +pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
 +pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
 +pub const PTR_NON_NULL: [&str; 4] = ["core", "ptr", "non_null", "NonNull"];
 +pub const INSTANT_NOW: [&str; 4] = ["std", "time", "Instant", "now"];
 +pub const INSTANT: [&str; 3] = ["std", "time", "Instant"];
index 5089987ef720081fa4fc769edc448d462cf78730,0000000000000000000000000000000000000000..aad7da61a8a54626530ef1821d3874df997ea72f
mode 100644,000000..100644
--- /dev/null
@@@ -1,1112 -1,0 +1,1109 @@@
- use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite};
 +//! Contains utility functions to generate suggestions.
 +#![deny(clippy::missing_docs_in_private_items)]
 +
-             let snip = snippet_with_applicability(cx, expr.span, default, applicability);
++use crate::source::{
++    snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite,
++};
 +use crate::ty::expr_sig;
 +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::{Closure, ExprKind, HirId, MutTy, TyKind};
 +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 +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 std::borrow::Cow;
 +use std::fmt::{Display, Write as _};
 +use std::ops::{Add, Neg, Not, Sub};
 +
 +/// A helper type to build suggestion correctly handling parentheses.
 +#[derive(Clone, Debug, 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),
 +        }
 +    }
 +}
 +
 +#[expect(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 {
-     fn fake_read(
-         &mut self,
-         _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>,
-         _: FakeReadCause,
-         _: HirId,
-     ) {}
++            let (snip, _) = snippet_with_context(cx, expr.span, ctxt, 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::Err => Sugg::NonParen(get_snippet(expr.span)),
 +            hir::ExprKind::DropTemps(inner) => Self::hir_from_snippet(inner, get_snippet),
 +            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 snippet_without_expansion = |cx, span: Span, default| {
 +            if span.from_expansion() {
 +                snippet_with_macro_callsite(cx, span, default)
 +            } else {
 +                snippet(cx, 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(snippet_without_expansion(cx, expr.span, default)),
 +            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(snippet_without_expansion(cx, expr.span, default)),
 +            ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
 +                AssocOp::DotDot,
 +                lhs.as_ref()
 +                    .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
 +                rhs.as_ref()
 +                    .map_or("".into(), |rhs| snippet_without_expansion(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_without_expansion(cx, lhs.span, default)),
 +                rhs.as_ref()
 +                    .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
 +            ),
 +            ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp(
 +                AssocOp::Assign,
 +                snippet_without_expansion(cx, lhs.span, default),
 +                snippet_without_expansion(cx, rhs.span, default),
 +            ),
 +            ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
 +                astbinop2assignop(op),
 +                snippet_without_expansion(cx, lhs.span, default),
 +                snippet_without_expansion(cx, rhs.span, default),
 +            ),
 +            ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
 +                AssocOp::from_ast_binop(op.node),
 +                snippet_without_expansion(cx, lhs.span, default),
 +                snippet_without_expansion(cx, rhs.span, default),
 +            ),
 +            ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp(
 +                AssocOp::As,
 +                snippet_without_expansion(cx, lhs.span, default),
 +                snippet_without_expansion(cx, ty.span, default),
 +            ),
 +            ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
 +                AssocOp::Colon,
 +                snippet_without_expansion(cx, lhs.span, default),
 +                snippet_without_expansion(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 prefix the expression with the `async` keyword.
 +    /// Can be used after `blockify` to create an async block.
 +    pub fn asyncify(self) -> Sugg<'static> {
 +        Sugg::NonParen(Cow::Owned(format!("async {self}")))
 +    }
 +
 +    /// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>`
 +    /// suggestion.
 +    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} {} {rhs}",
 +                op.to_ast_binop().expect("Those are AST ops").to_string()
 +            )
 +        },
 +        AssocOp::Assign => format!("{lhs} = {rhs}"),
 +        AssocOp::AssignOp(op) => {
 +            format!("{lhs} {}= {rhs}", token_kind_to_string(&token::BinOp(op)))
 +        },
 +        AssocOp::As => format!("{lhs} as {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!("{attr}\n{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!("{l}\n")
 +                    } else {
 +                        format!("{indent}{l}\n")
 +                    }
 +                })
 +                .collect::<String>();
 +
 +            self.span_suggestion(span, msg, format!("{new_item}\n{indent}"), applicability);
 +        }
 +    }
 +
 +    fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability) {
 +        let mut remove_span = item;
 +        let fmpos = cx.sess().source_map().lookup_byte_offset(remove_span.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, "", 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(&Closure { fn_decl, body, .. }) = closure.kind {
 +        let closure_body = cx.tcx.hir().body(body);
 +        // 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);
 +        let infcx = cx.tcx.infer_ctxt().build();
 +        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!("{}{end_snip}", self.suggestion_start);
 +        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 ty = match parent_expr.kind {
 +            ExprKind::MethodCall(_, receiver, call_args, _) => {
 +                if let Some(sig) = self
 +                    .cx
 +                    .typeck_results()
 +                    .type_dependent_def_id(parent_expr.hir_id)
 +                    .map(|did| self.cx.tcx.fn_sig(did).skip_binder())
 +                {
 +                    std::iter::once(receiver)
 +                        .chain(call_args.iter())
 +                        .position(|arg| arg.hir_id == cmt_hir_id)
 +                        .map(|i| sig.inputs()[i])
 +                } else {
 +                    return false;
 +                }
 +            },
 +            ExprKind::Call(func, call_args) => {
 +                if let Some(sig) = expr_sig(self.cx, func) {
 +                    call_args
 +                        .iter()
 +                        .position(|arg| arg.hir_id == cmt_hir_id)
 +                        .and_then(|i| sig.input(i))
 +                        .map(ty::Binder::skip_binder)
 +                } else {
 +                    return false;
 +                }
 +            },
 +            _ => return false,
 +        };
 +
 +        ty.map_or(false, |ty| matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref()))
 +    }
 +}
 +
 +impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
 +    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
 +
 +    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`
 +                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 => {
 +                            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, _: &PlaceWithHirId<'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 b344db634f612c6a4950e93cc79440db6ad89907,0000000000000000000000000000000000000000..b8824024e6c786bffb527a5a9c49fbcf05e95c57
mode 100644,000000..100644
--- /dev/null
@@@ -1,132 -1,0 +1,131 @@@
-                 .map(|s| &**s)
-                 .unwrap_or("lintcheck/lintcheck_crates.toml")
 +use clap::{Arg, ArgAction, ArgMatches, Command};
 +use std::env;
 +use std::path::PathBuf;
 +
 +fn get_clap_config() -> ArgMatches {
 +    Command::new("lintcheck")
 +        .about("run clippy on a set of crates and check output")
 +        .args([
 +            Arg::new("only")
 +                .action(ArgAction::Set)
 +                .value_name("CRATE")
 +                .long("only")
 +                .help("Only process a single crate of the list"),
 +            Arg::new("crates-toml")
 +                .action(ArgAction::Set)
 +                .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::new("threads")
 +                .action(ArgAction::Set)
 +                .value_name("N")
 +                .value_parser(clap::value_parser!(usize))
 +                .short('j')
 +                .long("jobs")
 +                .help("Number of threads to use, 0 automatic choice"),
 +            Arg::new("fix")
 +                .long("fix")
 +                .help("Runs cargo clippy --fix and checks if all suggestions apply"),
 +            Arg::new("filter")
 +                .long("filter")
 +                .action(ArgAction::Append)
 +                .value_name("clippy_lint_name")
 +                .help("Apply a filter to only collect specified lints, this also overrides `allow` attributes"),
 +            Arg::new("markdown")
 +                .long("markdown")
 +                .help("Change the reports table to use markdown links"),
 +            Arg::new("recursive")
 +                .long("--recursive")
 +                .help("Run clippy on the dependencies of crates specified in crates-toml")
 +                .conflicts_with("threads")
 +                .conflicts_with("fix"),
 +        ])
 +        .get_matches()
 +}
 +
 +#[derive(Debug, Clone)]
 +pub(crate) struct LintcheckConfig {
 +    /// max number of jobs to spawn (default 1)
 +    pub max_jobs: usize,
 +    /// we read the sources to check from here
 +    pub sources_toml_path: PathBuf,
 +    /// we save the clippy lint results here
 +    pub lintcheck_results_path: PathBuf,
 +    /// Check only a specified package
 +    pub only: Option<String>,
 +    /// whether to just run --fix and not collect all the warnings
 +    pub fix: bool,
 +    /// A list of lints that this lintcheck run should focus on
 +    pub lint_filter: Vec<String>,
 +    /// Indicate if the output should support markdown syntax
 +    pub markdown: bool,
 +    /// Run clippy on the dependencies of crates
 +    pub recursive: bool,
 +}
 +
 +impl LintcheckConfig {
 +    pub fn new() -> Self {
 +        let clap_config = get_clap_config();
 +
 +        // first, check if we got anything passed via the LINTCHECK_TOML env var,
 +        // if not, ask clap if we got any value for --crates-toml  <foo>
 +        // if not, use the default "lintcheck/lintcheck_crates.toml"
 +        let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| {
 +            clap_config
 +                .get_one::<String>("crates-toml")
-                 (rayon::current_num_threads() / 2) as usize
++                .map_or("lintcheck/lintcheck_crates.toml", |s| &**s)
 +                .into()
 +        });
 +
 +        let markdown = clap_config.contains_id("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.get_one::<usize>("threads") {
 +            Some(&0) => {
 +                // automatic choice
 +                // Rayon seems to return thread count so half that for core count
++                rayon::current_num_threads() / 2
 +            },
 +            Some(&threads) => threads,
 +            // no -j passed, use a single thread
 +            None => 1,
 +        };
 +
 +        let lint_filter: Vec<String> = clap_config
 +            .get_many::<String>("filter")
 +            .map(|iter| {
 +                iter.map(|lint_name| {
 +                    let mut filter = lint_name.replace('_', "-");
 +                    if !filter.starts_with("clippy::") {
 +                        filter.insert_str(0, "clippy::");
 +                    }
 +                    filter
 +                })
 +                .collect()
 +            })
 +            .unwrap_or_default();
 +
 +        LintcheckConfig {
 +            max_jobs,
 +            sources_toml_path,
 +            lintcheck_results_path,
 +            only: clap_config.get_one::<String>("only").map(String::from),
 +            fix: clap_config.contains_id("fix"),
 +            lint_filter,
 +            markdown,
 +            recursive: clap_config.contains_id("recursive"),
 +        }
 +    }
 +}
index 63221bab32d313fde2b44db339d6d835d789b224,0000000000000000000000000000000000000000..47724a2fedb0072c98ae11ec9149d8c7b0152f37
mode 100644,000000..100644
--- /dev/null
@@@ -1,67 -1,0 +1,67 @@@
- /// 1. Sends [DriverInfo] to the [crate::recursive::LintcheckServer] running on `addr`
 +use crate::recursive::{deserialize_line, serialize_line, DriverInfo};
 +
 +use std::io::{self, BufReader, Write};
 +use std::net::TcpStream;
 +use std::process::{self, Command, Stdio};
 +use std::{env, mem};
 +
++/// 1. Sends [`DriverInfo`] to the [`crate::recursive::LintcheckServer`] running on `addr`
 +/// 2. Receives [bool] from the server, if `false` returns `None`
 +/// 3. Otherwise sends the stderr of running `clippy-driver` to the server
 +fn run_clippy(addr: &str) -> Option<i32> {
 +    let driver_info = DriverInfo {
 +        package_name: env::var("CARGO_PKG_NAME").ok()?,
 +        crate_name: env::var("CARGO_CRATE_NAME").ok()?,
 +        version: env::var("CARGO_PKG_VERSION").ok()?,
 +    };
 +
 +    let mut stream = BufReader::new(TcpStream::connect(addr).unwrap());
 +
 +    serialize_line(&driver_info, stream.get_mut());
 +
 +    let should_run = deserialize_line::<bool, _>(&mut stream);
 +    if !should_run {
 +        return None;
 +    }
 +
 +    // Remove --cap-lints allow so that clippy runs and lints are emitted
 +    let mut include_next = true;
 +    let args = env::args().skip(1).filter(|arg| match arg.as_str() {
 +        "--cap-lints=allow" => false,
 +        "--cap-lints" => {
 +            include_next = false;
 +            false
 +        },
 +        _ => mem::replace(&mut include_next, true),
 +    });
 +
 +    let output = Command::new(env::var("CLIPPY_DRIVER").expect("missing env CLIPPY_DRIVER"))
 +        .args(args)
 +        .stdout(Stdio::inherit())
 +        .output()
 +        .expect("failed to run clippy-driver");
 +
 +    stream
 +        .get_mut()
 +        .write_all(&output.stderr)
 +        .unwrap_or_else(|e| panic!("{e:?} in {driver_info:?}"));
 +
 +    match output.status.code() {
 +        Some(0) => Some(0),
 +        code => {
 +            io::stderr().write_all(&output.stderr).unwrap();
 +            Some(code.expect("killed by signal"))
 +        },
 +    }
 +}
 +
 +pub fn drive(addr: &str) {
 +    process::exit(run_clippy(addr).unwrap_or_else(|| {
 +        Command::new("rustc")
 +            .args(env::args_os().skip(2))
 +            .status()
 +            .unwrap()
 +            .code()
 +            .unwrap()
 +    }))
 +}
index cc2b3e1acec7104e6d63249172fe5f6a6cba336c,0000000000000000000000000000000000000000..54c1b80c42dbf0fd320aa06677770d42db78ddf9
mode 100644,000000..100644
--- /dev/null
@@@ -1,872 -1,0 +1,874 @@@
-         let file = match Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) {
-             Ok(stripped) => format!("$CARGO_HOME/{}", stripped.display()),
-             Err(_) => 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)]
 +
 +mod config;
 +mod driver;
 +mod recursive;
 +
 +use crate::config::LintcheckConfig;
 +use crate::recursive::LintcheckServer;
 +
 +use std::collections::{HashMap, HashSet};
 +use std::env;
 +use std::env::consts::EXE_SUFFIX;
 +use std::fmt::Write as _;
 +use std::fs;
 +use std::io::ErrorKind;
 +use std::path::{Path, PathBuf};
 +use std::process::Command;
 +use std::sync::atomic::{AtomicUsize, Ordering};
 +use std::thread;
 +use std::time::Duration;
 +
 +use cargo_metadata::diagnostic::{Diagnostic, DiagnosticLevel};
 +use cargo_metadata::Message;
 +use rayon::prelude::*;
 +use serde::{Deserialize, Serialize};
 +use walkdir::{DirEntry, WalkDir};
 +
 +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>,
 +    #[serde(default)]
 +    recursive: RecursiveOptions,
 +}
 +
 +#[derive(Debug, Serialize, Deserialize, Default)]
 +struct RecursiveOptions {
 +    ignore: HashSet<String>,
 +}
 +
 +/// 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,
 +    file: String,
 +    line: usize,
 +    column: usize,
 +    lint_type: String,
 +    message: String,
 +    is_ice: bool,
 +}
 +
 +#[allow(unused)]
 +impl ClippyWarning {
 +    fn new(diag: Diagnostic, crate_name: &str, crate_version: &str) -> Option<Self> {
 +        let lint_type = diag.code?.code;
 +        if !(lint_type.contains("clippy") || diag.message.contains("clippy"))
 +            || diag.message.contains("could not read cargo metadata")
 +        {
 +            return None;
 +        }
 +
 +        let span = diag.spans.into_iter().find(|span| span.is_primary)?;
 +
-             ),
++        let file = if let Ok(stripped) = Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) {
++            format!("$CARGO_HOME/{}", stripped.display())
++        } else {
++            format!(
 +                "target/lintcheck/sources/{}-{}/{}",
 +                crate_name, crate_version, span.file_name
-             let _ = write!(output, "[`{}`]({}#L{})", file_with_pos, file, self.line);
++            )
 +        };
 +
 +        Some(Self {
 +            crate_name: crate_name.to_owned(),
 +            file,
 +            line: span.line_start,
 +            column: span.column_start,
 +            lint_type,
 +            message: diag.message,
 +            is_ice: diag.level == DiagnosticLevel::Ice,
 +        })
 +    }
 +
 +    fn to_output(&self, markdown: bool) -> String {
 +        let file_with_pos = format!("{}:{}:{}", &self.file, &self.line, &self.column);
 +        if markdown {
 +            let mut file = self.file.clone();
 +            if !file.starts_with('$') {
 +                file.insert_str(0, "../");
 +            }
 +
 +            let mut output = String::from("| ");
-             format!("{} {} \"{}\"\n", file_with_pos, self.lint_type, self.message)
++            let _ = write!(output, "[`{file_with_pos}`]({file}#L{})", self.line);
 +            let _ = write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.message);
 +            output.push('\n');
 +            output
 +        } else {
-             Err(ureq::Error::Transport(e)) => eprintln!("Error: {}", e),
++            format!("{file_with_pos} {} \"{}\"\n", self.lint_type, self.message)
 +        }
 +    }
 +}
 +
++#[allow(clippy::result_large_err)]
 +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),
-         eprintln!("retrying in {} seconds...", retries);
-         thread::sleep(Duration::from_secs(retries as u64));
++            Err(ureq::Error::Transport(e)) => eprintln!("Error: {e}"),
 +            Err(e) => return Err(e),
 +        }
-                 let url = format!("https://crates.io/api/v1/crates/{}/{}/download", name, version);
-                 println!("Downloading and extracting {} {} from {}", name, version, url);
++        eprintln!("retrying in {retries} seconds...");
++        thread::sleep(Duration::from_secs(u64::from(retries)));
 +        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 krate_file_path = krate_download_dir.join(format!("{}-{}.crate.tar.gz", name, version));
++                let url = format!("https://crates.io/api/v1/crates/{name}/{version}/download");
++                println!("Downloading and extracting {name} {version} from {url}");
 +                create_dirs(&krate_download_dir, &extract_dir);
 +
-                     path: extract_dir.join(format!("{}-{}/", name, version)),
++                let krate_file_path = krate_download_dir.join(format!("{name}-{version}.crate.tar.gz"));
 +                // 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(),
-                     repo_path.push(format!("{}-git", name));
++                    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
-                     println!("Cloning {} and checking out {}", url, commit);
++                    repo_path.push(format!("{name}-git"));
 +                    repo_path
 +                };
 +                // clone the repo if we have not done so
 +                if !repo_path.is_dir() {
-                         eprintln!("Failed to clone {} into {}", url, repo_path.display())
++                    println!("Cloning {url} and checking out {commit}");
 +                    if !Command::new("git")
 +                        .arg("clone")
 +                        .arg(url)
 +                        .arg(&repo_path)
 +                        .status()
 +                        .expect("Failed to clone git repo!")
 +                        .success()
 +                    {
-                     eprintln!("Failed to checkout {} of repo at {}", commit, repo_path.display())
++                        eprintln!("Failed to clone {url} into {}", repo_path.display());
 +                    }
 +                }
 +                // check out the commit/branch/whatever
 +                if !Command::new("git")
 +                    .args(["-c", "advice.detachedHead=false"])
 +                    .arg("checkout")
 +                    .arg(commit)
 +                    .current_dir(&repo_path)
 +                    .status()
 +                    .expect("Failed to check out commit")
 +                    .success()
 +                {
-                     println!("Deleting existing directory at {:?}", dest_crate_root);
++                    eprintln!("Failed to checkout {commit} of repo at {}", repo_path.display());
 +                }
 +
 +                Crate {
 +                    version: commit.clone(),
 +                    name: name.clone(),
 +                    path: repo_path,
 +                    options: options.clone(),
 +                }
 +            },
 +            CrateSource::Path { name, path, options } => {
++                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)
++                }
++
 +                // 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!("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)
-                 }
++                    println!("Deleting existing directory at {dest_crate_root:?}");
 +                    std::fs::remove_dir_all(&dest_crate_root).unwrap();
 +                }
 +
-             clippy_args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"])
++                println!("Copying {path:?} to {dest_crate_root:?}");
 +
 +                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
++    #[allow(clippy::too_many_arguments)]
 +    fn run_clippy_lints(
 +        &self,
 +        cargo_clippy_path: &Path,
 +        clippy_driver_path: &Path,
 +        target_dir_index: &AtomicUsize,
 +        total_crates_to_lint: usize,
 +        config: &LintcheckConfig,
 +        lint_filter: &Vec<String>,
 +        server: &Option<LintcheckServer>,
 +    ) -> 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 % config.max_jobs;
 +        let perc = (index * 100) / total_crates_to_lint;
 +
 +        if config.max_jobs == 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 cargo_clippy_args = if config.fix {
 +            vec!["--fix", "--"]
 +        } else {
 +            vec!["--", "--message-format=json", "--"]
 +        };
 +
 +        let mut clippy_args = Vec::<&str>::new();
 +        if let Some(options) = &self.options {
 +            for opt in options {
 +                clippy_args.push(opt);
 +            }
 +        } else {
-             clippy_args.extend(lint_filter.iter().map(|filter| filter.as_str()))
++            clippy_args.extend(["-Wclippy::pedantic", "-Wclippy::cargo"]);
 +        }
 +
 +        if lint_filter.is_empty() {
 +            clippy_args.push("--cap-lints=warn");
 +        } else {
 +            clippy_args.push("--cap-lints=allow");
-             .env(
-                 "CARGO_TARGET_DIR",
-                 shared_target_dir.join(format!("_{:?}", thread_index)),
-             )
++            clippy_args.extend(lint_filter.iter().map(std::string::String::as_str));
 +        }
 +
 +        if let Some(server) = server {
 +            let target = shared_target_dir.join("recursive");
 +
 +            // `cargo clippy` is a wrapper around `cargo check` that mainly sets `RUSTC_WORKSPACE_WRAPPER` to
 +            // `clippy-driver`. We do the same thing here with a couple changes:
 +            //
 +            // `RUSTC_WRAPPER` is used instead of `RUSTC_WORKSPACE_WRAPPER` so that we can lint all crate
 +            // dependencies rather than only workspace members
 +            //
 +            // The wrapper is set to the `lintcheck` so we can force enable linting and ignore certain crates
 +            // (see `crate::driver`)
 +            let status = Command::new("cargo")
 +                .arg("check")
 +                .arg("--quiet")
 +                .current_dir(&self.path)
 +                .env("CLIPPY_ARGS", clippy_args.join("__CLIPPY_HACKERY__"))
 +                .env("CARGO_TARGET_DIR", target)
 +                .env("RUSTC_WRAPPER", env::current_exe().unwrap())
 +                // Pass the absolute path so `crate::driver` can find `clippy-driver`, as it's executed in various
 +                // different working directories
 +                .env("CLIPPY_DRIVER", clippy_driver_path)
 +                .env("LINTCHECK_SERVER", server.local_addr.to_string())
 +                .status()
 +                .expect("failed to run cargo");
 +
 +            assert_eq!(status.code(), Some(0));
 +
 +            return Vec::new();
 +        }
 +
 +        cargo_clippy_args.extend(clippy_args);
 +
 +        let all_output = Command::new(&cargo_clippy_path)
 +            // use the looping index to create individual target dirs
-                     "ERROR: failed to apply some suggetion to {} / to (sub)crate {}",
-                     self.name, subcrate
++            .env("CARGO_TARGET_DIR", shared_target_dir.join(format!("_{thread_index:?}")))
 +            .args(&cargo_clippy_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 config.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!(
-         std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display()));
++                    "ERROR: failed to apply some suggetion to {} / to (sub)crate {subcrate}",
++                    self.name
 +                );
 +            }
 +            // fast path, we don't need the warnings anyway
 +            return Vec::new();
 +        }
 +
 +        // get all clippy warnings and ICEs
 +        let warnings: Vec<ClippyWarning> = Message::parse_stream(stdout.as_bytes())
 +            .filter_map(|msg| match msg {
 +                Ok(Message::CompilerMessage(message)) => ClippyWarning::new(message.message, &self.name, &self.version),
 +                _ => None,
 +            })
 +            .collect();
 +
 +        warnings
 +    }
 +}
 +
 +/// 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 `lintcheck_crates.toml` file
 +fn read_crates(toml_path: &Path) -> (Vec<CrateSource>, RecursiveOptions) {
 +    let toml_content: String =
-         toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e));
++        std::fs::read_to_string(toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display()));
 +    let crate_list: SourceList =
-     let tomlcrates: Vec<TomlCrate> = crate_list
-         .crates
-         .into_iter()
-         .map(|(_cratename, tomlcrate)| tomlcrate)
-         .collect();
++        toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{e}", toml_path.display()));
 +    // parse the hashmap of the toml file into a list of crates
-     tomlcrates.into_iter().for_each(|tk| {
++    let tomlcrates: Vec<TomlCrate> = crate_list.crates.into_values().collect();
 +
 +    // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate =>
 +    // multiple Cratesources)
 +    let mut crate_sources = Vec::new();
-             versions.iter().for_each(|ver| {
++    for tk in tomlcrates {
 +        if let Some(ref path) = tk.path {
 +            crate_sources.push(CrateSource::Path {
 +                name: tk.name.clone(),
 +                path: PathBuf::from(path),
 +                options: tk.options.clone(),
 +            });
 +        } else if let Some(ref versions) = tk.versions {
 +            // if we have multiple versions, save each one
-             })
++            for ver in versions.iter() {
 +                crate_sources.push(CrateSource::CratesIo {
 +                    name: tk.name.clone(),
 +                    version: ver.to_string(),
 +                    options: tk.options.clone(),
 +                });
-             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");
-             }
++            }
 +        } else if tk.git_url.is_some() && tk.git_hash.is_some() {
 +            // otherwise, we should have a git source
 +            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(),
 +            });
 +        } else {
 +            panic!("Invalid crate source: {tk:?}");
 +        }
 +
 +        // 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:?}");
++            assert_eq!(
++                tk.git_hash.is_some(),
++                tk.git_url.is_some(),
++                "Error: Encountered TomlCrate with only one of git_hash and git_url!"
++            );
++            assert!(
++                tk.path.is_none() || (tk.git_hash.is_none() && tk.versions.is_none()),
++                "Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields"
++            );
 +            unreachable!("Failed to translate TomlCrate into CrateSource!");
 +        }
-     stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint));
++    }
 +    // sort the crates
 +    crate_sources.sort();
 +
 +    (crate_sources, crate_list.recursive)
 +}
 +
 +/// 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.lint_type).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
-         .map(|(lint, count)| format!("| {:<50} |  {:>4} |\n", lint, count))
++    stats.sort_by_key(|(lint, count)| format!("{count:0>4}, {lint}"));
 +
 +    let mut header = String::from("| lint                                               | count |\n");
 +    header.push_str("| -------------------------------------------------- | ----- |\n");
 +    let stats_string = stats
 +        .iter()
-         if let Ok(metadata) = std::fs::metadata(&shared_target_dir) {
++        .map(|(lint, count)| format!("| {lint:<50} |  {count:>4} |\n"))
 +        .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, paths: [&Path; 2]) -> bool {
 +    if !lintcheck_logs_path.exists() {
 +        return true;
 +    }
 +
 +    let clippy_modified: std::time::SystemTime = {
 +        let [cargo, driver] = paths.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(cargo, driver)
 +    };
 +
 +    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
 +}
 +
++#[allow(clippy::too_many_lines)]
 +fn main() {
 +    // We're being executed as a `RUSTC_WRAPPER` as part of `--recursive`
 +    if let Ok(addr) = env::var("LINTCHECK_SERVER") {
 +        driver::drive(&addr);
 +    }
 +
 +    // 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 clippy's repo root!\nUse `cargo lintcheck` alternatively.");
 +        std::process::exit(3);
 +    }
 +
 +    let config = LintcheckConfig::new();
 +
 +    println!("Compiling clippy...");
 +    build_clippy();
 +    println!("Done compiling");
 +
 +    let cargo_clippy_path = fs::canonicalize(format!("target/debug/cargo-clippy{EXE_SUFFIX}")).unwrap();
 +    let clippy_driver_path = fs::canonicalize(format!("target/debug/clippy-driver{EXE_SUFFIX}")).unwrap();
 +
 +    // 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,
 +        [&cargo_clippy_path, &clippy_driver_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
-                 std::fs::remove_dir_all(&shared_target_dir)
++        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...");
-         let _ = fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive");
++                std::fs::remove_dir_all(shared_target_dir)
 +                    .expect("failed to remove target/lintcheck/shared_target_dir");
 +            }
 +        }
 +    }
 +
 +    // 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 clippy's warnings
 +    // flatten into one big list of warnings
 +
 +    let (crates, recursive_options) = 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 crates: Vec<Crate> = crates
 +        .into_iter()
 +        .filter(|krate| {
 +            if let Some(only_one_crate) = &config.only {
 +                let name = match krate {
 +                    CrateSource::CratesIo { name, .. }
 +                    | CrateSource::Git { name, .. }
 +                    | CrateSource::Path { name, .. } => name,
 +                };
 +
 +                name == only_one_crate
 +            } else {
 +                true
 +            }
 +        })
 +        .map(|krate| krate.download_and_extract())
 +        .collect();
 +
 +    if crates.is_empty() {
 +        eprintln!(
 +            "ERROR: could not find crate '{}' in lintcheck/lintcheck_crates.toml",
 +            config.only.unwrap(),
 +        );
 +        std::process::exit(1);
 +    }
 +
 +    // run parallel with rayon
 +
 +    // 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
 +
 +    rayon::ThreadPoolBuilder::new()
 +        .num_threads(config.max_jobs)
 +        .build_global()
 +        .unwrap();
 +
 +    let server = config.recursive.then(|| {
-     for (cratename, msg) in ices.iter() {
-         let _ = write!(text, "{}: '{}'", cratename, msg);
++        fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive").unwrap_or_default();
 +
 +        LintcheckServer::spawn(recursive_options)
 +    });
 +
 +    let mut clippy_warnings: Vec<ClippyWarning> = crates
 +        .par_iter()
 +        .flat_map(|krate| {
 +            krate.run_clippy_lints(
 +                &cargo_clippy_path,
 +                &clippy_driver_path,
 +                &counter,
 +                crates.len(),
 +                &config,
 +                &lint_filter,
 +                &server,
 +            )
 +        })
 +        .collect();
 +
 +    if let Some(server) = server {
 +        clippy_warnings.extend(server.warnings());
 +    }
 +
 +    // 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");
 +    }
 +    write!(text, "{}", all_msgs.join("")).unwrap();
 +    text.push_str("\n\n### ICEs:\n");
-         .filter(|(old_key, old_val)| new_stats.get::<&String>(&old_key) == Some(old_val))
++    for (cratename, msg) in &ices {
++        let _ = write!(text, "{cratename}: '{msg}'");
 +    }
 +
 +    println!("Writing logs to {}", config.lintcheck_results_path.display());
 +    fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap();
 +    fs::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()
-     same_in_both_hashmaps.iter().for_each(|(k, v)| {
++        .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
-     });
++    for (k, v) in &same_in_both_hashmaps {
 +        assert!(old_stats_deduped.remove(k) == Some(*v));
 +        assert!(new_stats_deduped.remove(k) == Some(*v));
-         .filter(|(new_key, _)| old_stats_deduped.get::<str>(&new_key).is_none())
++    }
 +
 +    println!("\nStats:");
 +
 +    // list all new counts  (key is in new stats but not in old stats)
 +    new_stats_deduped
 +        .iter()
-             println!("{} 0 => {}", new_key, new_value);
++        .filter(|(new_key, _)| old_stats_deduped.get::<str>(new_key).is_none())
 +        .for_each(|(new_key, new_value)| {
-         .filter(|(new_key, _new_val)| old_stats_deduped.get::<str>(&new_key).is_some())
++            println!("{new_key} 0 => {new_value}");
 +        });
 +
 +    // list all changed counts (key is in both maps but value differs)
 +    new_stats_deduped
 +        .iter()
-             let old_val = old_stats_deduped.get::<str>(&new_key).unwrap();
-             println!("{} {} => {}", new_key, old_val, new_val);
++        .filter(|(new_key, _new_val)| old_stats_deduped.get::<str>(new_key).is_some())
 +        .for_each(|(new_key, new_val)| {
-         .filter(|(old_key, _)| new_stats_deduped.get::<&String>(&old_key).is_none())
++            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()
-             println!("{} {} => 0", old_key, old_value);
++        .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)| {
-         if err.kind() != ErrorKind::AlreadyExists {
-             panic!("cannot create lintcheck target dir");
-         }
++            println!("{old_key} {old_value} => 0");
 +        });
 +}
 +
 +/// 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| {
-     std::fs::create_dir(&krate_download_dir).unwrap_or_else(|err| {
-         if err.kind() != ErrorKind::AlreadyExists {
-             panic!("cannot create crate download dir");
-         }
++        assert_eq!(
++            err.kind(),
++            ErrorKind::AlreadyExists,
++            "cannot create lintcheck target dir"
++        );
 +    });
-     std::fs::create_dir(&extract_dir).unwrap_or_else(|err| {
-         if err.kind() != ErrorKind::AlreadyExists {
-             panic!("cannot create crate extraction dir");
-         }
++    std::fs::create_dir(krate_download_dir).unwrap_or_else(|err| {
++        assert_eq!(err.kind(), ErrorKind::AlreadyExists, "cannot create crate download dir");
 +    });
-         .args(&args)
++    std::fs::create_dir(extract_dir).unwrap_or_else(|err| {
++        assert_eq!(
++            err.kind(),
++            ErrorKind::AlreadyExists,
++            "cannot create crate extraction dir"
++        );
 +    });
 +}
 +
 +/// Returns the path to the Clippy project directory
 +#[must_use]
 +fn clippy_project_root() -> &'static Path {
 +    Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap()
 +}
 +
 +#[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 67dcfc2b199c4a2f1f191e041a72a280d16da1f7,0000000000000000000000000000000000000000..49072e65192f21c151669b6fad5d5f16d377e8a3
mode 100644,000000..100644
--- /dev/null
@@@ -1,123 -1,0 +1,123 @@@
- //! this allows [crate::driver] to be run for every dependency. The driver connects to
- //! [LintcheckServer] to ask if it should be skipped, and if not sends the stderr of running clippy
- //! on the crate to the server
 +//! In `--recursive` mode we set the `lintcheck` binary as the `RUSTC_WRAPPER` of `cargo check`,
-             sender,
++//! this allows [`crate::driver`] to be run for every dependency. The driver connects to
++//! [`LintcheckServer`] to ask if it should be skipped, and if not sends the stderr of running
++//! clippy on the crate to the server
 +
 +use crate::ClippyWarning;
 +use crate::RecursiveOptions;
 +
 +use std::collections::HashSet;
 +use std::io::{BufRead, BufReader, Read, Write};
 +use std::net::{SocketAddr, TcpListener, TcpStream};
 +use std::sync::{Arc, Mutex};
 +use std::thread;
 +
 +use cargo_metadata::diagnostic::Diagnostic;
 +use crossbeam_channel::{Receiver, Sender};
 +use serde::de::DeserializeOwned;
 +use serde::{Deserialize, Serialize};
 +
 +#[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize)]
 +pub(crate) struct DriverInfo {
 +    pub package_name: String,
 +    pub crate_name: String,
 +    pub version: String,
 +}
 +
 +pub(crate) fn serialize_line<T, W>(value: &T, writer: &mut W)
 +where
 +    T: Serialize,
 +    W: Write,
 +{
 +    let mut buf = serde_json::to_vec(&value).expect("failed to serialize");
 +    buf.push(b'\n');
 +    writer.write_all(&buf).expect("write_all failed");
 +}
 +
 +pub(crate) fn deserialize_line<T, R>(reader: &mut R) -> T
 +where
 +    T: DeserializeOwned,
 +    R: BufRead,
 +{
 +    let mut string = String::new();
 +    reader.read_line(&mut string).expect("read_line failed");
 +    serde_json::from_str(&string).expect("failed to deserialize")
 +}
 +
 +fn process_stream(
 +    stream: TcpStream,
 +    sender: &Sender<ClippyWarning>,
 +    options: &RecursiveOptions,
 +    seen: &Mutex<HashSet<DriverInfo>>,
 +) {
 +    let mut stream = BufReader::new(stream);
 +
 +    let driver_info: DriverInfo = deserialize_line(&mut stream);
 +
 +    let unseen = seen.lock().unwrap().insert(driver_info.clone());
 +    let ignored = options.ignore.contains(&driver_info.package_name);
 +    let should_run = unseen && !ignored;
 +
 +    serialize_line(&should_run, stream.get_mut());
 +
 +    let mut stderr = String::new();
 +    stream.read_to_string(&mut stderr).unwrap();
 +
 +    let messages = stderr
 +        .lines()
 +        .filter_map(|json_msg| serde_json::from_str::<Diagnostic>(json_msg).ok())
 +        .filter_map(|diag| ClippyWarning::new(diag, &driver_info.package_name, &driver_info.version));
 +
 +    for message in messages {
 +        sender.send(message).unwrap();
 +    }
 +}
 +
 +pub(crate) struct LintcheckServer {
 +    pub local_addr: SocketAddr,
 +    receiver: Receiver<ClippyWarning>,
 +    sender: Arc<Sender<ClippyWarning>>,
 +}
 +
 +impl LintcheckServer {
 +    pub fn spawn(options: RecursiveOptions) -> Self {
 +        let listener = TcpListener::bind("localhost:0").unwrap();
 +        let local_addr = listener.local_addr().unwrap();
 +
 +        let (sender, receiver) = crossbeam_channel::unbounded::<ClippyWarning>();
 +        let sender = Arc::new(sender);
 +        // The spawned threads hold a `Weak<Sender>` so that they don't keep the channel connected
 +        // indefinitely
 +        let sender_weak = Arc::downgrade(&sender);
 +
 +        // Ignore dependencies multiple times, e.g. for when it's both checked and compiled for a
 +        // build dependency
 +        let seen = Mutex::default();
 +
 +        thread::spawn(move || {
 +            thread::scope(|s| {
 +                s.spawn(|| {
 +                    while let Ok((stream, _)) = listener.accept() {
 +                        let sender = sender_weak.upgrade().expect("received connection after server closed");
 +                        let options = &options;
 +                        let seen = &seen;
 +                        s.spawn(move || process_stream(stream, &sender, options, seen));
 +                    }
 +                });
 +            });
 +        });
 +
 +        Self {
 +            local_addr,
 +            receiver,
++            sender,
 +        }
 +    }
 +
 +    pub fn warnings(self) -> impl Iterator<Item = ClippyWarning> {
 +        // causes the channel to become disconnected so that the receiver iterator ends
 +        drop(self.sender);
 +
 +        self.receiver.into_iter()
 +    }
 +}
index 49b13cb54e71ebd43665ea766d0868fafe2d182a,0000000000000000000000000000000000000000..748d8a317160f415ec61252dd5f92e316dabaecc
mode 100644,000000..100644
--- /dev/null
@@@ -1,3 -1,0 +1,3 @@@
- channel = "nightly-2022-10-06"
 +[toolchain]
++channel = "nightly-2022-10-20"
 +components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
index bd27bc7938f8ef5aeb0a02ab243f1dce20150240,0000000000000000000000000000000000000000..c033ad294a38d974e0e9e65497a15d58c3b95e40
mode 100644,000000..100644
--- /dev/null
@@@ -1,600 -1,0 +1,606 @@@
 +// autogenerated. Please look at /clippy_dev/src/update_lints.rs
 +
 +macro_rules! include_lint {
 +    ($file_name: expr) => {
 +        include_str!($file_name)
 +    };
 +}
 +
 +macro_rules! docs {
 +    ($($lint_name: expr,)*) => {
 +        pub fn explain(lint: &str) {
 +            println!("{}", match lint {
 +                $(
 +                    $lint_name => include_lint!(concat!("docs/", concat!($lint_name, ".txt"))),
 +                )*
 +                _ => "unknown lint",
 +            })
 +        }
 +    }
 +}
 +
 +docs! {
 +    "absurd_extreme_comparisons",
 +    "alloc_instead_of_core",
 +    "allow_attributes_without_reason",
 +    "almost_complete_letter_range",
 +    "almost_swapped",
 +    "approx_constant",
 +    "arithmetic_side_effects",
 +    "as_conversions",
++    "as_ptr_cast_mut",
 +    "as_underscore",
 +    "assertions_on_constants",
 +    "assertions_on_result_states",
 +    "assign_op_pattern",
 +    "async_yields_async",
 +    "await_holding_invalid_type",
 +    "await_holding_lock",
 +    "await_holding_refcell_ref",
 +    "bad_bit_mask",
 +    "bind_instead_of_map",
 +    "blanket_clippy_restriction_lints",
 +    "blocks_in_if_conditions",
 +    "bool_assert_comparison",
 +    "bool_comparison",
 +    "bool_to_int_with_if",
 +    "borrow_as_ptr",
 +    "borrow_deref_ref",
 +    "borrow_interior_mutable_const",
 +    "borrowed_box",
 +    "box_collection",
 +    "box_default",
 +    "boxed_local",
 +    "branches_sharing_code",
 +    "builtin_type_shadow",
 +    "bytes_count_to_len",
 +    "bytes_nth",
 +    "cargo_common_metadata",
 +    "case_sensitive_file_extension_comparisons",
 +    "cast_abs_to_unsigned",
 +    "cast_enum_constructor",
 +    "cast_enum_truncation",
 +    "cast_lossless",
++    "cast_nan_to_int",
 +    "cast_possible_truncation",
 +    "cast_possible_wrap",
 +    "cast_precision_loss",
 +    "cast_ptr_alignment",
 +    "cast_ref_to_mut",
 +    "cast_sign_loss",
 +    "cast_slice_different_sizes",
 +    "cast_slice_from_raw_parts",
 +    "char_lit_as_u8",
 +    "chars_last_cmp",
 +    "chars_next_cmp",
 +    "checked_conversions",
 +    "clone_double_ref",
 +    "clone_on_copy",
 +    "clone_on_ref_ptr",
 +    "cloned_instead_of_copied",
 +    "cmp_nan",
 +    "cmp_null",
 +    "cmp_owned",
 +    "cognitive_complexity",
 +    "collapsible_else_if",
 +    "collapsible_if",
 +    "collapsible_match",
 +    "collapsible_str_replace",
 +    "comparison_chain",
 +    "comparison_to_empty",
 +    "copy_iterator",
 +    "crate_in_macro_def",
 +    "create_dir",
 +    "crosspointer_transmute",
 +    "dbg_macro",
 +    "debug_assert_with_mut_call",
 +    "decimal_literal_representation",
 +    "declare_interior_mutable_const",
 +    "default_instead_of_iter_empty",
 +    "default_numeric_fallback",
 +    "default_trait_access",
 +    "default_union_representation",
 +    "deprecated_cfg_attr",
 +    "deprecated_semver",
 +    "deref_addrof",
 +    "deref_by_slicing",
 +    "derivable_impls",
 +    "derive_hash_xor_eq",
 +    "derive_ord_xor_partial_ord",
 +    "derive_partial_eq_without_eq",
 +    "disallowed_macros",
 +    "disallowed_methods",
 +    "disallowed_names",
 +    "disallowed_script_idents",
 +    "disallowed_types",
 +    "diverging_sub_expression",
 +    "doc_link_with_quotes",
 +    "doc_markdown",
 +    "double_comparisons",
 +    "double_must_use",
 +    "double_neg",
 +    "double_parens",
 +    "drop_copy",
 +    "drop_non_drop",
 +    "drop_ref",
 +    "duplicate_mod",
 +    "duplicate_underscore_argument",
 +    "duration_subsec",
 +    "else_if_without_else",
 +    "empty_drop",
 +    "empty_enum",
 +    "empty_line_after_outer_attr",
 +    "empty_loop",
 +    "empty_structs_with_brackets",
 +    "enum_clike_unportable_variant",
 +    "enum_glob_use",
 +    "enum_variant_names",
 +    "eq_op",
 +    "equatable_if_let",
 +    "erasing_op",
 +    "err_expect",
 +    "excessive_precision",
 +    "exhaustive_enums",
 +    "exhaustive_structs",
 +    "exit",
 +    "expect_fun_call",
 +    "expect_used",
 +    "expl_impl_clone_on_copy",
 +    "explicit_auto_deref",
 +    "explicit_counter_loop",
 +    "explicit_deref_methods",
 +    "explicit_into_iter_loop",
 +    "explicit_iter_loop",
 +    "explicit_write",
 +    "extend_with_drain",
 +    "extra_unused_lifetimes",
 +    "fallible_impl_from",
 +    "field_reassign_with_default",
 +    "filetype_is_file",
 +    "filter_map_identity",
 +    "filter_map_next",
 +    "filter_next",
 +    "flat_map_identity",
 +    "flat_map_option",
 +    "float_arithmetic",
 +    "float_cmp",
 +    "float_cmp_const",
 +    "float_equality_without_abs",
 +    "fn_address_comparisons",
 +    "fn_params_excessive_bools",
 +    "fn_to_numeric_cast",
 +    "fn_to_numeric_cast_any",
 +    "fn_to_numeric_cast_with_truncation",
 +    "for_kv_map",
 +    "forget_copy",
 +    "forget_non_drop",
 +    "forget_ref",
 +    "format_in_format_args",
 +    "format_push_string",
 +    "from_iter_instead_of_collect",
 +    "from_over_into",
 +    "from_str_radix_10",
 +    "future_not_send",
 +    "get_first",
 +    "get_last_with_len",
 +    "get_unwrap",
 +    "identity_op",
 +    "if_let_mutex",
 +    "if_not_else",
 +    "if_same_then_else",
 +    "if_then_some_else_none",
 +    "ifs_same_cond",
 +    "implicit_clone",
 +    "implicit_hasher",
 +    "implicit_return",
 +    "implicit_saturating_add",
 +    "implicit_saturating_sub",
 +    "imprecise_flops",
 +    "inconsistent_digit_grouping",
 +    "inconsistent_struct_constructor",
 +    "index_refutable_slice",
 +    "indexing_slicing",
 +    "ineffective_bit_mask",
 +    "inefficient_to_string",
 +    "infallible_destructuring_match",
 +    "infinite_iter",
 +    "inherent_to_string",
 +    "inherent_to_string_shadow_display",
 +    "init_numbered_fields",
 +    "inline_always",
 +    "inline_asm_x86_att_syntax",
 +    "inline_asm_x86_intel_syntax",
 +    "inline_fn_without_body",
 +    "inspect_for_each",
 +    "int_plus_one",
 +    "integer_arithmetic",
 +    "integer_division",
 +    "into_iter_on_ref",
 +    "invalid_null_ptr_usage",
 +    "invalid_regex",
 +    "invalid_upcast_comparisons",
 +    "invalid_utf8_in_unchecked",
 +    "invisible_characters",
 +    "is_digit_ascii_radix",
 +    "items_after_statements",
 +    "iter_cloned_collect",
 +    "iter_count",
 +    "iter_kv_map",
 +    "iter_next_loop",
 +    "iter_next_slice",
 +    "iter_not_returning_iterator",
 +    "iter_nth",
 +    "iter_nth_zero",
 +    "iter_on_empty_collections",
 +    "iter_on_single_items",
 +    "iter_overeager_cloned",
 +    "iter_skip_next",
 +    "iter_with_drain",
 +    "iterator_step_by_zero",
 +    "just_underscores_and_digits",
 +    "large_const_arrays",
 +    "large_digit_groups",
 +    "large_enum_variant",
 +    "large_include_file",
 +    "large_stack_arrays",
 +    "large_types_passed_by_value",
 +    "len_without_is_empty",
 +    "len_zero",
 +    "let_and_return",
 +    "let_underscore_drop",
 +    "let_underscore_lock",
 +    "let_underscore_must_use",
 +    "let_unit_value",
 +    "linkedlist",
 +    "lossy_float_literal",
 +    "macro_use_imports",
 +    "main_recursion",
 +    "manual_assert",
 +    "manual_async_fn",
 +    "manual_bits",
 +    "manual_clamp",
++    "manual_filter",
 +    "manual_filter_map",
 +    "manual_find",
 +    "manual_find_map",
 +    "manual_flatten",
 +    "manual_instant_elapsed",
 +    "manual_map",
 +    "manual_memcpy",
 +    "manual_non_exhaustive",
 +    "manual_ok_or",
 +    "manual_range_contains",
 +    "manual_rem_euclid",
 +    "manual_retain",
 +    "manual_saturating_arithmetic",
 +    "manual_split_once",
 +    "manual_str_repeat",
 +    "manual_string_new",
 +    "manual_strip",
 +    "manual_swap",
 +    "manual_unwrap_or",
 +    "many_single_char_names",
 +    "map_clone",
 +    "map_collect_result_unit",
 +    "map_entry",
 +    "map_err_ignore",
 +    "map_flatten",
 +    "map_identity",
 +    "map_unwrap_or",
 +    "match_as_ref",
 +    "match_bool",
 +    "match_like_matches_macro",
 +    "match_on_vec_items",
 +    "match_overlapping_arm",
 +    "match_ref_pats",
 +    "match_result_ok",
 +    "match_same_arms",
 +    "match_single_binding",
 +    "match_str_case_mismatch",
 +    "match_wild_err_arm",
 +    "match_wildcard_for_single_variants",
 +    "maybe_infinite_iter",
 +    "mem_forget",
 +    "mem_replace_option_with_none",
 +    "mem_replace_with_default",
 +    "mem_replace_with_uninit",
 +    "min_max",
 +    "mismatched_target_os",
 +    "mismatching_type_param_order",
 +    "misrefactored_assign_op",
 +    "missing_const_for_fn",
 +    "missing_docs_in_private_items",
 +    "missing_enforced_import_renames",
 +    "missing_errors_doc",
 +    "missing_inline_in_public_items",
 +    "missing_panics_doc",
 +    "missing_safety_doc",
 +    "missing_spin_loop",
++    "missing_trait_methods",
 +    "mistyped_literal_suffixes",
 +    "mixed_case_hex_literals",
 +    "mixed_read_write_in_expression",
 +    "mod_module_files",
 +    "module_inception",
 +    "module_name_repetitions",
 +    "modulo_arithmetic",
 +    "modulo_one",
 +    "multi_assignments",
 +    "multiple_crate_versions",
 +    "multiple_inherent_impl",
 +    "must_use_candidate",
 +    "must_use_unit",
 +    "mut_from_ref",
 +    "mut_mut",
 +    "mut_mutex_lock",
 +    "mut_range_bound",
 +    "mutable_key_type",
 +    "mutex_atomic",
 +    "mutex_integer",
 +    "naive_bytecount",
 +    "needless_arbitrary_self_type",
 +    "needless_bitwise_bool",
 +    "needless_bool",
 +    "needless_borrow",
 +    "needless_borrowed_reference",
 +    "needless_collect",
 +    "needless_continue",
 +    "needless_doctest_main",
 +    "needless_for_each",
 +    "needless_late_init",
 +    "needless_lifetimes",
 +    "needless_match",
 +    "needless_option_as_deref",
 +    "needless_option_take",
 +    "needless_parens_on_range_literals",
 +    "needless_pass_by_value",
 +    "needless_question_mark",
 +    "needless_range_loop",
 +    "needless_return",
 +    "needless_splitn",
 +    "needless_update",
 +    "neg_cmp_op_on_partial_ord",
 +    "neg_multiply",
 +    "negative_feature_names",
 +    "never_loop",
 +    "new_ret_no_self",
 +    "new_without_default",
 +    "no_effect",
 +    "no_effect_replace",
 +    "no_effect_underscore_binding",
 +    "non_ascii_literal",
 +    "non_octal_unix_permissions",
 +    "non_send_fields_in_send_ty",
 +    "nonminimal_bool",
 +    "nonsensical_open_options",
 +    "nonstandard_macro_braces",
 +    "not_unsafe_ptr_arg_deref",
 +    "obfuscated_if_else",
 +    "octal_escapes",
 +    "ok_expect",
 +    "only_used_in_recursion",
 +    "op_ref",
 +    "option_as_ref_deref",
 +    "option_env_unwrap",
 +    "option_filter_map",
 +    "option_if_let_else",
 +    "option_map_or_none",
 +    "option_map_unit_fn",
 +    "option_option",
 +    "or_fun_call",
 +    "or_then_unwrap",
 +    "out_of_bounds_indexing",
 +    "overflow_check_conditional",
 +    "overly_complex_bool_expr",
 +    "panic",
 +    "panic_in_result_fn",
 +    "panicking_unwrap",
++    "partial_pub_fields",
 +    "partialeq_ne_impl",
 +    "partialeq_to_none",
 +    "path_buf_push_overwrite",
 +    "pattern_type_mismatch",
 +    "possible_missing_comma",
 +    "precedence",
 +    "print_in_format_impl",
 +    "print_literal",
 +    "print_stderr",
 +    "print_stdout",
 +    "print_with_newline",
 +    "println_empty_string",
 +    "ptr_arg",
 +    "ptr_as_ptr",
 +    "ptr_eq",
 +    "ptr_offset_with_cast",
 +    "pub_use",
 +    "question_mark",
 +    "range_minus_one",
 +    "range_plus_one",
 +    "range_zip_with_len",
 +    "rc_buffer",
 +    "rc_clone_in_vec_init",
 +    "rc_mutex",
 +    "read_zero_byte_vec",
 +    "recursive_format_impl",
 +    "redundant_allocation",
 +    "redundant_clone",
 +    "redundant_closure",
 +    "redundant_closure_call",
 +    "redundant_closure_for_method_calls",
 +    "redundant_else",
 +    "redundant_feature_names",
 +    "redundant_field_names",
 +    "redundant_pattern",
 +    "redundant_pattern_matching",
 +    "redundant_pub_crate",
 +    "redundant_slicing",
 +    "redundant_static_lifetimes",
 +    "ref_binding_to_reference",
 +    "ref_option_ref",
 +    "repeat_once",
 +    "rest_pat_in_fully_bound_structs",
 +    "result_large_err",
 +    "result_map_or_into_option",
 +    "result_map_unit_fn",
 +    "result_unit_err",
 +    "return_self_not_must_use",
 +    "reversed_empty_ranges",
 +    "same_functions_in_if_condition",
 +    "same_item_push",
 +    "same_name_method",
 +    "search_is_some",
 +    "self_assignment",
 +    "self_named_constructors",
 +    "self_named_module_files",
 +    "semicolon_if_nothing_returned",
 +    "separated_literal_suffix",
 +    "serde_api_misuse",
 +    "shadow_reuse",
 +    "shadow_same",
 +    "shadow_unrelated",
 +    "short_circuit_statement",
 +    "should_implement_trait",
 +    "significant_drop_in_scrutinee",
 +    "similar_names",
 +    "single_char_add_str",
 +    "single_char_lifetime_names",
 +    "single_char_pattern",
 +    "single_component_path_imports",
 +    "single_element_loop",
 +    "single_match",
 +    "single_match_else",
 +    "size_of_in_element_count",
 +    "skip_while_next",
 +    "slow_vector_initialization",
 +    "stable_sort_primitive",
 +    "std_instead_of_alloc",
 +    "std_instead_of_core",
 +    "str_to_string",
 +    "string_add",
 +    "string_add_assign",
 +    "string_extend_chars",
 +    "string_from_utf8_as_bytes",
 +    "string_lit_as_bytes",
 +    "string_slice",
 +    "string_to_string",
 +    "strlen_on_c_strings",
 +    "struct_excessive_bools",
 +    "suboptimal_flops",
 +    "suspicious_arithmetic_impl",
 +    "suspicious_assignment_formatting",
 +    "suspicious_else_formatting",
 +    "suspicious_map",
 +    "suspicious_op_assign_impl",
 +    "suspicious_operation_groupings",
 +    "suspicious_splitn",
 +    "suspicious_to_owned",
 +    "suspicious_unary_op_formatting",
 +    "swap_ptr_to_ref",
 +    "tabs_in_doc_comments",
 +    "temporary_assignment",
 +    "to_digit_is_some",
 +    "to_string_in_format_args",
 +    "todo",
 +    "too_many_arguments",
 +    "too_many_lines",
 +    "toplevel_ref_arg",
 +    "trailing_empty_array",
 +    "trait_duplication_in_bounds",
 +    "transmute_bytes_to_str",
 +    "transmute_float_to_int",
 +    "transmute_int_to_bool",
 +    "transmute_int_to_char",
 +    "transmute_int_to_float",
 +    "transmute_num_to_bytes",
 +    "transmute_ptr_to_ptr",
 +    "transmute_ptr_to_ref",
 +    "transmute_undefined_repr",
 +    "transmutes_expressible_as_ptr_casts",
 +    "transmuting_null",
 +    "trim_split_whitespace",
 +    "trivial_regex",
 +    "trivially_copy_pass_by_ref",
 +    "try_err",
 +    "type_complexity",
 +    "type_repetition_in_bounds",
 +    "undocumented_unsafe_blocks",
 +    "undropped_manually_drops",
 +    "unicode_not_nfc",
 +    "unimplemented",
 +    "uninit_assumed_init",
 +    "uninit_vec",
 +    "uninlined_format_args",
 +    "unit_arg",
 +    "unit_cmp",
 +    "unit_hash",
 +    "unit_return_expecting_ord",
 +    "unnecessary_cast",
 +    "unnecessary_filter_map",
 +    "unnecessary_find_map",
 +    "unnecessary_fold",
 +    "unnecessary_join",
 +    "unnecessary_lazy_evaluations",
 +    "unnecessary_mut_passed",
 +    "unnecessary_operation",
 +    "unnecessary_owned_empty_strings",
 +    "unnecessary_self_imports",
 +    "unnecessary_sort_by",
 +    "unnecessary_to_owned",
 +    "unnecessary_unwrap",
 +    "unnecessary_wraps",
 +    "unneeded_field_pattern",
 +    "unneeded_wildcard_pattern",
 +    "unnested_or_patterns",
 +    "unreachable",
 +    "unreadable_literal",
 +    "unsafe_derive_deserialize",
 +    "unsafe_removed_from_name",
 +    "unseparated_literal_suffix",
 +    "unsound_collection_transmute",
 +    "unused_async",
++    "unused_format_specs",
 +    "unused_io_amount",
 +    "unused_peekable",
 +    "unused_rounding",
 +    "unused_self",
 +    "unused_unit",
 +    "unusual_byte_groupings",
 +    "unwrap_in_result",
 +    "unwrap_or_else_default",
 +    "unwrap_used",
 +    "upper_case_acronyms",
 +    "use_debug",
 +    "use_self",
 +    "used_underscore_binding",
 +    "useless_asref",
 +    "useless_attribute",
 +    "useless_conversion",
 +    "useless_format",
 +    "useless_let_if_seq",
 +    "useless_transmute",
 +    "useless_vec",
 +    "vec_box",
 +    "vec_init_then_push",
 +    "vec_resize_to_zero",
 +    "verbose_bit_mask",
 +    "verbose_file_reads",
 +    "vtable_address_comparisons",
 +    "while_immutable_condition",
 +    "while_let_loop",
 +    "while_let_on_iterator",
 +    "wildcard_dependencies",
 +    "wildcard_enum_match_arm",
 +    "wildcard_imports",
 +    "wildcard_in_or_patterns",
 +    "write_literal",
 +    "write_with_newline",
 +    "writeln_empty_string",
 +    "wrong_self_convention",
 +    "wrong_transmute",
 +    "zero_divided_by_zero",
 +    "zero_prefixed_literal",
 +    "zero_ptr",
 +    "zero_sized_map_values",
 +    "zst_offset",
 +
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..228dde996bb2f41f5985bc3ebe984fcc25031af4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer
++
++### Why is this bad?
++Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior
++mutability is used, making it unlikely that having it as a mutable pointer is correct.
++
++### Example
++```
++let string = String::with_capacity(1);
++let ptr = string.as_ptr() as *mut u8;
++unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR
++```
++Use instead:
++```
++let mut string = String::with_capacity(1);
++let ptr = string.as_mut_ptr();
++unsafe { ptr.write(4) };
++```
index ffac894d0c50a46cef1cb788d5c8ceef945ab7c1,0000000000000000000000000000000000000000..1c670c7733377f04f3dcaefdd4158b7ff59332f2
mode 100644,000000..100644
--- /dev/null
@@@ -1,23 -1,0 +1,17 @@@
- ### Known problems
- The lint may miss some cases (e.g. Box::new(String::from(""))).
- On the other hand, it will trigger on cases where the `default`
- code comes from a macro that does something different based on
- e.g. target operating system.
 +### What it does
 +checks for `Box::new(T::default())`, which is better written as
 +`Box::<T>::default()`.
 +
 +### Why is this bad?
 +First, it's more complex, involving two calls instead of one.
 +Second, `Box::default()` can be faster
 +[in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box).
 +
 +### Example
 +```
 +let x: Box<String> = Box::new(Default::default());
 +```
 +Use instead:
 +```
 +let x: Box<String> = Box::default();
 +```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..122f5da0c921883aed797ca3880a4c71daf5f867
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for a known NaN float being cast to an integer
++
++### Why is this bad?
++NaNs are cast into zero, so one could simply use this and make the
++code more readable. The lint could also hint at a programmer error.
++
++### Example
++```
++let _: (0.0_f32 / 0.0) as u64;
++```
++Use instead:
++```
++let _: = 0_u64;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..19a4d9319d94b700d1e88671e4e8f9f6ae6c20ef
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for usages of `match` which could be implemented using `filter`
++
++### Why is this bad?
++Using the `filter` method is clearer and more concise.
++
++### Example
++```
++match Some(0) {
++    Some(x) => if x % 2 == 0 {
++                    Some(x)
++               } else {
++                    None
++                },
++    None => None,
++};
++```
++Use instead:
++```
++Some(0).filter(|&x| x % 2 == 0);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..788ad764f8c3960c3334b8107b66c07829d14c96
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++### What it does
++Checks if a provided method is used implicitly by a trait
++implementation. A usage example would be a wrapper where every method
++should perform some operation before delegating to the inner type's
++implemenation.
++
++This lint should typically be enabled on a specific trait `impl` item
++rather than globally.
++
++### Why is this bad?
++Indicates that a method is missing.
++
++### Example
++```
++trait Trait {
++    fn required();
++
++    fn provided() {}
++}
++
++#[warn(clippy::missing_trait_methods)]
++impl Trait for Type {
++    fn required() { /* ... */ }
++}
++```
++Use instead:
++```
++trait Trait {
++    fn required();
++
++    fn provided() {}
++}
++
++#[warn(clippy::missing_trait_methods)]
++impl Trait for Type {
++    fn required() { /* ... */ }
++
++    fn provided() { /* ... */ }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b529adf1547de3fb465c03a63cda7640301c36a8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++### What it does
++Checks whether partial fields of a struct are public.
++
++Either make all fields of a type public, or make none of them public
++
++### Why is this bad?
++Most types should either be:
++* Abstract data types: complex objects with opaque implementation which guard
++interior invariants and expose intentionally limited API to the outside world.
++* Data: relatively simple objects which group a bunch of related attributes together.
++
++### Example
++```
++pub struct Color {
++    pub r: u8,
++    pub g: u8,
++    b: u8,
++}
++```
++Use instead:
++```
++pub struct Color {
++    pub r: u8,
++    pub g: u8,
++    pub b: u8,
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..77be3a2fb170d1d81e7818e074b59e4dbcab936a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Detects [formatting parameters] that have no effect on the output of
++`format!()`, `println!()` or similar macros.
++
++### Why is this bad?
++Shorter format specifiers are easier to read, it may also indicate that
++an expected formatting operation such as adding padding isn't happening.
++
++### Example
++```
++println!("{:.}", 1.0);
++
++println!("not padded: {:5}", format_args!("..."));
++```
++Use instead:
++```
++println!("{}", 1.0);
++
++println!("not padded: {}", format_args!("..."));
++// OR
++println!("padded: {:5}", format!("..."));
++```
++
++[formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters
index fa769222d1af379ed5b737ebd25dbdd37cb87267,0000000000000000000000000000000000000000..c10ee969c014620b996dafa32d94367d10db825a
mode 100644,000000..100644
--- /dev/null
@@@ -1,511 -1,0 +1,511 @@@
-                 let cargo_content = fs::read(&cargo_toml_path)?;
 +#![feature(test)] // compiletest_rs requires this attribute
 +#![feature(once_cell)]
 +#![feature(is_sorted)]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use compiletest_rs as compiletest;
 +use compiletest_rs::common::Mode as TestMode;
 +
 +use std::collections::HashMap;
 +use std::env::{self, remove_var, set_var, var_os};
 +use std::ffi::{OsStr, OsString};
 +use std::fs;
 +use std::io;
 +use std::path::{Path, PathBuf};
 +use std::sync::LazyLock;
 +use test_utils::IS_RUSTC_TEST_SUITE;
 +
 +mod test_utils;
 +
 +// whether to run internal tests or not
 +const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal");
 +
 +/// All crates used in UI tests are listed here
 +static TEST_DEPENDENCIES: &[&str] = &[
 +    "clippy_lints",
 +    "clippy_utils",
 +    "derive_new",
 +    "futures",
 +    "if_chain",
 +    "itertools",
 +    "quote",
 +    "regex",
 +    "serde",
 +    "serde_derive",
 +    "syn",
 +    "tokio",
 +    "parking_lot",
 +    "rustc_semver",
 +];
 +
 +// Test dependencies may need an `extern crate` here to ensure that they show up
 +// in the depinfo file (otherwise cargo thinks they are unused)
 +#[allow(unused_extern_crates)]
 +extern crate clippy_lints;
 +#[allow(unused_extern_crates)]
 +extern crate clippy_utils;
 +#[allow(unused_extern_crates)]
 +extern crate derive_new;
 +#[allow(unused_extern_crates)]
 +extern crate futures;
 +#[allow(unused_extern_crates)]
 +extern crate if_chain;
 +#[allow(unused_extern_crates)]
 +extern crate itertools;
 +#[allow(unused_extern_crates)]
 +extern crate parking_lot;
 +#[allow(unused_extern_crates)]
 +extern crate quote;
 +#[allow(unused_extern_crates)]
 +extern crate rustc_semver;
 +#[allow(unused_extern_crates)]
 +extern crate syn;
 +#[allow(unused_extern_crates)]
 +extern crate tokio;
 +
 +/// Produces a string with an `--extern` flag for all UI test crate
 +/// dependencies.
 +///
 +/// The dependency files are located by parsing the depinfo file for this test
 +/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test
 +/// dependencies must be added to Cargo.toml at the project root. Test
 +/// dependencies that are not *directly* used by this test module require an
 +/// `extern crate` declaration.
 +static EXTERN_FLAGS: LazyLock<String> = LazyLock::new(|| {
 +    let current_exe_depinfo = {
 +        let mut path = env::current_exe().unwrap();
 +        path.set_extension("d");
 +        fs::read_to_string(path).unwrap()
 +    };
 +    let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len());
 +    for line in current_exe_depinfo.lines() {
 +        // each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:`
 +        let parse_name_path = || {
 +            if line.starts_with(char::is_whitespace) {
 +                return None;
 +            }
 +            let path_str = line.strip_suffix(':')?;
 +            let path = Path::new(path_str);
 +            if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") {
 +                return None;
 +            }
 +            let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?;
 +            // the "lib" prefix is not present for dll files
 +            let name = name.strip_prefix("lib").unwrap_or(name);
 +            Some((name, path_str))
 +        };
 +        if let Some((name, path)) = parse_name_path() {
 +            if TEST_DEPENDENCIES.contains(&name) {
 +                // A dependency may be listed twice if it is available in sysroot,
 +                // and the sysroot dependencies are listed first. As of the writing,
 +                // this only seems to apply to if_chain.
 +                crates.insert(name, path);
 +            }
 +        }
 +    }
 +    let not_found: Vec<&str> = TEST_DEPENDENCIES
 +        .iter()
 +        .copied()
 +        .filter(|n| !crates.contains_key(n))
 +        .collect();
 +    assert!(
 +        not_found.is_empty(),
 +        "dependencies not found in depinfo: {not_found:?}\n\
 +        help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\
 +        help: Try adding to dev-dependencies in Cargo.toml\n\
 +        help: Be sure to also add `extern crate ...;` to tests/compile-test.rs",
 +    );
 +    crates
 +        .into_iter()
 +        .map(|(name, path)| format!(" --extern {name}={path}"))
 +        .collect()
 +});
 +
 +fn base_config(test_dir: &str) -> compiletest::Config {
 +    let mut config = compiletest::Config {
 +        edition: Some("2021".into()),
 +        mode: TestMode::Ui,
 +        ..Default::default()
 +    };
 +
 +    if let Ok(filters) = env::var("TESTNAME") {
 +        config.filters = filters.split(',').map(ToString::to_string).collect();
 +    }
 +
 +    if let Some(path) = option_env!("RUSTC_LIB_PATH") {
 +        let path = PathBuf::from(path);
 +        config.run_lib_path = path.clone();
 +        config.compile_lib_path = path;
 +    }
 +    let current_exe_path = env::current_exe().unwrap();
 +    let deps_path = current_exe_path.parent().unwrap();
 +    let profile_path = deps_path.parent().unwrap();
 +
 +    // Using `-L dependency={}` enforces that external dependencies are added with `--extern`.
 +    // This is valuable because a) it allows us to monitor what external dependencies are used
 +    // and b) it ensures that conflicting rlibs are resolved properly.
 +    let host_libs = option_env!("HOST_LIBS")
 +        .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display()))
 +        .unwrap_or_default();
 +    config.target_rustcflags = Some(format!(
 +        "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{host_libs}{}",
 +        deps_path.display(),
 +        &*EXTERN_FLAGS,
 +    ));
 +
 +    config.src_base = Path::new("tests").join(test_dir);
 +    config.build_base = profile_path.join("test").join(test_dir);
 +    config.rustc_path = profile_path.join(if cfg!(windows) {
 +        "clippy-driver.exe"
 +    } else {
 +        "clippy-driver"
 +    });
 +    config
 +}
 +
 +fn run_ui() {
 +    let mut config = base_config("ui");
 +    config.rustfix_coverage = true;
 +    // use tests/clippy.toml
 +    let _g = VarGuard::set("CARGO_MANIFEST_DIR", fs::canonicalize("tests").unwrap());
 +    let _threads = VarGuard::set(
 +        "RUST_TEST_THREADS",
 +        // if RUST_TEST_THREADS is set, adhere to it, otherwise override it
 +        env::var("RUST_TEST_THREADS").unwrap_or_else(|_| {
 +            std::thread::available_parallelism()
 +                .map_or(1, std::num::NonZeroUsize::get)
 +                .to_string()
 +        }),
 +    );
 +    compiletest::run_tests(&config);
 +    check_rustfix_coverage();
 +}
 +
 +fn run_internal_tests() {
 +    // only run internal tests with the internal-tests feature
 +    if !RUN_INTERNAL_TESTS {
 +        return;
 +    }
 +    let config = base_config("ui-internal");
 +    compiletest::run_tests(&config);
 +}
 +
 +fn run_ui_toml() {
 +    fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
 +        let mut result = true;
 +        let opts = compiletest::test_opts(config);
 +        for dir in fs::read_dir(&config.src_base)? {
 +            let dir = dir?;
 +            if !dir.file_type()?.is_dir() {
 +                continue;
 +            }
 +            let dir_path = dir.path();
 +            let _g = VarGuard::set("CARGO_MANIFEST_DIR", &dir_path);
 +            for file in fs::read_dir(&dir_path)? {
 +                let file = file?;
 +                let file_path = file.path();
 +                if file.file_type()?.is_dir() {
 +                    continue;
 +                }
 +                if file_path.extension() != Some(OsStr::new("rs")) {
 +                    continue;
 +                }
 +                let paths = compiletest::common::TestPaths {
 +                    file: file_path,
 +                    base: config.src_base.clone(),
 +                    relative_dir: dir_path.file_name().unwrap().into(),
 +                };
 +                let test_name = compiletest::make_test_name(config, &paths);
 +                let index = tests
 +                    .iter()
 +                    .position(|test| test.desc.name == test_name)
 +                    .expect("The test should be in there");
 +                result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
 +            }
 +        }
 +        Ok(result)
 +    }
 +
 +    let mut config = base_config("ui-toml");
 +    config.src_base = config.src_base.canonicalize().unwrap();
 +
 +    let tests = compiletest::make_tests(&config);
 +
 +    let res = run_tests(&config, tests);
 +    match res {
 +        Ok(true) => {},
 +        Ok(false) => panic!("Some tests failed"),
 +        Err(e) => {
 +            panic!("I/O failure during tests: {e:?}");
 +        },
 +    }
 +}
 +
 +fn run_ui_cargo() {
 +    fn run_tests(
 +        config: &compiletest::Config,
 +        filters: &[String],
 +        mut tests: Vec<tester::TestDescAndFn>,
 +    ) -> Result<bool, io::Error> {
 +        let mut result = true;
 +        let opts = compiletest::test_opts(config);
 +
 +        for dir in fs::read_dir(&config.src_base)? {
 +            let dir = dir?;
 +            if !dir.file_type()?.is_dir() {
 +                continue;
 +            }
 +
 +            // Use the filter if provided
 +            let dir_path = dir.path();
 +            for filter in filters {
 +                if !dir_path.ends_with(filter) {
 +                    continue;
 +                }
 +            }
 +
 +            for case in fs::read_dir(&dir_path)? {
 +                let case = case?;
 +                if !case.file_type()?.is_dir() {
 +                    continue;
 +                }
 +
 +                let src_path = case.path().join("src");
 +
 +                // When switching between branches, if the previous branch had a test
 +                // that the current branch does not have, the directory is not removed
 +                // because an ignored Cargo.lock file exists.
 +                if !src_path.exists() {
 +                    continue;
 +                }
 +
 +                env::set_current_dir(&src_path)?;
 +
 +                let cargo_toml_path = case.path().join("Cargo.toml");
++                let cargo_content = fs::read(cargo_toml_path)?;
 +                let cargo_parsed: toml::Value = toml::from_str(
 +                    std::str::from_utf8(&cargo_content).expect("`Cargo.toml` is not a valid utf-8 file!"),
 +                )
 +                .expect("Can't parse `Cargo.toml`");
 +
 +                let _g = VarGuard::set("CARGO_MANIFEST_DIR", case.path());
 +                let _h = VarGuard::set(
 +                    "CARGO_PKG_RUST_VERSION",
 +                    cargo_parsed
 +                        .get("package")
 +                        .and_then(|p| p.get("rust-version"))
 +                        .and_then(toml::Value::as_str)
 +                        .unwrap_or(""),
 +                );
 +
 +                for file in fs::read_dir(&src_path)? {
 +                    let file = file?;
 +                    if file.file_type()?.is_dir() {
 +                        continue;
 +                    }
 +
 +                    // Search for the main file to avoid running a test for each file in the project
 +                    let file_path = file.path();
 +                    match file_path.file_name().and_then(OsStr::to_str) {
 +                        Some("main.rs") => {},
 +                        _ => continue,
 +                    }
 +                    let _g = VarGuard::set("CLIPPY_CONF_DIR", case.path());
 +                    let paths = compiletest::common::TestPaths {
 +                        file: file_path,
 +                        base: config.src_base.clone(),
 +                        relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
 +                    };
 +                    let test_name = compiletest::make_test_name(config, &paths);
 +                    let index = tests
 +                        .iter()
 +                        .position(|test| test.desc.name == test_name)
 +                        .expect("The test should be in there");
 +                    result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
 +                }
 +            }
 +        }
 +        Ok(result)
 +    }
 +
 +    if IS_RUSTC_TEST_SUITE {
 +        return;
 +    }
 +
 +    let mut config = base_config("ui-cargo");
 +    config.src_base = config.src_base.canonicalize().unwrap();
 +
 +    let tests = compiletest::make_tests(&config);
 +
 +    let current_dir = env::current_dir().unwrap();
 +    let res = run_tests(&config, &config.filters, tests);
 +    env::set_current_dir(current_dir).unwrap();
 +
 +    match res {
 +        Ok(true) => {},
 +        Ok(false) => panic!("Some tests failed"),
 +        Err(e) => {
 +            panic!("I/O failure during tests: {e:?}");
 +        },
 +    }
 +}
 +
 +#[test]
 +fn compile_test() {
 +    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
 +    run_ui();
 +    run_ui_toml();
 +    run_ui_cargo();
 +    run_internal_tests();
 +}
 +
 +const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[
 +    "assign_ops2.rs",
 +    "borrow_deref_ref_unfixable.rs",
 +    "cast_size_32bit.rs",
 +    "char_lit_as_u8.rs",
 +    "cmp_owned/without_suggestion.rs",
 +    "dbg_macro.rs",
 +    "deref_addrof_double_trigger.rs",
 +    "doc/unbalanced_ticks.rs",
 +    "eprint_with_newline.rs",
 +    "explicit_counter_loop.rs",
 +    "iter_skip_next_unfixable.rs",
 +    "let_and_return.rs",
 +    "literals.rs",
 +    "map_flatten.rs",
 +    "map_unwrap_or.rs",
 +    "match_bool.rs",
 +    "mem_replace_macro.rs",
 +    "needless_arbitrary_self_type_unfixable.rs",
 +    "needless_borrow_pat.rs",
 +    "needless_for_each_unfixable.rs",
 +    "nonminimal_bool.rs",
 +    "print_literal.rs",
 +    "print_with_newline.rs",
 +    "redundant_static_lifetimes_multiple.rs",
 +    "ref_binding_to_reference.rs",
 +    "repl_uninit.rs",
 +    "result_map_unit_fn_unfixable.rs",
 +    "search_is_some.rs",
 +    "single_component_path_imports_nested_first.rs",
 +    "string_add.rs",
 +    "suspicious_to_owned.rs",
 +    "toplevel_ref_arg_non_rustfix.rs",
 +    "unit_arg.rs",
 +    "unnecessary_clone.rs",
 +    "unnecessary_lazy_eval_unfixable.rs",
 +    "write_literal.rs",
 +    "write_literal_2.rs",
 +    "write_with_newline.rs",
 +];
 +
 +fn check_rustfix_coverage() {
 +    let missing_coverage_path = Path::new("debug/test/ui/rustfix_missing_coverage.txt");
 +    let missing_coverage_path = if let Ok(target_dir) = std::env::var("CARGO_TARGET_DIR") {
 +        PathBuf::from(target_dir).join(missing_coverage_path)
 +    } else {
 +        missing_coverage_path.to_path_buf()
 +    };
 +
 +    if let Ok(missing_coverage_contents) = std::fs::read_to_string(missing_coverage_path) {
 +        assert!(RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS.iter().is_sorted_by_key(Path::new));
 +
 +        for rs_file in missing_coverage_contents.lines() {
 +            let rs_path = Path::new(rs_file);
 +            if rs_path.starts_with("tests/ui/crashes") {
 +                continue;
 +            }
 +            assert!(rs_path.starts_with("tests/ui/"), "{rs_file:?}");
 +            let filename = rs_path.strip_prefix("tests/ui/").unwrap();
 +            assert!(
 +                RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS
 +                    .binary_search_by_key(&filename, Path::new)
 +                    .is_ok(),
 +                "`{rs_file}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \
 +                Please either add `// run-rustfix` at the top of the file or add the file to \
 +                `RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS` in `tests/compile-test.rs`.",
 +            );
 +        }
 +    }
 +}
 +
 +#[test]
 +fn rustfix_coverage_known_exceptions_accuracy() {
 +    for filename in RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS {
 +        let rs_path = Path::new("tests/ui").join(filename);
 +        assert!(
 +            rs_path.exists(),
 +            "`{}` does not exist",
 +            rs_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display()
 +        );
 +        let fixed_path = rs_path.with_extension("fixed");
 +        assert!(
 +            !fixed_path.exists(),
 +            "`{}` exists",
 +            fixed_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display()
 +        );
 +    }
 +}
 +
 +#[test]
 +fn ui_cargo_toml_metadata() {
 +    let ui_cargo_path = Path::new("tests/ui-cargo");
 +    let cargo_common_metadata_path = ui_cargo_path.join("cargo_common_metadata");
 +    let publish_exceptions =
 +        ["fail_publish", "fail_publish_true", "pass_publish_empty"].map(|path| cargo_common_metadata_path.join(path));
 +
 +    for entry in walkdir::WalkDir::new(ui_cargo_path) {
 +        let entry = entry.unwrap();
 +        let path = entry.path();
 +        if path.file_name() != Some(OsStr::new("Cargo.toml")) {
 +            continue;
 +        }
 +
 +        let toml = fs::read_to_string(path).unwrap().parse::<toml::Value>().unwrap();
 +
 +        let package = toml.as_table().unwrap().get("package").unwrap().as_table().unwrap();
 +
 +        let name = package.get("name").unwrap().as_str().unwrap().replace('-', "_");
 +        assert!(
 +            path.parent()
 +                .unwrap()
 +                .components()
 +                .map(|component| component.as_os_str().to_string_lossy().replace('-', "_"))
 +                .any(|s| *s == name)
 +                || path.starts_with(&cargo_common_metadata_path),
 +            "{path:?} has incorrect package name"
 +        );
 +
 +        let publish = package.get("publish").and_then(toml::Value::as_bool).unwrap_or(true);
 +        assert!(
 +            !publish || publish_exceptions.contains(&path.parent().unwrap().to_path_buf()),
 +            "{path:?} lacks `publish = false`"
 +        );
 +    }
 +}
 +
 +/// Restores an env var on drop
 +#[must_use]
 +struct VarGuard {
 +    key: &'static str,
 +    value: Option<OsString>,
 +}
 +
 +impl VarGuard {
 +    fn set(key: &'static str, val: impl AsRef<OsStr>) -> Self {
 +        let value = var_os(key);
 +        set_var(key, val);
 +        Self { key, value }
 +    }
 +}
 +
 +impl Drop for VarGuard {
 +    fn drop(&mut self) {
 +        match self.value.as_deref() {
 +            None => remove_var(self.key),
 +            Some(value) => set_var(self.key, value),
 +        }
 +    }
 +}
index 961525bbd9101dfbc6759f69ba3f05d7aa94e170,0000000000000000000000000000000000000000..6d0022f7a5ccfe56f4c5990a4c0f5258fbd95bf2
mode 100644,000000..100644
--- /dev/null
@@@ -1,104 -1,0 +1,111 @@@
-     for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] {
 +//! 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",
++        "lintcheck",
++        "rustc_tools_util",
++    ] {
 +        run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]);
 +    }
 +}
 +
 +#[test]
 +#[ignore]
 +#[cfg(feature = "internal")]
 +fn run_metadata_collection_lint() {
 +    use std::fs::File;
 +    use std::time::SystemTime;
 +
 +    // Setup for validation
 +    let metadata_output_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("util/gh-pages/lints.json");
 +    let start_time = SystemTime::now();
 +
 +    // Run collection as is
 +    std::env::set_var("ENABLE_METADATA_COLLECTION", "1");
 +    run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]);
 +
 +    // Check if cargo caching got in the way
 +    if let Ok(file) = File::open(metadata_output_path) {
 +        if let Ok(metadata) = file.metadata() {
 +            if let Ok(last_modification) = metadata.modified() {
 +                if last_modification > start_time {
 +                    // The output file has been modified. Most likely by a hungry
 +                    // metadata collection monster. So We'll return.
 +                    return;
 +                }
 +            }
 +        }
 +    }
 +
 +    // Force cargo to invalidate the caches
 +    filetime::set_file_mtime(
 +        PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("clippy_lints/src/lib.rs"),
 +        filetime::FileTime::now(),
 +    )
 +    .unwrap();
 +
 +    // Running the collection again
 +    run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]);
 +}
 +
 +fn run_clippy_for_package(project: &str, args: &[&str]) {
 +    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
 +
 +    let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH);
 +
 +    command
 +        .current_dir(root_dir.join(project))
 +        .env("CARGO_INCREMENTAL", "0")
 +        .arg("clippy")
 +        .arg("--all-targets")
 +        .arg("--all-features");
 +
 +    if let Ok(dogfood_args) = std::env::var("__CLIPPY_DOGFOOD_ARGS") {
 +        for arg in dogfood_args.split_whitespace() {
 +            command.arg(arg);
 +        }
 +    }
 +
 +    command.arg("--").args(args);
 +    command.arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
 +
 +    if cfg!(feature = "internal") {
 +        // internal lints only exist if we build with the internal feature
 +        command.args(["-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 a1b8e2ee162cf67eb3722946e1884415b768149c,0000000000000000000000000000000000000000..07c5941013c1af5d6b89a661100c9b2a42e01801
mode 100644,000000..100644
--- /dev/null
@@@ -1,13 -1,0 +1,13 @@@
- thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints.rs
++thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints/produce_ice.rs:28:9
 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
 +
 +error: internal compiler error: unexpected panic
 +
 +note: the compiler unexpectedly panicked. this is a bug.
 +
 +note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new
 +
 +note: Clippy version: foo
 +
 +query stack during panic:
 +end of query stack
index b823ff7fe37f04d2e8763c6495ee4bbf029094c6,0000000000000000000000000000000000000000..9a9790a4bae51ec7c99447c4a4ddfa46f1080e77
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,27 @@@
- #![allow(clippy::missing_clippy_version_attribute)]
 +#![warn(clippy::internal)]
++#![allow(clippy::missing_clippy_version_attribute, clippy::unnecessary_def_path)]
 +
 +mod paths {
 +    // Good path
 +    pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
 +
 +    // Path to method on inherent impl of a primitive type
 +    pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
 +
 +    // Path to method on inherent impl
 +    pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
 +
 +    // Path with empty segment
 +    pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
 +
 +    // Path with bad crate
 +    pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
 +
 +    // Path with bad module
 +    pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
 +
 +    // Path to method on an enum inherent impl
 +    pub const OPTION_IS_SOME: [&str; 4] = ["core", "option", "Option", "is_some"];
 +}
 +
 +fn main() {}
index 4c050332f2cc938ce497ecb9c55b3738b0d186c1,0000000000000000000000000000000000000000..cbbb4652306415b7256435bcdc5f83256c47c6b4
mode 100644,000000..100644
--- /dev/null
@@@ -1,62 -1,0 +1,62 @@@
- #[allow(unused)]
 +// run-rustfix
 +// aux-build:paths.rs
 +#![deny(clippy::internal)]
 +#![feature(rustc_private)]
 +
 +extern crate clippy_utils;
 +extern crate paths;
 +extern crate rustc_hir;
 +extern crate rustc_lint;
 +extern crate rustc_middle;
 +extern crate rustc_span;
 +
 +#[allow(unused)]
 +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type};
 +#[allow(unused)]
 +use clippy_utils::{
 +    is_expr_path_def_path, is_path_diagnostic_item, is_res_diagnostic_ctor, is_res_lang_ctor, is_trait_method,
 +    match_def_path, match_trait_method, path_res,
 +};
 +
 +#[allow(unused)]
 +use rustc_hir::LangItem;
 +#[allow(unused)]
 +use rustc_span::sym;
 +
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::Expr;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::Ty;
 +
- #[allow(unused)]
++#[allow(unused, clippy::unnecessary_def_path)]
 +static OPTION: [&str; 3] = ["core", "option", "Option"];
-     #[allow(unused)]
++#[allow(unused, clippy::unnecessary_def_path)]
 +const RESULT: &[&str] = &["core", "result", "Result"];
 +
 +fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) {
 +    let _ = is_type_diagnostic_item(cx, ty, sym::Option);
 +    let _ = is_type_diagnostic_item(cx, ty, sym::Result);
 +    let _ = is_type_diagnostic_item(cx, ty, sym::Result);
 +
++    #[allow(unused, clippy::unnecessary_def_path)]
 +    let rc_path = &["alloc", "rc", "Rc"];
 +    let _ = is_type_diagnostic_item(cx, ty, sym::Rc);
 +
 +    let _ = is_type_diagnostic_item(cx, ty, sym::Option);
 +    let _ = is_type_diagnostic_item(cx, ty, sym::Result);
 +
 +    let _ = is_type_lang_item(cx, ty, LangItem::OwnedBox);
 +    let _ = is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit);
 +
 +    let _ = cx.tcx.lang_items().require(LangItem::OwnedBox).ok() == Some(did);
 +    let _ = cx.tcx.is_diagnostic_item(sym::Option, did);
 +    let _ = cx.tcx.lang_items().require(LangItem::OptionSome).ok() == Some(did);
 +
 +    let _ = is_trait_method(cx, expr, sym::AsRef);
 +
 +    let _ = is_path_diagnostic_item(cx, expr, sym::Option);
 +    let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().require(LangItem::IteratorNext).ok() == Some(id));
 +    let _ = is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome);
 +}
 +
 +fn main() {}
index 6506f1f164ac6b47d82f66485284af19042292b9,0000000000000000000000000000000000000000..f17fed6c6530410cc139dfe34c4dfa85d155917d
mode 100644,000000..100644
--- /dev/null
@@@ -1,62 -1,0 +1,62 @@@
- #[allow(unused)]
 +// run-rustfix
 +// aux-build:paths.rs
 +#![deny(clippy::internal)]
 +#![feature(rustc_private)]
 +
 +extern crate clippy_utils;
 +extern crate paths;
 +extern crate rustc_hir;
 +extern crate rustc_lint;
 +extern crate rustc_middle;
 +extern crate rustc_span;
 +
 +#[allow(unused)]
 +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type};
 +#[allow(unused)]
 +use clippy_utils::{
 +    is_expr_path_def_path, is_path_diagnostic_item, is_res_diagnostic_ctor, is_res_lang_ctor, is_trait_method,
 +    match_def_path, match_trait_method, path_res,
 +};
 +
 +#[allow(unused)]
 +use rustc_hir::LangItem;
 +#[allow(unused)]
 +use rustc_span::sym;
 +
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::Expr;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::Ty;
 +
- #[allow(unused)]
++#[allow(unused, clippy::unnecessary_def_path)]
 +static OPTION: [&str; 3] = ["core", "option", "Option"];
-     #[allow(unused)]
++#[allow(unused, clippy::unnecessary_def_path)]
 +const RESULT: &[&str] = &["core", "result", "Result"];
 +
 +fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) {
 +    let _ = match_type(cx, ty, &OPTION);
 +    let _ = match_type(cx, ty, RESULT);
 +    let _ = match_type(cx, ty, &["core", "result", "Result"]);
 +
++    #[allow(unused, clippy::unnecessary_def_path)]
 +    let rc_path = &["alloc", "rc", "Rc"];
 +    let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
 +
 +    let _ = match_type(cx, ty, &paths::OPTION);
 +    let _ = match_type(cx, ty, paths::RESULT);
 +
 +    let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]);
 +    let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]);
 +
 +    let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]);
 +    let _ = match_def_path(cx, did, &["core", "option", "Option"]);
 +    let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]);
 +
 +    let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]);
 +
 +    let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]);
 +    let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]);
 +    let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]);
 +}
 +
 +fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b5ff3a5420561a8d7f473e843d26d73e77c9ede4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++#![feature(rustc_private)]
++#![allow(unused)]
++#![warn(clippy::unnecessary_def_path)]
++
++extern crate rustc_hir;
++
++use rustc_hir::LangItem;
++
++fn main() {
++    const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"];
++    const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
++    const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
++
++    // Don't lint, not yet a diagnostic or language item
++    const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..af46d87bf676e42816316dcf94aef37abb063059
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++error: hardcoded path to a language item
++  --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40
++   |
++LL |     const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
++   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: convert all references to use `LangItem::DerefMut`
++   = note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
++
++error: hardcoded path to a diagnostic item
++  --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43
++   |
++LL |     const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
++   |                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: convert all references to use `sym::deref_method`
++
++error: hardcoded path to a diagnostic item
++  --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36
++   |
++LL |     const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"];
++   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: convert all references to use `sym::Deref`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0d1d9258433b215589e77f03437e3578f3631c2f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++#![allow(unused)]
++#![warn(clippy::as_ptr_cast_mut)]
++#![allow(clippy::wrong_self_convention)]
++
++struct MutPtrWrapper(Vec<u8>);
++impl MutPtrWrapper {
++    fn as_ptr(&mut self) -> *const u8 {
++        self.0.as_mut_ptr() as *const u8
++    }
++}
++
++struct Covariant<T>(*const T);
++impl<T> Covariant<T> {
++    fn as_ptr(self) -> *const T {
++        self.0
++    }
++}
++
++fn main() {
++    let mut string = String::new();
++    let _ = string.as_ptr() as *mut u8;
++    let _: *mut i8 = string.as_ptr() as *mut _;
++    let _ = string.as_ptr() as *const i8;
++    let _ = string.as_mut_ptr();
++    let _ = string.as_mut_ptr() as *mut u8;
++    let _ = string.as_mut_ptr() as *const u8;
++
++    let nn = std::ptr::NonNull::new(4 as *mut u8).unwrap();
++    let _ = nn.as_ptr() as *mut u8;
++
++    let mut wrap = MutPtrWrapper(Vec::new());
++    let _ = wrap.as_ptr() as *mut u8;
++
++    let mut local = 4;
++    let ref_with_write_perm = Covariant(std::ptr::addr_of_mut!(local) as *const _);
++    let _ = ref_with_write_perm.as_ptr() as *mut u8;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2189c3d2f8556d702b708d94f64957390d18369a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: casting the result of `as_ptr` to *mut u8
++  --> $DIR/as_ptr_cast_mut.rs:21:13
++   |
++LL |     let _ = string.as_ptr() as *mut u8;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `string.as_mut_ptr()`
++   |
++   = note: `-D clippy::as-ptr-cast-mut` implied by `-D warnings`
++
++error: casting the result of `as_ptr` to *mut i8
++  --> $DIR/as_ptr_cast_mut.rs:22:22
++   |
++LL |     let _: *mut i8 = string.as_ptr() as *mut _;
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `string.as_mut_ptr()`
++
++error: aborting due to 2 previous errors
++
index 597318a556b850fc5b356214849b6c23ca734b13,0000000000000000000000000000000000000000..27ad538f24d8ce4ca06a41032ed0e701e70f4eb1
mode 100644,000000..100644
--- /dev/null
@@@ -1,14 -1,0 +1,12 @@@
- if_chain! {
-     if let StmtKind::Local(local) = stmt.kind;
-     if let Some(init) = local.init;
-     if let ExprKind::Cast(expr, cast_ty) = init.kind;
-     if let TyKind::Path(ref qpath) = cast_ty.kind;
-     if match_qpath(qpath, &["char"]);
-     if let ExprKind::Lit(ref lit) = expr.kind;
-     if let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node;
-     if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind;
-     if name.as_str() == "x";
-     then {
-         // report your lint here
-     }
++if let StmtKind::Local(local) = stmt.kind
++    && let Some(init) = local.init
++    && let ExprKind::Cast(expr, cast_ty) = init.kind
++    && let TyKind::Path(ref qpath) = cast_ty.kind
++    && match_qpath(qpath, &["char"])
++    && let ExprKind::Lit(ref lit) = expr.kind
++    && let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node
++    && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind
++    && name.as_str() == "x"
++{
++    // report your lint here
 +}
index a529981e2e68372c0cd034cdbc187cc0c2da900a,0000000000000000000000000000000000000000..9de0550d81d0051654cab991cee3a6b240eaeb34
mode 100644,000000..100644
--- /dev/null
@@@ -1,64 -1,0 +1,58 @@@
- if_chain! {
-     if let ExprKind::Block(block, None) = expr.kind;
-     if block.stmts.len() == 3;
-     if let StmtKind::Local(local) = block.stmts[0].kind;
-     if let Some(init) = local.init;
-     if let ExprKind::Lit(ref lit) = init.kind;
-     if let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node;
-     if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind;
-     if name.as_str() == "x";
-     if let StmtKind::Local(local1) = block.stmts[1].kind;
-     if let Some(init1) = local1.init;
-     if let ExprKind::Lit(ref lit1) = init1.kind;
-     if let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node;
-     if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local1.pat.kind;
-     if name1.as_str() == "_t";
-     if let StmtKind::Semi(e) = block.stmts[2].kind;
-     if let ExprKind::Unary(UnOp::Neg, inner) = e.kind;
-     if let ExprKind::Path(ref qpath) = inner.kind;
-     if match_qpath(qpath, &["x"]);
-     if block.expr.is_none();
-     then {
-         // report your lint here
-     }
++if let ExprKind::Block(block, None) = expr.kind
++    && block.stmts.len() == 3
++    && let StmtKind::Local(local) = block.stmts[0].kind
++    && let Some(init) = local.init
++    && let ExprKind::Lit(ref lit) = init.kind
++    && let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node
++    && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind
++    && name.as_str() == "x"
++    && let StmtKind::Local(local1) = block.stmts[1].kind
++    && let Some(init1) = local1.init
++    && let ExprKind::Lit(ref lit1) = init1.kind
++    && let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node
++    && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local1.pat.kind
++    && name1.as_str() == "_t"
++    && let StmtKind::Semi(e) = block.stmts[2].kind
++    && let ExprKind::Unary(UnOp::Neg, inner) = e.kind
++    && let ExprKind::Path(ref qpath) = inner.kind
++    && match_qpath(qpath, &["x"])
++    && block.expr.is_none()
++{
++    // report your lint here
 +}
- if_chain! {
-     if let ExprKind::Block(block, None) = expr.kind;
-     if block.stmts.len() == 1;
-     if let StmtKind::Local(local) = block.stmts[0].kind;
-     if let Some(init) = local.init;
-     if let ExprKind::Call(func, args) = init.kind;
-     if let ExprKind::Path(ref qpath) = func.kind;
-     if match_qpath(qpath, &["String", "new"]);
-     if args.is_empty();
-     if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind;
-     if name.as_str() == "expr";
-     if let Some(trailing_expr) = block.expr;
-     if let ExprKind::Call(func1, args1) = trailing_expr.kind;
-     if let ExprKind::Path(ref qpath1) = func1.kind;
-     if match_qpath(qpath1, &["drop"]);
-     if args1.len() == 1;
-     if let ExprKind::Path(ref qpath2) = args1[0].kind;
-     if match_qpath(qpath2, &["expr"]);
-     then {
-         // report your lint here
-     }
++if let ExprKind::Block(block, None) = expr.kind
++    && block.stmts.len() == 1
++    && let StmtKind::Local(local) = block.stmts[0].kind
++    && let Some(init) = local.init
++    && let ExprKind::Call(func, args) = init.kind
++    && let ExprKind::Path(ref qpath) = func.kind
++    && match_qpath(qpath, &["String", "new"])
++    && args.is_empty()
++    && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind
++    && name.as_str() == "expr"
++    && let Some(trailing_expr) = block.expr
++    && let ExprKind::Call(func1, args1) = trailing_expr.kind
++    && let ExprKind::Path(ref qpath1) = func1.kind
++    && match_qpath(qpath1, &["drop"])
++    && args1.len() == 1
++    && let ExprKind::Path(ref qpath2) = args1[0].kind
++    && match_qpath(qpath2, &["expr"])
++{
++    // report your lint here
 +}
- if_chain! {
-     if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind;
-     if let FnRetTy::DefaultReturn(_) = fn_decl.output;
-     let expr1 = &cx.tcx.hir().body(body_id).value;
-     if let ExprKind::Call(func, args) = expr1.kind;
-     if let ExprKind::Path(ref qpath) = func.kind;
-     if matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _));
-     if args.len() == 1;
-     if let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind;
-     if let FnRetTy::DefaultReturn(_) = fn_decl1.output;
-     let expr2 = &cx.tcx.hir().body(body_id1).value;
-     if let ExprKind::Block(block, None) = expr2.kind;
-     if block.stmts.is_empty();
-     if block.expr.is_none();
-     then {
-         // report your lint here
-     }
++if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind
++    && let FnRetTy::DefaultReturn(_) = fn_decl.output
++    && expr1 = &cx.tcx.hir().body(body_id).value
++    && let ExprKind::Call(func, args) = expr1.kind
++    && let ExprKind::Path(ref qpath) = func.kind
++    && matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _))
++    && args.len() == 1
++    && let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind
++    && let FnRetTy::DefaultReturn(_) = fn_decl1.output
++    && expr2 = &cx.tcx.hir().body(body_id1).value
++    && let ExprKind::Block(block, None) = expr2.kind
++    && block.stmts.is_empty()
++    && block.expr.is_none()
++{
++    // report your lint here
 +}
index 266312d63e50d57815e9aab022a45e884ae327bd,0000000000000000000000000000000000000000..f040f6330a64d8687bb53fb592b7342387e6c857
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,14 @@@
- if_chain! {
-     if let StmtKind::Local(local) = stmt.kind;
-     if let Some(init) = local.init;
-     if let ExprKind::Call(func, args) = init.kind;
-     if let ExprKind::Path(ref qpath) = func.kind;
-     if match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]);
-     if args.len() == 2;
-     if let ExprKind::Lit(ref lit) = args[0].kind;
-     if let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node;
-     if let ExprKind::Lit(ref lit1) = args[1].kind;
-     if let LitKind::Int(4, LitIntType::Unsuffixed) = lit1.node;
-     if let PatKind::Wild = local.pat.kind;
-     then {
-         // report your lint here
-     }
++if let StmtKind::Local(local) = stmt.kind
++    && let Some(init) = local.init
++    && let ExprKind::Call(func, args) = init.kind
++    && let ExprKind::Path(ref qpath) = func.kind
++    && match_qpath(qpath, &["{{root}}", "std", "cmp", "min"])
++    && args.len() == 2
++    && let ExprKind::Lit(ref lit) = args[0].kind
++    && let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node
++    && let ExprKind::Lit(ref lit1) = args[1].kind
++    && let LitKind::Int(4, LitIntType::Unsuffixed) = lit1.node
++    && let PatKind::Wild = local.pat.kind
++{
++    // report your lint here
 +}
index 8d92849b3668f4ea410eadcf293e93bbb1db1632,0000000000000000000000000000000000000000..5d79618820d80595dd9db6a50bfebb3c42d7df90
mode 100644,000000..100644
--- /dev/null
@@@ -1,50 -1,0 +1,46 @@@
- if_chain! {
-     if let StmtKind::Local(local) = stmt.kind;
-     if let Some(init) = local.init;
-     if let ExprKind::If(cond, then, Some(else_expr)) = init.kind;
-     if let ExprKind::DropTemps(expr) = cond.kind;
-     if let ExprKind::Lit(ref lit) = expr.kind;
-     if let LitKind::Bool(true) = lit.node;
-     if let ExprKind::Block(block, None) = then.kind;
-     if block.stmts.len() == 1;
-     if let StmtKind::Semi(e) = block.stmts[0].kind;
-     if let ExprKind::Binary(op, left, right) = e.kind;
-     if BinOpKind::Eq == op.node;
-     if let ExprKind::Lit(ref lit1) = left.kind;
-     if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node;
-     if let ExprKind::Lit(ref lit2) = right.kind;
-     if let LitKind::Int(1, LitIntType::Unsuffixed) = lit2.node;
-     if block.expr.is_none();
-     if let ExprKind::Block(block1, None) = else_expr.kind;
-     if block1.stmts.len() == 1;
-     if let StmtKind::Semi(e1) = block1.stmts[0].kind;
-     if let ExprKind::Binary(op1, left1, right1) = e1.kind;
-     if BinOpKind::Eq == op1.node;
-     if let ExprKind::Lit(ref lit3) = left1.kind;
-     if let LitKind::Int(2, LitIntType::Unsuffixed) = lit3.node;
-     if let ExprKind::Lit(ref lit4) = right1.kind;
-     if let LitKind::Int(2, LitIntType::Unsuffixed) = lit4.node;
-     if block1.expr.is_none();
-     if let PatKind::Wild = local.pat.kind;
-     then {
-         // report your lint here
-     }
++if let StmtKind::Local(local) = stmt.kind
++    && let Some(init) = local.init
++    && let ExprKind::If(cond, then, Some(else_expr)) = init.kind
++    && let ExprKind::DropTemps(expr) = cond.kind
++    && let ExprKind::Lit(ref lit) = expr.kind
++    && let LitKind::Bool(true) = lit.node
++    && let ExprKind::Block(block, None) = then.kind
++    && block.stmts.len() == 1
++    && let StmtKind::Semi(e) = block.stmts[0].kind
++    && let ExprKind::Binary(op, left, right) = e.kind
++    && BinOpKind::Eq == op.node
++    && let ExprKind::Lit(ref lit1) = left.kind
++    && let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node
++    && let ExprKind::Lit(ref lit2) = right.kind
++    && let LitKind::Int(1, LitIntType::Unsuffixed) = lit2.node
++    && block.expr.is_none()
++    && let ExprKind::Block(block1, None) = else_expr.kind
++    && block1.stmts.len() == 1
++    && let StmtKind::Semi(e1) = block1.stmts[0].kind
++    && let ExprKind::Binary(op1, left1, right1) = e1.kind
++    && BinOpKind::Eq == op1.node
++    && let ExprKind::Lit(ref lit3) = left1.kind
++    && let LitKind::Int(2, LitIntType::Unsuffixed) = lit3.node
++    && let ExprKind::Lit(ref lit4) = right1.kind
++    && let LitKind::Int(2, LitIntType::Unsuffixed) = lit4.node
++    && block1.expr.is_none()
++    && let PatKind::Wild = local.pat.kind
++{
++    // report your lint here
 +}
- if_chain! {
-     if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind;
-     if let ExprKind::Let(let_expr) = cond.kind;
-     if let PatKind::Lit(lit_expr) = let_expr.pat.kind;
-     if let ExprKind::Lit(ref lit) = lit_expr.kind;
-     if let LitKind::Bool(true) = lit.node;
-     if let ExprKind::Path(ref qpath) = let_expr.init.kind;
-     if match_qpath(qpath, &["a"]);
-     if let ExprKind::Block(block, None) = then.kind;
-     if block.stmts.is_empty();
-     if block.expr.is_none();
-     if let ExprKind::Block(block1, None) = else_expr.kind;
-     if block1.stmts.is_empty();
-     if block1.expr.is_none();
-     then {
-         // report your lint here
-     }
++if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind
++    && let ExprKind::Let(let_expr) = cond.kind
++    && let PatKind::Lit(lit_expr) = let_expr.pat.kind
++    && let ExprKind::Lit(ref lit) = lit_expr.kind
++    && let LitKind::Bool(true) = lit.node
++    && let ExprKind::Path(ref qpath) = let_expr.init.kind
++    && match_qpath(qpath, &["a"])
++    && let ExprKind::Block(block, None) = then.kind
++    && block.stmts.is_empty()
++    && block.expr.is_none()
++    && let ExprKind::Block(block1, None) = else_expr.kind
++    && block1.stmts.is_empty()
++    && block1.expr.is_none()
++{
++    // report your lint here
 +}
index bce4bc702733f206e11fc99e4585316dea805594,0000000000000000000000000000000000000000..32a3127b85a3e3f50546165e796ef8864f0c160e
mode 100644,000000..100644
--- /dev/null
@@@ -1,14 -1,0 +1,12 @@@
- if_chain! {
-     if let StmtKind::Local(local) = stmt.kind;
-     if let Some(init) = local.init;
-     if let ExprKind::Call(func, args) = init.kind;
-     if let ExprKind::Path(ref qpath) = func.kind;
-     if match_qpath(qpath, &["std", "mem", "transmute"]);
-     if args.len() == 1;
-     if let ExprKind::Path(ref qpath1) = args[0].kind;
-     if match_qpath(qpath1, &["ZPTR"]);
-     if let PatKind::Wild = local.pat.kind;
-     then {
-         // report your lint here
-     }
++if let StmtKind::Local(local) = stmt.kind
++    && let Some(init) = local.init
++    && let ExprKind::Call(func, args) = init.kind
++    && let ExprKind::Path(ref qpath) = func.kind
++    && match_qpath(qpath, &["std", "mem", "transmute"])
++    && args.len() == 1
++    && let ExprKind::Path(ref qpath1) = args[0].kind
++    && match_qpath(qpath1, &["ZPTR"])
++    && let PatKind::Wild = local.pat.kind
++{
++    // report your lint here
 +}
index ceb53fcd496369c11e90ea971cc02091e8c33780,0000000000000000000000000000000000000000..94a6436ed5479a0fe90f3ec26147199f229c8db3
mode 100644,000000..100644
--- /dev/null
@@@ -1,113 -1,0 +1,101 @@@
- if_chain! {
-     if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr);
-     if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = pat.kind;
-     if name.as_str() == "y";
-     if let ExprKind::Struct(qpath, fields, None) = arg.kind;
-     if matches!(qpath, QPath::LangItem(LangItem::Range, _));
-     if fields.len() == 2;
-     if fields[0].ident.as_str() == "start";
-     if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
-     if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
-     if fields[1].ident.as_str() == "end";
-     if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
-     if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
-     if let ExprKind::Block(block, None) = body.kind;
-     if block.stmts.len() == 1;
-     if let StmtKind::Local(local) = block.stmts[0].kind;
-     if let Some(init) = local.init;
-     if let ExprKind::Path(ref qpath1) = init.kind;
-     if match_qpath(qpath1, &["y"]);
-     if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind;
-     if name1.as_str() == "z";
-     if block.expr.is_none();
-     then {
-         // report your lint here
-     }
++if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr)
++    && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = pat.kind
++    && name.as_str() == "y"
++    && let ExprKind::Struct(qpath, fields, None) = arg.kind
++    && matches!(qpath, QPath::LangItem(LangItem::Range, _))
++    && fields.len() == 2
++    && fields[0].ident.as_str() == "start"
++    && let ExprKind::Lit(ref lit) = fields[0].expr.kind
++    && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node
++    && fields[1].ident.as_str() == "end"
++    && let ExprKind::Lit(ref lit1) = fields[1].expr.kind
++    && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node
++    && let ExprKind::Block(block, None) = body.kind
++    && block.stmts.len() == 1
++    && let StmtKind::Local(local) = block.stmts[0].kind
++    && let Some(init) = local.init
++    && let ExprKind::Path(ref qpath1) = init.kind
++    && match_qpath(qpath1, &["y"])
++    && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind
++    && name1.as_str() == "z"
++    && block.expr.is_none()
++{
++    // report your lint here
 +}
- if_chain! {
-     if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr);
-     if let PatKind::Wild = pat.kind;
-     if let ExprKind::Struct(qpath, fields, None) = arg.kind;
-     if matches!(qpath, QPath::LangItem(LangItem::Range, _));
-     if fields.len() == 2;
-     if fields[0].ident.as_str() == "start";
-     if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
-     if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
-     if fields[1].ident.as_str() == "end";
-     if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
-     if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
-     if let ExprKind::Block(block, None) = body.kind;
-     if block.stmts.len() == 1;
-     if let StmtKind::Semi(e) = block.stmts[0].kind;
-     if let ExprKind::Break(destination, None) = e.kind;
-     if destination.label.is_none();
-     if block.expr.is_none();
-     then {
-         // report your lint here
-     }
++if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr)
++    && let PatKind::Wild = pat.kind
++    && let ExprKind::Struct(qpath, fields, None) = arg.kind
++    && matches!(qpath, QPath::LangItem(LangItem::Range, _))
++    && fields.len() == 2
++    && fields[0].ident.as_str() == "start"
++    && let ExprKind::Lit(ref lit) = fields[0].expr.kind
++    && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node
++    && fields[1].ident.as_str() == "end"
++    && let ExprKind::Lit(ref lit1) = fields[1].expr.kind
++    && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node
++    && let ExprKind::Block(block, None) = body.kind
++    && block.stmts.len() == 1
++    && let StmtKind::Semi(e) = block.stmts[0].kind
++    && let ExprKind::Break(destination, None) = e.kind
++    && destination.label.is_none()
++    && block.expr.is_none()
++{
++    // report your lint here
 +}
- if_chain! {
-     if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr);
-     if let PatKind::Wild = pat.kind;
-     if let ExprKind::Struct(qpath, fields, None) = arg.kind;
-     if matches!(qpath, QPath::LangItem(LangItem::Range, _));
-     if fields.len() == 2;
-     if fields[0].ident.as_str() == "start";
-     if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
-     if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
-     if fields[1].ident.as_str() == "end";
-     if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
-     if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
-     if let ExprKind::Block(block, None) = body.kind;
-     if block.stmts.len() == 1;
-     if let StmtKind::Semi(e) = block.stmts[0].kind;
-     if let ExprKind::Break(destination, None) = e.kind;
-     if let Some(label) = destination.label;
-     if label.ident.as_str() == "'label";
-     if block.expr.is_none();
-     then {
-         // report your lint here
-     }
++if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr)
++    && let PatKind::Wild = pat.kind
++    && let ExprKind::Struct(qpath, fields, None) = arg.kind
++    && matches!(qpath, QPath::LangItem(LangItem::Range, _))
++    && fields.len() == 2
++    && fields[0].ident.as_str() == "start"
++    && let ExprKind::Lit(ref lit) = fields[0].expr.kind
++    && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node
++    && fields[1].ident.as_str() == "end"
++    && let ExprKind::Lit(ref lit1) = fields[1].expr.kind
++    && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node
++    && let ExprKind::Block(block, None) = body.kind
++    && block.stmts.len() == 1
++    && let StmtKind::Semi(e) = block.stmts[0].kind
++    && let ExprKind::Break(destination, None) = e.kind
++    && let Some(label) = destination.label
++    && label.ident.as_str() == "'label"
++    && block.expr.is_none()
++{
++    // report your lint here
 +}
- if_chain! {
-     if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr);
-     if let ExprKind::Path(ref qpath) = condition.kind;
-     if match_qpath(qpath, &["a"]);
-     if let ExprKind::Block(block, None) = body.kind;
-     if block.stmts.len() == 1;
-     if let StmtKind::Semi(e) = block.stmts[0].kind;
-     if let ExprKind::Break(destination, None) = e.kind;
-     if destination.label.is_none();
-     if block.expr.is_none();
-     then {
-         // report your lint here
-     }
++if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr)
++    && let ExprKind::Path(ref qpath) = condition.kind
++    && match_qpath(qpath, &["a"])
++    && let ExprKind::Block(block, None) = body.kind
++    && block.stmts.len() == 1
++    && let StmtKind::Semi(e) = block.stmts[0].kind
++    && let ExprKind::Break(destination, None) = e.kind
++    && destination.label.is_none()
++    && block.expr.is_none()
++{
++    // report your lint here
 +}
- if_chain! {
-     if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr);
-     if let PatKind::Lit(lit_expr) = let_pat.kind;
-     if let ExprKind::Lit(ref lit) = lit_expr.kind;
-     if let LitKind::Bool(true) = lit.node;
-     if let ExprKind::Path(ref qpath) = let_expr.kind;
-     if match_qpath(qpath, &["a"]);
-     if let ExprKind::Block(block, None) = if_then.kind;
-     if block.stmts.len() == 1;
-     if let StmtKind::Semi(e) = block.stmts[0].kind;
-     if let ExprKind::Break(destination, None) = e.kind;
-     if destination.label.is_none();
-     if block.expr.is_none();
-     then {
-         // report your lint here
-     }
++if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr)
++    && let PatKind::Lit(lit_expr) = let_pat.kind
++    && let ExprKind::Lit(ref lit) = lit_expr.kind
++    && let LitKind::Bool(true) = lit.node
++    && let ExprKind::Path(ref qpath) = let_expr.kind
++    && match_qpath(qpath, &["a"])
++    && let ExprKind::Block(block, None) = if_then.kind
++    && block.stmts.len() == 1
++    && let StmtKind::Semi(e) = block.stmts[0].kind
++    && let ExprKind::Break(destination, None) = e.kind
++    && destination.label.is_none()
++    && block.expr.is_none()
++{
++    // report your lint here
 +}
- if_chain! {
-     if let ExprKind::Loop(body, None, LoopSource::Loop, _) = expr.kind;
-     if body.stmts.len() == 1;
-     if let StmtKind::Semi(e) = body.stmts[0].kind;
-     if let ExprKind::Break(destination, None) = e.kind;
-     if destination.label.is_none();
-     if body.expr.is_none();
-     then {
-         // report your lint here
-     }
++if let ExprKind::Loop(body, None, LoopSource::Loop, _) = expr.kind
++    && body.stmts.len() == 1
++    && let StmtKind::Semi(e) = body.stmts[0].kind
++    && let ExprKind::Break(destination, None) = e.kind
++    && destination.label.is_none()
++    && body.expr.is_none()
++{
++    // report your lint here
 +}
index 2cf69a035b4c7364ff6ee3991c78f50101f067bf,0000000000000000000000000000000000000000..88e2ca656a4f60a5ae8cd9f3392d5b5e6179bdfe
mode 100644,000000..100644
--- /dev/null
@@@ -1,38 -1,0 +1,36 @@@
- if_chain! {
-     if let StmtKind::Local(local) = stmt.kind;
-     if let Some(init) = local.init;
-     if let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind;
-     if let ExprKind::Lit(ref lit) = scrutinee.kind;
-     if let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node;
-     if arms.len() == 3;
-     if let PatKind::Lit(lit_expr) = arms[0].pat.kind;
-     if let ExprKind::Lit(ref lit1) = lit_expr.kind;
-     if let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node;
-     if arms[0].guard.is_none();
-     if let ExprKind::Lit(ref lit2) = arms[0].body.kind;
-     if let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node;
-     if let PatKind::Lit(lit_expr1) = arms[1].pat.kind;
-     if let ExprKind::Lit(ref lit3) = lit_expr1.kind;
-     if let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node;
-     if arms[1].guard.is_none();
-     if let ExprKind::Block(block, None) = arms[1].body.kind;
-     if block.stmts.len() == 1;
-     if let StmtKind::Local(local1) = block.stmts[0].kind;
-     if let Some(init1) = local1.init;
-     if let ExprKind::Lit(ref lit4) = init1.kind;
-     if let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node;
-     if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local1.pat.kind;
-     if name.as_str() == "x";
-     if let Some(trailing_expr) = block.expr;
-     if let ExprKind::Path(ref qpath) = trailing_expr.kind;
-     if match_qpath(qpath, &["x"]);
-     if let PatKind::Wild = arms[2].pat.kind;
-     if arms[2].guard.is_none();
-     if let ExprKind::Lit(ref lit5) = arms[2].body.kind;
-     if let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node;
-     if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind;
-     if name1.as_str() == "a";
-     then {
-         // report your lint here
-     }
++if let StmtKind::Local(local) = stmt.kind
++    && let Some(init) = local.init
++    && let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind
++    && let ExprKind::Lit(ref lit) = scrutinee.kind
++    && let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node
++    && arms.len() == 3
++    && let PatKind::Lit(lit_expr) = arms[0].pat.kind
++    && let ExprKind::Lit(ref lit1) = lit_expr.kind
++    && let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node
++    && arms[0].guard.is_none()
++    && let ExprKind::Lit(ref lit2) = arms[0].body.kind
++    && let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node
++    && let PatKind::Lit(lit_expr1) = arms[1].pat.kind
++    && let ExprKind::Lit(ref lit3) = lit_expr1.kind
++    && let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node
++    && arms[1].guard.is_none()
++    && let ExprKind::Block(block, None) = arms[1].body.kind
++    && block.stmts.len() == 1
++    && let StmtKind::Local(local1) = block.stmts[0].kind
++    && let Some(init1) = local1.init
++    && let ExprKind::Lit(ref lit4) = init1.kind
++    && let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node
++    && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local1.pat.kind
++    && name.as_str() == "x"
++    && let Some(trailing_expr) = block.expr
++    && let ExprKind::Path(ref qpath) = trailing_expr.kind
++    && match_qpath(qpath, &["x"])
++    && let PatKind::Wild = arms[2].pat.kind
++    && arms[2].guard.is_none()
++    && let ExprKind::Lit(ref lit5) = arms[2].body.kind
++    && let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node
++    && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind
++    && name1.as_str() == "a"
++{
++    // report your lint here
 +}
index 471bbce4f4185e2184ca058174d6cfe5480b8006,0000000000000000000000000000000000000000..c2a369610cc1b7cf0bf5635faed84c5999ebf3ed
mode 100644,000000..100644
--- /dev/null
@@@ -1,12 -1,0 +1,10 @@@
- if_chain! {
-     if let ExprKind::Repeat(value, length) = expr.kind;
-     if let ExprKind::Lit(ref lit) = value.kind;
-     if let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node;
-     if let ArrayLen::Body(anon_const) = length;
-     let expr1 = &cx.tcx.hir().body(anon_const.body).value;
-     if let ExprKind::Lit(ref lit1) = expr1.kind;
-     if let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node;
-     then {
-         // report your lint here
-     }
++if let ExprKind::Repeat(value, length) = expr.kind
++    && let ExprKind::Lit(ref lit) = value.kind
++    && let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node
++    && let ArrayLen::Body(anon_const) = length
++    && expr1 = &cx.tcx.hir().body(anon_const.body).value
++    && let ExprKind::Lit(ref lit1) = expr1.kind
++    && let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node
++{
++    // report your lint here
 +}
index b5bbc9e213c6e928330105b81cd6b0e7fb17fd45,0000000000000000000000000000000000000000..0b332d5e7d0e1e2c9b8b9cdb0fe41898dbe6b6d9
mode 100644,000000..100644
--- /dev/null
@@@ -1,64 -1,0 +1,56 @@@
- if_chain! {
-     if let ExprKind::Struct(qpath, fields, None) = expr.kind;
-     if match_qpath(qpath, &["Test"]);
-     if fields.len() == 1;
-     if fields[0].ident.as_str() == "field";
-     if let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind;
-     if let ExprKind::DropTemps(expr1) = cond.kind;
-     if let ExprKind::Lit(ref lit) = expr1.kind;
-     if let LitKind::Bool(true) = lit.node;
-     if let ExprKind::Block(block, None) = then.kind;
-     if block.stmts.is_empty();
-     if let Some(trailing_expr) = block.expr;
-     if let ExprKind::Lit(ref lit1) = trailing_expr.kind;
-     if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node;
-     if let ExprKind::Block(block1, None) = else_expr.kind;
-     if block1.stmts.is_empty();
-     if let Some(trailing_expr1) = block1.expr;
-     if let ExprKind::Lit(ref lit2) = trailing_expr1.kind;
-     if let LitKind::Int(0, LitIntType::Unsuffixed) = lit2.node;
-     then {
-         // report your lint here
-     }
++if let ExprKind::Struct(qpath, fields, None) = expr.kind
++    && match_qpath(qpath, &["Test"])
++    && fields.len() == 1
++    && fields[0].ident.as_str() == "field"
++    && let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind
++    && let ExprKind::DropTemps(expr1) = cond.kind
++    && let ExprKind::Lit(ref lit) = expr1.kind
++    && let LitKind::Bool(true) = lit.node
++    && let ExprKind::Block(block, None) = then.kind
++    && block.stmts.is_empty()
++    && let Some(trailing_expr) = block.expr
++    && let ExprKind::Lit(ref lit1) = trailing_expr.kind
++    && let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node
++    && let ExprKind::Block(block1, None) = else_expr.kind
++    && block1.stmts.is_empty()
++    && let Some(trailing_expr1) = block1.expr
++    && let ExprKind::Lit(ref lit2) = trailing_expr1.kind
++    && let LitKind::Int(0, LitIntType::Unsuffixed) = lit2.node
++{
++    // report your lint here
 +}
- if_chain! {
-     if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind;
-     if match_qpath(qpath, &["Test"]);
-     if fields.len() == 1;
-     if fields[0].ident.as_str() == "field";
-     if let PatKind::Lit(lit_expr) = fields[0].pat.kind;
-     if let ExprKind::Lit(ref lit) = lit_expr.kind;
-     if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node;
-     if arm.guard.is_none();
-     if let ExprKind::Block(block, None) = arm.body.kind;
-     if block.stmts.is_empty();
-     if block.expr.is_none();
-     then {
-         // report your lint here
-     }
++if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind
++    && match_qpath(qpath, &["Test"])
++    && fields.len() == 1
++    && fields[0].ident.as_str() == "field"
++    && let PatKind::Lit(lit_expr) = fields[0].pat.kind
++    && let ExprKind::Lit(ref lit) = lit_expr.kind
++    && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node
++    && arm.guard.is_none()
++    && let ExprKind::Block(block, None) = arm.body.kind
++    && block.stmts.is_empty()
++    && block.expr.is_none()
++{
++    // report your lint here
 +}
- if_chain! {
-     if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind;
-     if match_qpath(qpath, &["TestTuple"]);
-     if fields.len() == 1;
-     if let PatKind::Lit(lit_expr) = fields[0].kind;
-     if let ExprKind::Lit(ref lit) = lit_expr.kind;
-     if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node;
-     if arm.guard.is_none();
-     if let ExprKind::Block(block, None) = arm.body.kind;
-     if block.stmts.is_empty();
-     if block.expr.is_none();
-     then {
-         // report your lint here
-     }
++if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind
++    && match_qpath(qpath, &["TestTuple"])
++    && fields.len() == 1
++    && let PatKind::Lit(lit_expr) = fields[0].kind
++    && let ExprKind::Lit(ref lit) = lit_expr.kind
++    && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node
++    && arm.guard.is_none()
++    && let ExprKind::Block(block, None) = arm.body.kind
++    && block.stmts.is_empty()
++    && block.expr.is_none()
++{
++    // report your lint here
 +}
- if_chain! {
-     if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind;
-     if method_name.ident.as_str() == "test";
-     if let ExprKind::Path(ref qpath) = receiver.kind;
-     if match_qpath(qpath, &["test_method_call"]);
-     if args.is_empty();
-     then {
-         // report your lint here
-     }
++if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind
++    && method_name.ident.as_str() == "test"
++    && let ExprKind::Path(ref qpath) = receiver.kind
++    && match_qpath(qpath, &["test_method_call"])
++    && args.is_empty()
++{
++    // report your lint here
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..911fa856aa0a3f790591e4187581e90c740ab41a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,57 @@@
++// run-rustfix
++#![warn(clippy::box_default)]
++
++#[derive(Default)]
++struct ImplementsDefault;
++
++struct OwnDefault;
++
++impl OwnDefault {
++    fn default() -> Self {
++        Self
++    }
++}
++
++macro_rules! outer {
++    ($e: expr) => {
++        $e
++    };
++}
++
++fn main() {
++    let _string: Box<String> = Box::default();
++    let _byte = Box::<u8>::default();
++    let _vec = Box::<std::vec::Vec<u8>>::default();
++    let _impl = Box::<ImplementsDefault>::default();
++    let _impl2 = Box::<ImplementsDefault>::default();
++    let _impl3: Box<ImplementsDefault> = Box::default();
++    let _own = Box::new(OwnDefault::default()); // should not lint
++    let _in_macro = outer!(Box::<std::string::String>::default());
++    let _string_default = outer!(Box::<std::string::String>::default());
++    let _vec2: Box<Vec<ImplementsDefault>> = Box::default();
++    let _vec3: Box<Vec<bool>> = Box::default();
++    let _vec4: Box<_> = Box::<std::vec::Vec<bool>>::default();
++    let _more = ret_ty_fn();
++    call_ty_fn(Box::default());
++}
++
++fn ret_ty_fn() -> Box<bool> {
++    Box::<bool>::default()
++}
++
++#[allow(clippy::boxed_local)]
++fn call_ty_fn(_b: Box<u8>) {
++    issue_9621_dyn_trait();
++}
++
++use std::io::{Read, Result};
++
++impl Read for ImplementsDefault {
++    fn read(&mut self, _: &mut [u8]) -> Result<usize> {
++        Ok(0)
++    }
++}
++
++fn issue_9621_dyn_trait() {
++    let _: Box<dyn Read> = Box::<ImplementsDefault>::default();
++}
index dc522705bc6248b99ac1fa6644ea29db27691890,0000000000000000000000000000000000000000..20019c2ee5a07a550042311cdfc1bf5b0beffeed
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,57 @@@
-     // false negative: default is from different expansion
++// run-rustfix
 +#![warn(clippy::box_default)]
 +
 +#[derive(Default)]
 +struct ImplementsDefault;
 +
 +struct OwnDefault;
 +
 +impl OwnDefault {
 +    fn default() -> Self {
 +        Self
 +    }
 +}
 +
 +macro_rules! outer {
 +    ($e: expr) => {
 +        $e
 +    };
 +}
 +
 +fn main() {
 +    let _string: Box<String> = Box::new(Default::default());
 +    let _byte = Box::new(u8::default());
 +    let _vec = Box::new(Vec::<u8>::new());
 +    let _impl = Box::new(ImplementsDefault::default());
 +    let _impl2 = Box::new(<ImplementsDefault as Default>::default());
 +    let _impl3: Box<ImplementsDefault> = Box::new(Default::default());
 +    let _own = Box::new(OwnDefault::default()); // should not lint
 +    let _in_macro = outer!(Box::new(String::new()));
++    let _string_default = outer!(Box::new(String::from("")));
 +    let _vec2: Box<Vec<ImplementsDefault>> = Box::new(vec![]);
++    let _vec3: Box<Vec<bool>> = Box::new(Vec::from([]));
++    let _vec4: Box<_> = Box::new(Vec::from([false; 0]));
++    let _more = ret_ty_fn();
++    call_ty_fn(Box::new(u8::default()));
++}
++
++fn ret_ty_fn() -> Box<bool> {
++    Box::new(bool::default())
++}
++
++#[allow(clippy::boxed_local)]
++fn call_ty_fn(_b: Box<u8>) {
++    issue_9621_dyn_trait();
++}
++
++use std::io::{Read, Result};
++
++impl Read for ImplementsDefault {
++    fn read(&mut self, _: &mut [u8]) -> Result<usize> {
++        Ok(0)
++    }
++}
++
++fn issue_9621_dyn_trait() {
++    let _: Box<dyn Read> = Box::new(ImplementsDefault::default());
 +}
index b2030e95acb107526899a444dd20525fbb5f4899,0000000000000000000000000000000000000000..5ea410331afb309757dee4fa1d6ee579ce87904c
mode 100644,000000..100644
--- /dev/null
@@@ -1,59 -1,0 +1,88 @@@
-   --> $DIR/box_default.rs:21:32
 +error: `Box::new(_)` of default value
-    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++  --> $DIR/box_default.rs:22:32
 +   |
 +LL |     let _string: Box<String> = Box::new(Default::default());
-    = help: use `Box::default()` instead
++   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()`
 +   |
-   --> $DIR/box_default.rs:22:17
 +   = note: `-D clippy::box-default` implied by `-D warnings`
 +
 +error: `Box::new(_)` of default value
-    |                 ^^^^^^^^^^^^^^^^^^^^^^^
-    |
-    = help: use `Box::default()` instead
++  --> $DIR/box_default.rs:23:17
 +   |
 +LL |     let _byte = Box::new(u8::default());
-   --> $DIR/box_default.rs:23:16
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<u8>::default()`
 +
 +error: `Box::new(_)` of default value
-    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^
-    |
-    = help: use `Box::default()` instead
++  --> $DIR/box_default.rs:24:16
 +   |
 +LL |     let _vec = Box::new(Vec::<u8>::new());
-   --> $DIR/box_default.rs:24:17
++   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<std::vec::Vec<u8>>::default()`
 +
 +error: `Box::new(_)` of default value
-    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-    |
-    = help: use `Box::default()` instead
++  --> $DIR/box_default.rs:25:17
 +   |
 +LL |     let _impl = Box::new(ImplementsDefault::default());
-   --> $DIR/box_default.rs:25:18
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<ImplementsDefault>::default()`
 +
 +error: `Box::new(_)` of default value
-    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-    |
-    = help: use `Box::default()` instead
++  --> $DIR/box_default.rs:26:18
 +   |
 +LL |     let _impl2 = Box::new(<ImplementsDefault as Default>::default());
-   --> $DIR/box_default.rs:26:42
++   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<ImplementsDefault>::default()`
 +
 +error: `Box::new(_)` of default value
-    |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-    |
-    = help: use `Box::default()` instead
++  --> $DIR/box_default.rs:27:42
 +   |
 +LL |     let _impl3: Box<ImplementsDefault> = Box::new(Default::default());
-   --> $DIR/box_default.rs:28:28
++   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()`
 +
 +error: `Box::new(_)` of default value
-    |                            ^^^^^^^^^^^^^^^^^^^^^^^
++  --> $DIR/box_default.rs:29:28
 +   |
 +LL |     let _in_macro = outer!(Box::new(String::new()));
-    = help: use `Box::default()` instead
++   |                            ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<std::string::String>::default()`
++
++error: `Box::new(_)` of default value
++  --> $DIR/box_default.rs:30:34
++   |
++LL |     let _string_default = outer!(Box::new(String::from("")));
++   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<std::string::String>::default()`
++
++error: `Box::new(_)` of default value
++  --> $DIR/box_default.rs:31:46
++   |
++LL |     let _vec2: Box<Vec<ImplementsDefault>> = Box::new(vec![]);
++   |                                              ^^^^^^^^^^^^^^^^ help: try: `Box::default()`
++
++error: `Box::new(_)` of default value
++  --> $DIR/box_default.rs:32:33
++   |
++LL |     let _vec3: Box<Vec<bool>> = Box::new(Vec::from([]));
++   |                                 ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()`
++
++error: `Box::new(_)` of default value
++  --> $DIR/box_default.rs:33:25
++   |
++LL |     let _vec4: Box<_> = Box::new(Vec::from([false; 0]));
++   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<std::vec::Vec<bool>>::default()`
++
++error: `Box::new(_)` of default value
++  --> $DIR/box_default.rs:35:16
++   |
++LL |     call_ty_fn(Box::new(u8::default()));
++   |                ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()`
++
++error: `Box::new(_)` of default value
++  --> $DIR/box_default.rs:39:5
++   |
++LL |     Box::new(bool::default())
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<bool>::default()`
++
++error: `Box::new(_)` of default value
++  --> $DIR/box_default.rs:56:28
 +   |
- error: aborting due to 7 previous errors
++LL |     let _: Box<dyn Read> = Box::new(ImplementsDefault::default());
++   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<ImplementsDefault>::default()`
 +
++error: aborting due to 14 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4326abc9a5410526b0a272b1ad43fcc8e4582462
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++#![feature(lang_items, start, libc)]
++#![warn(clippy::box_default)]
++#![no_std]
++
++pub struct NotBox<T> {
++    _value: T,
++}
++
++impl<T> NotBox<T> {
++    pub fn new(value: T) -> Self {
++        Self { _value: value }
++    }
++}
++
++impl<T: Default> Default for NotBox<T> {
++    fn default() -> Self {
++        Self::new(T::default())
++    }
++}
++
++#[start]
++fn main(_argc: isize, _argv: *const *const u8) -> isize {
++    let _p = NotBox::new(isize::default());
++    0
++}
++
++#[panic_handler]
++fn panic(_info: &core::panic::PanicInfo) -> ! {
++    loop {}
++}
++
++#[lang = "eh_personality"]
++extern "C" fn eh_personality() {}
index a37f3fec20f1ed2eabddd868ea3af2d89052f820,0000000000000000000000000000000000000000..e6bf944c7a5ecbb3cc08836e9a236c82b34424b9
mode 100644,000000..100644
--- /dev/null
@@@ -1,32 -1,0 +1,48 @@@
- #![allow(clippy::uninlined_format_args)]
 +// run-rustfix
++
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::cast_abs_to_unsigned)]
++#![allow(clippy::uninlined_format_args, unused)]
 +
 +fn main() {
 +    let x: i32 = -42;
 +    let y: u32 = x.unsigned_abs();
 +    println!("The absolute value of {} is {}", x, y);
 +
 +    let a: i32 = -3;
 +    let _: usize = a.unsigned_abs() as usize;
 +    let _: usize = a.unsigned_abs() as _;
 +    let _ = a.unsigned_abs() as usize;
 +
 +    let a: i64 = -3;
 +    let _ = a.unsigned_abs() as usize;
 +    let _ = a.unsigned_abs() as u8;
 +    let _ = a.unsigned_abs() as u16;
 +    let _ = a.unsigned_abs() as u32;
 +    let _ = a.unsigned_abs();
 +    let _ = a.unsigned_abs() as u128;
 +
 +    let a: isize = -3;
 +    let _ = a.unsigned_abs();
 +    let _ = a.unsigned_abs() as u8;
 +    let _ = a.unsigned_abs() as u16;
 +    let _ = a.unsigned_abs() as u32;
 +    let _ = a.unsigned_abs() as u64;
 +    let _ = a.unsigned_abs() as u128;
 +
 +    let _ = (x as i64 - y as i64).unsigned_abs() as u32;
 +}
++
++fn msrv_1_50() {
++    #![clippy::msrv = "1.50"]
++
++    let x: i32 = 10;
++    assert_eq!(10u32, x.abs() as u32);
++}
++
++fn msrv_1_51() {
++    #![clippy::msrv = "1.51"]
++
++    let x: i32 = 10;
++    assert_eq!(10u32, x.unsigned_abs());
++}
index 5706930af5a05ec87b43b0471be6bd2f3e8ccfb1,0000000000000000000000000000000000000000..c87320b5209db8165714a1a1f7f2b837b6380287
mode 100644,000000..100644
--- /dev/null
@@@ -1,32 -1,0 +1,48 @@@
- #![allow(clippy::uninlined_format_args)]
 +// run-rustfix
++
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::cast_abs_to_unsigned)]
++#![allow(clippy::uninlined_format_args, unused)]
 +
 +fn main() {
 +    let x: i32 = -42;
 +    let y: u32 = x.abs() as u32;
 +    println!("The absolute value of {} is {}", x, y);
 +
 +    let a: i32 = -3;
 +    let _: usize = a.abs() as usize;
 +    let _: usize = a.abs() as _;
 +    let _ = a.abs() as usize;
 +
 +    let a: i64 = -3;
 +    let _ = a.abs() as usize;
 +    let _ = a.abs() as u8;
 +    let _ = a.abs() as u16;
 +    let _ = a.abs() as u32;
 +    let _ = a.abs() as u64;
 +    let _ = a.abs() as u128;
 +
 +    let a: isize = -3;
 +    let _ = a.abs() as usize;
 +    let _ = a.abs() as u8;
 +    let _ = a.abs() as u16;
 +    let _ = a.abs() as u32;
 +    let _ = a.abs() as u64;
 +    let _ = a.abs() as u128;
 +
 +    let _ = (x as i64 - y as i64).abs() as u32;
 +}
++
++fn msrv_1_50() {
++    #![clippy::msrv = "1.50"]
++
++    let x: i32 = 10;
++    assert_eq!(10u32, x.abs() as u32);
++}
++
++fn msrv_1_51() {
++    #![clippy::msrv = "1.51"]
++
++    let x: i32 = 10;
++    assert_eq!(10u32, x.abs() as u32);
++}
index 7cea11c183d236fbf229e606e2689a3a0923a96e,0000000000000000000000000000000000000000..1b39c554b03845d12d1c13cdfabfc23c48949a79
mode 100644,000000..100644
--- /dev/null
@@@ -1,106 -1,0 +1,112 @@@
-   --> $DIR/cast_abs_to_unsigned.rs:7:18
 +error: casting the result of `i32::abs()` to u32
-   --> $DIR/cast_abs_to_unsigned.rs:11:20
++  --> $DIR/cast_abs_to_unsigned.rs:9:18
 +   |
 +LL |     let y: u32 = x.abs() as u32;
 +   |                  ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()`
 +   |
 +   = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings`
 +
 +error: casting the result of `i32::abs()` to usize
-   --> $DIR/cast_abs_to_unsigned.rs:12:20
++  --> $DIR/cast_abs_to_unsigned.rs:13:20
 +   |
 +LL |     let _: usize = a.abs() as usize;
 +   |                    ^^^^^^^ help: replace with: `a.unsigned_abs()`
 +
 +error: casting the result of `i32::abs()` to usize
-   --> $DIR/cast_abs_to_unsigned.rs:13:13
++  --> $DIR/cast_abs_to_unsigned.rs:14:20
 +   |
 +LL |     let _: usize = a.abs() as _;
 +   |                    ^^^^^^^ help: replace with: `a.unsigned_abs()`
 +
 +error: casting the result of `i32::abs()` to usize
-   --> $DIR/cast_abs_to_unsigned.rs:16:13
++  --> $DIR/cast_abs_to_unsigned.rs:15:13
 +   |
 +LL |     let _ = a.abs() as usize;
 +   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 +
 +error: casting the result of `i64::abs()` to usize
-   --> $DIR/cast_abs_to_unsigned.rs:17:13
++  --> $DIR/cast_abs_to_unsigned.rs:18:13
 +   |
 +LL |     let _ = a.abs() as usize;
 +   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 +
 +error: casting the result of `i64::abs()` to u8
-   --> $DIR/cast_abs_to_unsigned.rs:18:13
++  --> $DIR/cast_abs_to_unsigned.rs:19:13
 +   |
 +LL |     let _ = a.abs() as u8;
 +   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 +
 +error: casting the result of `i64::abs()` to u16
-   --> $DIR/cast_abs_to_unsigned.rs:19:13
++  --> $DIR/cast_abs_to_unsigned.rs:20:13
 +   |
 +LL |     let _ = a.abs() as u16;
 +   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 +
 +error: casting the result of `i64::abs()` to u32
-   --> $DIR/cast_abs_to_unsigned.rs:20:13
++  --> $DIR/cast_abs_to_unsigned.rs:21:13
 +   |
 +LL |     let _ = a.abs() as u32;
 +   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 +
 +error: casting the result of `i64::abs()` to u64
-   --> $DIR/cast_abs_to_unsigned.rs:21:13
++  --> $DIR/cast_abs_to_unsigned.rs:22:13
 +   |
 +LL |     let _ = a.abs() as u64;
 +   |             ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()`
 +
 +error: casting the result of `i64::abs()` to u128
-   --> $DIR/cast_abs_to_unsigned.rs:24:13
++  --> $DIR/cast_abs_to_unsigned.rs:23:13
 +   |
 +LL |     let _ = a.abs() as u128;
 +   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 +
 +error: casting the result of `isize::abs()` to usize
-   --> $DIR/cast_abs_to_unsigned.rs:25:13
++  --> $DIR/cast_abs_to_unsigned.rs:26:13
 +   |
 +LL |     let _ = a.abs() as usize;
 +   |             ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()`
 +
 +error: casting the result of `isize::abs()` to u8
-   --> $DIR/cast_abs_to_unsigned.rs:26:13
++  --> $DIR/cast_abs_to_unsigned.rs:27:13
 +   |
 +LL |     let _ = a.abs() as u8;
 +   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 +
 +error: casting the result of `isize::abs()` to u16
-   --> $DIR/cast_abs_to_unsigned.rs:27:13
++  --> $DIR/cast_abs_to_unsigned.rs:28:13
 +   |
 +LL |     let _ = a.abs() as u16;
 +   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 +
 +error: casting the result of `isize::abs()` to u32
-   --> $DIR/cast_abs_to_unsigned.rs:28:13
++  --> $DIR/cast_abs_to_unsigned.rs:29:13
 +   |
 +LL |     let _ = a.abs() as u32;
 +   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 +
 +error: casting the result of `isize::abs()` to u64
-   --> $DIR/cast_abs_to_unsigned.rs:29:13
++  --> $DIR/cast_abs_to_unsigned.rs:30:13
 +   |
 +LL |     let _ = a.abs() as u64;
 +   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 +
 +error: casting the result of `isize::abs()` to u128
-   --> $DIR/cast_abs_to_unsigned.rs:31:13
++  --> $DIR/cast_abs_to_unsigned.rs:31:13
 +   |
 +LL |     let _ = a.abs() as u128;
 +   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 +
 +error: casting the result of `i64::abs()` to u32
- error: aborting due to 17 previous errors
++  --> $DIR/cast_abs_to_unsigned.rs:33:13
 +   |
 +LL |     let _ = (x as i64 - y as i64).abs() as u32;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(x as i64 - y as i64).unsigned_abs()`
 +
++error: casting the result of `i32::abs()` to u32
++  --> $DIR/cast_abs_to_unsigned.rs:47:23
++   |
++LL |     assert_eq!(10u32, x.abs() as u32);
++   |                       ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()`
++
++error: aborting due to 18 previous errors
 +
index 9e2da45c37858297a38a18a163b86993b8ea70e4,0000000000000000000000000000000000000000..af13b755e310a55224e5048af11931621070106a
mode 100644,000000..100644
--- /dev/null
@@@ -1,42 -1,0 +1,55 @@@
 +// run-rustfix
 +
++#![feature(custom_inner_attributes)]
 +#![allow(dead_code)]
 +#![warn(clippy::cast_lossless)]
 +
 +fn main() {
 +    // Test clippy::cast_lossless with casts to integer types
 +    let _ = u8::from(true);
 +    let _ = u16::from(true);
 +    let _ = u32::from(true);
 +    let _ = u64::from(true);
 +    let _ = u128::from(true);
 +    let _ = usize::from(true);
 +
 +    let _ = i8::from(true);
 +    let _ = i16::from(true);
 +    let _ = i32::from(true);
 +    let _ = i64::from(true);
 +    let _ = i128::from(true);
 +    let _ = isize::from(true);
 +
 +    // Test with an expression wrapped in parens
 +    let _ = u16::from(true | false);
 +}
 +
 +// The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const,
 +// so we skip the lint if the expression is in a const fn.
 +// See #3656
 +const fn abc(input: bool) -> u32 {
 +    input as u32
 +}
 +
 +// Same as the above issue. We can't suggest `::from` in const fns in impls
 +mod cast_lossless_in_impl {
 +    struct A;
 +
 +    impl A {
 +        pub const fn convert(x: bool) -> u64 {
 +            x as u64
 +        }
 +    }
 +}
++
++fn msrv_1_27() {
++    #![clippy::msrv = "1.27"]
++
++    let _ = true as u8;
++}
++
++fn msrv_1_28() {
++    #![clippy::msrv = "1.28"]
++
++    let _ = u8::from(true);
++}
index b6f6c59a01f951905bf6195765a221d4c884b8a4,0000000000000000000000000000000000000000..3b06af899c60d0a5118b671a8a7066714da9a36d
mode 100644,000000..100644
--- /dev/null
@@@ -1,42 -1,0 +1,55 @@@
 +// run-rustfix
 +
++#![feature(custom_inner_attributes)]
 +#![allow(dead_code)]
 +#![warn(clippy::cast_lossless)]
 +
 +fn main() {
 +    // Test clippy::cast_lossless with casts to integer types
 +    let _ = true as u8;
 +    let _ = true as u16;
 +    let _ = true as u32;
 +    let _ = true as u64;
 +    let _ = true as u128;
 +    let _ = true as usize;
 +
 +    let _ = true as i8;
 +    let _ = true as i16;
 +    let _ = true as i32;
 +    let _ = true as i64;
 +    let _ = true as i128;
 +    let _ = true as isize;
 +
 +    // Test with an expression wrapped in parens
 +    let _ = (true | false) as u16;
 +}
 +
 +// The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const,
 +// so we skip the lint if the expression is in a const fn.
 +// See #3656
 +const fn abc(input: bool) -> u32 {
 +    input as u32
 +}
 +
 +// Same as the above issue. We can't suggest `::from` in const fns in impls
 +mod cast_lossless_in_impl {
 +    struct A;
 +
 +    impl A {
 +        pub const fn convert(x: bool) -> u64 {
 +            x as u64
 +        }
 +    }
 +}
++
++fn msrv_1_27() {
++    #![clippy::msrv = "1.27"]
++
++    let _ = true as u8;
++}
++
++fn msrv_1_28() {
++    #![clippy::msrv = "1.28"]
++
++    let _ = true as u8;
++}
index 6b148336011d5924f42311a47507a9ba08635f37,0000000000000000000000000000000000000000..768b033d10a28add2d6b2383346567d39edd0fc6
mode 100644,000000..100644
--- /dev/null
@@@ -1,82 -1,0 +1,88 @@@
-   --> $DIR/cast_lossless_bool.rs:8:13
 +error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)`
-   --> $DIR/cast_lossless_bool.rs:9:13
++  --> $DIR/cast_lossless_bool.rs:9:13
 +   |
 +LL |     let _ = true as u8;
 +   |             ^^^^^^^^^^ help: try: `u8::from(true)`
 +   |
 +   = note: `-D clippy::cast-lossless` implied by `-D warnings`
 +
 +error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
-   --> $DIR/cast_lossless_bool.rs:10:13
++  --> $DIR/cast_lossless_bool.rs:10:13
 +   |
 +LL |     let _ = true as u16;
 +   |             ^^^^^^^^^^^ help: try: `u16::from(true)`
 +
 +error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)`
-   --> $DIR/cast_lossless_bool.rs:11:13
++  --> $DIR/cast_lossless_bool.rs:11:13
 +   |
 +LL |     let _ = true as u32;
 +   |             ^^^^^^^^^^^ help: try: `u32::from(true)`
 +
 +error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)`
-   --> $DIR/cast_lossless_bool.rs:12:13
++  --> $DIR/cast_lossless_bool.rs:12:13
 +   |
 +LL |     let _ = true as u64;
 +   |             ^^^^^^^^^^^ help: try: `u64::from(true)`
 +
 +error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)`
-   --> $DIR/cast_lossless_bool.rs:13:13
++  --> $DIR/cast_lossless_bool.rs:13:13
 +   |
 +LL |     let _ = true as u128;
 +   |             ^^^^^^^^^^^^ help: try: `u128::from(true)`
 +
 +error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)`
-   --> $DIR/cast_lossless_bool.rs:15:13
++  --> $DIR/cast_lossless_bool.rs:14:13
 +   |
 +LL |     let _ = true as usize;
 +   |             ^^^^^^^^^^^^^ help: try: `usize::from(true)`
 +
 +error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)`
-   --> $DIR/cast_lossless_bool.rs:16:13
++  --> $DIR/cast_lossless_bool.rs:16:13
 +   |
 +LL |     let _ = true as i8;
 +   |             ^^^^^^^^^^ help: try: `i8::from(true)`
 +
 +error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)`
-   --> $DIR/cast_lossless_bool.rs:17:13
++  --> $DIR/cast_lossless_bool.rs:17:13
 +   |
 +LL |     let _ = true as i16;
 +   |             ^^^^^^^^^^^ help: try: `i16::from(true)`
 +
 +error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)`
-   --> $DIR/cast_lossless_bool.rs:18:13
++  --> $DIR/cast_lossless_bool.rs:18:13
 +   |
 +LL |     let _ = true as i32;
 +   |             ^^^^^^^^^^^ help: try: `i32::from(true)`
 +
 +error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)`
-   --> $DIR/cast_lossless_bool.rs:19:13
++  --> $DIR/cast_lossless_bool.rs:19:13
 +   |
 +LL |     let _ = true as i64;
 +   |             ^^^^^^^^^^^ help: try: `i64::from(true)`
 +
 +error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)`
-   --> $DIR/cast_lossless_bool.rs:20:13
++  --> $DIR/cast_lossless_bool.rs:20:13
 +   |
 +LL |     let _ = true as i128;
 +   |             ^^^^^^^^^^^^ help: try: `i128::from(true)`
 +
 +error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)`
-   --> $DIR/cast_lossless_bool.rs:23:13
++  --> $DIR/cast_lossless_bool.rs:21:13
 +   |
 +LL |     let _ = true as isize;
 +   |             ^^^^^^^^^^^^^ help: try: `isize::from(true)`
 +
 +error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
- error: aborting due to 13 previous errors
++  --> $DIR/cast_lossless_bool.rs:24:13
 +   |
 +LL |     let _ = (true | false) as u16;
 +   |             ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)`
 +
++error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)`
++  --> $DIR/cast_lossless_bool.rs:54:13
++   |
++LL |     let _ = true as u8;
++   |             ^^^^^^^^^^ help: try: `u8::from(true)`
++
++error: aborting due to 14 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..287c5aa216bd3341245baa0f54606bce2a2df3ed
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++#![warn(clippy::cast_nan_to_int)]
++#![allow(clippy::eq_op)]
++
++fn main() {
++    let _ = (0.0_f32 / -0.0) as usize;
++    let _ = (f64::INFINITY * -0.0) as usize;
++    let _ = (0.0 * f32::INFINITY) as usize;
++
++    let _ = (f64::INFINITY + f64::NEG_INFINITY) as usize;
++    let _ = (f32::INFINITY - f32::INFINITY) as usize;
++    let _ = (f32::INFINITY / f32::NEG_INFINITY) as usize;
++
++    // those won't be linted:
++    let _ = (1.0_f32 / 0.0) as usize;
++    let _ = (f32::INFINITY * f32::NEG_INFINITY) as usize;
++    let _ = (f32::INFINITY - f32::NEG_INFINITY) as usize;
++    let _ = (f64::INFINITY - 0.0) as usize;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3539be75a19db09d19bba2f96a637fe11c5c0b1c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++error: casting a known NaN to usize
++  --> $DIR/cast_nan_to_int.rs:5:13
++   |
++LL |     let _ = (0.0_f32 / -0.0) as usize;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: this always evaluates to 0
++   = note: `-D clippy::cast-nan-to-int` implied by `-D warnings`
++
++error: casting a known NaN to usize
++  --> $DIR/cast_nan_to_int.rs:6:13
++   |
++LL |     let _ = (f64::INFINITY * -0.0) as usize;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: this always evaluates to 0
++
++error: casting a known NaN to usize
++  --> $DIR/cast_nan_to_int.rs:7:13
++   |
++LL |     let _ = (0.0 * f32::INFINITY) as usize;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: this always evaluates to 0
++
++error: casting a known NaN to usize
++  --> $DIR/cast_nan_to_int.rs:9:13
++   |
++LL |     let _ = (f64::INFINITY + f64::NEG_INFINITY) as usize;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: this always evaluates to 0
++
++error: casting a known NaN to usize
++  --> $DIR/cast_nan_to_int.rs:10:13
++   |
++LL |     let _ = (f32::INFINITY - f32::INFINITY) as usize;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: this always evaluates to 0
++
++error: casting a known NaN to usize
++  --> $DIR/cast_nan_to_int.rs:11:13
++   |
++LL |     let _ = (f32::INFINITY / f32::NEG_INFINITY) as usize;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: this always evaluates to 0
++
++error: aborting due to 6 previous errors
++
index 061a4ab9b2ef8da2809901e986d07b3b45ab64ca,0000000000000000000000000000000000000000..8a5645b22ed19ddf82512b2c51944e7f92736555
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,45 @@@
- #![feature(stmt_expr_attributes)]
 +// run-rustfix
++#![feature(stmt_expr_attributes, custom_inner_attributes)]
 +
 +#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
 +#![warn(clippy::deprecated_cfg_attr)]
 +
 +// This doesn't get linted, see known problems
 +#![cfg_attr(rustfmt, rustfmt_skip)]
 +
 +#[rustfmt::skip]
 +trait Foo
 +{
 +fn foo(
 +);
 +}
 +
 +fn skip_on_statements() {
 +    #[rustfmt::skip]
 +    5+3;
 +}
 +
 +#[rustfmt::skip]
 +fn main() {
 +    foo::f();
 +}
 +
 +mod foo {
 +    #![cfg_attr(rustfmt, rustfmt_skip)]
 +
 +    pub fn f() {}
 +}
++
++fn msrv_1_29() {
++    #![clippy::msrv = "1.29"]
++
++    #[cfg_attr(rustfmt, rustfmt::skip)]
++    1+29;
++}
++
++fn msrv_1_30() {
++    #![clippy::msrv = "1.30"]
++
++    #[rustfmt::skip]
++    1+30;
++}
index 035169fab85befb570e7d06ebcc5ff518652109b,0000000000000000000000000000000000000000..2fb140efae7680085af0ea9eab5c089edb1fd1c6
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,45 @@@
- #![feature(stmt_expr_attributes)]
 +// run-rustfix
++#![feature(stmt_expr_attributes, custom_inner_attributes)]
 +
 +#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
 +#![warn(clippy::deprecated_cfg_attr)]
 +
 +// This doesn't get linted, see known problems
 +#![cfg_attr(rustfmt, rustfmt_skip)]
 +
 +#[rustfmt::skip]
 +trait Foo
 +{
 +fn foo(
 +);
 +}
 +
 +fn skip_on_statements() {
 +    #[cfg_attr(rustfmt, rustfmt::skip)]
 +    5+3;
 +}
 +
 +#[cfg_attr(rustfmt, rustfmt_skip)]
 +fn main() {
 +    foo::f();
 +}
 +
 +mod foo {
 +    #![cfg_attr(rustfmt, rustfmt_skip)]
 +
 +    pub fn f() {}
 +}
++
++fn msrv_1_29() {
++    #![clippy::msrv = "1.29"]
++
++    #[cfg_attr(rustfmt, rustfmt::skip)]
++    1+29;
++}
++
++fn msrv_1_30() {
++    #![clippy::msrv = "1.30"]
++
++    #[cfg_attr(rustfmt, rustfmt::skip)]
++    1+30;
++}
index c1efd47db90b0f313132e0a0465c5cf2826672e2,0000000000000000000000000000000000000000..08df7b2b39a0c26569df08e183e2f3a481c9f0d4
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,22 @@@
- error: aborting due to 2 previous errors
 +error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes
 +  --> $DIR/cfg_attr_rustfmt.rs:18:5
 +   |
 +LL |     #[cfg_attr(rustfmt, rustfmt::skip)]
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]`
 +   |
 +   = note: `-D clippy::deprecated-cfg-attr` implied by `-D warnings`
 +
 +error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes
 +  --> $DIR/cfg_attr_rustfmt.rs:22:1
 +   |
 +LL | #[cfg_attr(rustfmt, rustfmt_skip)]
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]`
 +
++error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes
++  --> $DIR/cfg_attr_rustfmt.rs:43:5
++   |
++LL |     #[cfg_attr(rustfmt, rustfmt::skip)]
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]`
++
++error: aborting due to 3 previous errors
 +
index cb7100bc9efae463047f00fe353fb1bce319b8b2,0000000000000000000000000000000000000000..f936957cb40c682d7d6bdbb538fddacb07c943fc
mode 100644,000000..100644
--- /dev/null
@@@ -1,79 -1,0 +1,95 @@@
 +// run-rustfix
 +
++#![feature(custom_inner_attributes)]
 +#![allow(
 +    clippy::cast_lossless,
++    unused,
 +    // Int::max_value will be deprecated in the future
 +    deprecated,
 +)]
 +#![warn(clippy::checked_conversions)]
 +
 +// Positive tests
 +
 +// Signed to unsigned
 +
 +pub fn i64_to_u32(value: i64) {
 +    let _ = u32::try_from(value).is_ok();
 +    let _ = u32::try_from(value).is_ok();
 +}
 +
 +pub fn i64_to_u16(value: i64) {
 +    let _ = u16::try_from(value).is_ok();
 +    let _ = u16::try_from(value).is_ok();
 +}
 +
 +pub fn isize_to_u8(value: isize) {
 +    let _ = u8::try_from(value).is_ok();
 +    let _ = u8::try_from(value).is_ok();
 +}
 +
 +// Signed to signed
 +
 +pub fn i64_to_i32(value: i64) {
 +    let _ = i32::try_from(value).is_ok();
 +    let _ = i32::try_from(value).is_ok();
 +}
 +
 +pub fn i64_to_i16(value: i64) {
 +    let _ = i16::try_from(value).is_ok();
 +    let _ = i16::try_from(value).is_ok();
 +}
 +
 +// Unsigned to X
 +
 +pub fn u32_to_i32(value: u32) {
 +    let _ = i32::try_from(value).is_ok();
 +    let _ = i32::try_from(value).is_ok();
 +}
 +
 +pub fn usize_to_isize(value: usize) {
 +    let _ = isize::try_from(value).is_ok() && value as i32 == 5;
 +    let _ = isize::try_from(value).is_ok() && value as i32 == 5;
 +}
 +
 +pub fn u32_to_u16(value: u32) {
 +    let _ = u16::try_from(value).is_ok() && value as i32 == 5;
 +    let _ = u16::try_from(value).is_ok() && value as i32 == 5;
 +}
 +
 +// Negative tests
 +
 +pub fn no_i64_to_i32(value: i64) {
 +    let _ = value <= (i32::max_value() as i64) && value >= 0;
 +    let _ = value <= (i32::MAX as i64) && value >= 0;
 +}
 +
 +pub fn no_isize_to_u8(value: isize) {
 +    let _ = value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize);
 +    let _ = value <= (u8::MAX as isize) && value >= (u8::MIN as isize);
 +}
 +
 +pub fn i8_to_u8(value: i8) {
 +    let _ = value >= 0;
 +}
 +
 +// Do not lint
 +pub const fn issue_8898(i: u32) -> bool {
 +    i <= i32::MAX as u32
 +}
 +
++fn msrv_1_33() {
++    #![clippy::msrv = "1.33"]
++
++    let value: i64 = 33;
++    let _ = value <= (u32::MAX as i64) && value >= 0;
++}
++
++fn msrv_1_34() {
++    #![clippy::msrv = "1.34"]
++
++    let value: i64 = 34;
++    let _ = u32::try_from(value).is_ok();
++}
++
 +fn main() {}
index ed4e0692388a55a4ad65dee76bf2649af70db3b2,0000000000000000000000000000000000000000..77aec713ff3169079e3562ba9d413116196079d8
mode 100644,000000..100644
--- /dev/null
@@@ -1,79 -1,0 +1,95 @@@
 +// run-rustfix
 +
++#![feature(custom_inner_attributes)]
 +#![allow(
 +    clippy::cast_lossless,
++    unused,
 +    // Int::max_value will be deprecated in the future
 +    deprecated,
 +)]
 +#![warn(clippy::checked_conversions)]
 +
 +// Positive tests
 +
 +// Signed to unsigned
 +
 +pub fn i64_to_u32(value: i64) {
 +    let _ = value <= (u32::max_value() as i64) && value >= 0;
 +    let _ = value <= (u32::MAX as i64) && value >= 0;
 +}
 +
 +pub fn i64_to_u16(value: i64) {
 +    let _ = value <= i64::from(u16::max_value()) && value >= 0;
 +    let _ = value <= i64::from(u16::MAX) && value >= 0;
 +}
 +
 +pub fn isize_to_u8(value: isize) {
 +    let _ = value <= (u8::max_value() as isize) && value >= 0;
 +    let _ = value <= (u8::MAX as isize) && value >= 0;
 +}
 +
 +// Signed to signed
 +
 +pub fn i64_to_i32(value: i64) {
 +    let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64);
 +    let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64);
 +}
 +
 +pub fn i64_to_i16(value: i64) {
 +    let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value());
 +    let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN);
 +}
 +
 +// Unsigned to X
 +
 +pub fn u32_to_i32(value: u32) {
 +    let _ = value <= i32::max_value() as u32;
 +    let _ = value <= i32::MAX as u32;
 +}
 +
 +pub fn usize_to_isize(value: usize) {
 +    let _ = value <= isize::max_value() as usize && value as i32 == 5;
 +    let _ = value <= isize::MAX as usize && value as i32 == 5;
 +}
 +
 +pub fn u32_to_u16(value: u32) {
 +    let _ = value <= u16::max_value() as u32 && value as i32 == 5;
 +    let _ = value <= u16::MAX as u32 && value as i32 == 5;
 +}
 +
 +// Negative tests
 +
 +pub fn no_i64_to_i32(value: i64) {
 +    let _ = value <= (i32::max_value() as i64) && value >= 0;
 +    let _ = value <= (i32::MAX as i64) && value >= 0;
 +}
 +
 +pub fn no_isize_to_u8(value: isize) {
 +    let _ = value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize);
 +    let _ = value <= (u8::MAX as isize) && value >= (u8::MIN as isize);
 +}
 +
 +pub fn i8_to_u8(value: i8) {
 +    let _ = value >= 0;
 +}
 +
 +// Do not lint
 +pub const fn issue_8898(i: u32) -> bool {
 +    i <= i32::MAX as u32
 +}
 +
++fn msrv_1_33() {
++    #![clippy::msrv = "1.33"]
++
++    let value: i64 = 33;
++    let _ = value <= (u32::MAX as i64) && value >= 0;
++}
++
++fn msrv_1_34() {
++    #![clippy::msrv = "1.34"]
++
++    let value: i64 = 34;
++    let _ = value <= (u32::MAX as i64) && value >= 0;
++}
++
 +fn main() {}
index 2e518040561c4abd16c2875416b4567360664461,0000000000000000000000000000000000000000..b2bf7af8daf87a8df2b6a723a1b72ee53dc882c8
mode 100644,000000..100644
--- /dev/null
@@@ -1,100 -1,0 +1,106 @@@
-   --> $DIR/checked_conversions.rs:15:13
 +error: checked cast can be simplified
-   --> $DIR/checked_conversions.rs:16:13
++  --> $DIR/checked_conversions.rs:17:13
 +   |
 +LL |     let _ = value <= (u32::max_value() as i64) && value >= 0;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
 +   |
 +   = note: `-D clippy::checked-conversions` implied by `-D warnings`
 +
 +error: checked cast can be simplified
-   --> $DIR/checked_conversions.rs:20:13
++  --> $DIR/checked_conversions.rs:18:13
 +   |
 +LL |     let _ = value <= (u32::MAX as i64) && value >= 0;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
 +
 +error: checked cast can be simplified
-   --> $DIR/checked_conversions.rs:21:13
++  --> $DIR/checked_conversions.rs:22:13
 +   |
 +LL |     let _ = value <= i64::from(u16::max_value()) && value >= 0;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 +
 +error: checked cast can be simplified
-   --> $DIR/checked_conversions.rs:25:13
++  --> $DIR/checked_conversions.rs:23:13
 +   |
 +LL |     let _ = value <= i64::from(u16::MAX) && value >= 0;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 +
 +error: checked cast can be simplified
-   --> $DIR/checked_conversions.rs:26:13
++  --> $DIR/checked_conversions.rs:27:13
 +   |
 +LL |     let _ = value <= (u8::max_value() as isize) && value >= 0;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
 +
 +error: checked cast can be simplified
-   --> $DIR/checked_conversions.rs:32:13
++  --> $DIR/checked_conversions.rs:28:13
 +   |
 +LL |     let _ = value <= (u8::MAX as isize) && value >= 0;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
 +
 +error: checked cast can be simplified
-   --> $DIR/checked_conversions.rs:33:13
++  --> $DIR/checked_conversions.rs:34:13
 +   |
 +LL |     let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 +
 +error: checked cast can be simplified
-   --> $DIR/checked_conversions.rs:37:13
++  --> $DIR/checked_conversions.rs:35:13
 +   |
 +LL |     let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 +
 +error: checked cast can be simplified
-   --> $DIR/checked_conversions.rs:38:13
++  --> $DIR/checked_conversions.rs:39:13
 +   |
 +LL |     let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
 +
 +error: checked cast can be simplified
-   --> $DIR/checked_conversions.rs:44:13
++  --> $DIR/checked_conversions.rs:40:13
 +   |
 +LL |     let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
 +
 +error: checked cast can be simplified
-   --> $DIR/checked_conversions.rs:45:13
++  --> $DIR/checked_conversions.rs:46:13
 +   |
 +LL |     let _ = value <= i32::max_value() as u32;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 +
 +error: checked cast can be simplified
-   --> $DIR/checked_conversions.rs:49:13
++  --> $DIR/checked_conversions.rs:47:13
 +   |
 +LL |     let _ = value <= i32::MAX as u32;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 +
 +error: checked cast can be simplified
-   --> $DIR/checked_conversions.rs:50:13
++  --> $DIR/checked_conversions.rs:51:13
 +   |
 +LL |     let _ = value <= isize::max_value() as usize && value as i32 == 5;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
 +
 +error: checked cast can be simplified
-   --> $DIR/checked_conversions.rs:54:13
++  --> $DIR/checked_conversions.rs:52:13
 +   |
 +LL |     let _ = value <= isize::MAX as usize && value as i32 == 5;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
 +
 +error: checked cast can be simplified
-   --> $DIR/checked_conversions.rs:55:13
++  --> $DIR/checked_conversions.rs:56:13
 +   |
 +LL |     let _ = value <= u16::max_value() as u32 && value as i32 == 5;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 +
 +error: checked cast can be simplified
- error: aborting due to 16 previous errors
++  --> $DIR/checked_conversions.rs:57:13
 +   |
 +LL |     let _ = value <= u16::MAX as u32 && value as i32 == 5;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 +
++error: checked cast can be simplified
++  --> $DIR/checked_conversions.rs:92:13
++   |
++LL |     let _ = value <= (u32::MAX as i64) && value >= 0;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
++
++error: aborting due to 17 previous errors
 +
index 4eb999e18e64e35c33e43fd1ecf9aff0aa0c6a55,0000000000000000000000000000000000000000..42ed232d1001cd3dd8c52b3efff71b8d747daab2
mode 100644,000000..100644
--- /dev/null
@@@ -1,15 -1,0 +1,39 @@@
 +// run-rustfix
++
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::cloned_instead_of_copied)]
++#![allow(unused)]
 +
 +fn main() {
 +    // yay
 +    let _ = [1].iter().copied();
 +    let _ = vec!["hi"].iter().copied();
 +    let _ = Some(&1).copied();
 +    let _ = Box::new([1].iter()).copied();
 +    let _ = Box::new(Some(&1)).copied();
 +
 +    // nay
 +    let _ = [String::new()].iter().cloned();
 +    let _ = Some(&String::new()).cloned();
 +}
++
++fn msrv_1_34() {
++    #![clippy::msrv = "1.34"]
++
++    let _ = [1].iter().cloned();
++    let _ = Some(&1).cloned();
++}
++
++fn msrv_1_35() {
++    #![clippy::msrv = "1.35"]
++
++    let _ = [1].iter().cloned();
++    let _ = Some(&1).copied(); // Option::copied needs 1.35
++}
++
++fn msrv_1_36() {
++    #![clippy::msrv = "1.36"]
++
++    let _ = [1].iter().copied(); // Iterator::copied needs 1.36
++    let _ = Some(&1).copied();
++}
index 894496c0ebbb5de0831cafe122d3f9debbee39aa,0000000000000000000000000000000000000000..471bd9654cc13371fa01948040fdf25e94a4e60a
mode 100644,000000..100644
--- /dev/null
@@@ -1,15 -1,0 +1,39 @@@
 +// run-rustfix
++
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::cloned_instead_of_copied)]
++#![allow(unused)]
 +
 +fn main() {
 +    // yay
 +    let _ = [1].iter().cloned();
 +    let _ = vec!["hi"].iter().cloned();
 +    let _ = Some(&1).cloned();
 +    let _ = Box::new([1].iter()).cloned();
 +    let _ = Box::new(Some(&1)).cloned();
 +
 +    // nay
 +    let _ = [String::new()].iter().cloned();
 +    let _ = Some(&String::new()).cloned();
 +}
++
++fn msrv_1_34() {
++    #![clippy::msrv = "1.34"]
++
++    let _ = [1].iter().cloned();
++    let _ = Some(&1).cloned();
++}
++
++fn msrv_1_35() {
++    #![clippy::msrv = "1.35"]
++
++    let _ = [1].iter().cloned();
++    let _ = Some(&1).cloned(); // Option::copied needs 1.35
++}
++
++fn msrv_1_36() {
++    #![clippy::msrv = "1.36"]
++
++    let _ = [1].iter().cloned(); // Iterator::copied needs 1.36
++    let _ = Some(&1).cloned();
++}
index e0707d32146891a50f6caca35169c453870459f4,0000000000000000000000000000000000000000..914c9a91e8300e048faf1bc014655c5bc073273f
mode 100644,000000..100644
--- /dev/null
@@@ -1,34 -1,0 +1,52 @@@
-   --> $DIR/cloned_instead_of_copied.rs:6:24
 +error: used `cloned` where `copied` could be used instead
-   --> $DIR/cloned_instead_of_copied.rs:7:31
++  --> $DIR/cloned_instead_of_copied.rs:9:24
 +   |
 +LL |     let _ = [1].iter().cloned();
 +   |                        ^^^^^^ help: try: `copied`
 +   |
 +   = note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings`
 +
 +error: used `cloned` where `copied` could be used instead
-   --> $DIR/cloned_instead_of_copied.rs:8:22
++  --> $DIR/cloned_instead_of_copied.rs:10:31
 +   |
 +LL |     let _ = vec!["hi"].iter().cloned();
 +   |                               ^^^^^^ help: try: `copied`
 +
 +error: used `cloned` where `copied` could be used instead
-   --> $DIR/cloned_instead_of_copied.rs:9:34
++  --> $DIR/cloned_instead_of_copied.rs:11:22
 +   |
 +LL |     let _ = Some(&1).cloned();
 +   |                      ^^^^^^ help: try: `copied`
 +
 +error: used `cloned` where `copied` could be used instead
-   --> $DIR/cloned_instead_of_copied.rs:10:32
++  --> $DIR/cloned_instead_of_copied.rs:12:34
 +   |
 +LL |     let _ = Box::new([1].iter()).cloned();
 +   |                                  ^^^^^^ help: try: `copied`
 +
 +error: used `cloned` where `copied` could be used instead
- error: aborting due to 5 previous errors
++  --> $DIR/cloned_instead_of_copied.rs:13:32
 +   |
 +LL |     let _ = Box::new(Some(&1)).cloned();
 +   |                                ^^^^^^ help: try: `copied`
 +
++error: used `cloned` where `copied` could be used instead
++  --> $DIR/cloned_instead_of_copied.rs:31:22
++   |
++LL |     let _ = Some(&1).cloned(); // Option::copied needs 1.35
++   |                      ^^^^^^ help: try: `copied`
++
++error: used `cloned` where `copied` could be used instead
++  --> $DIR/cloned_instead_of_copied.rs:37:24
++   |
++LL |     let _ = [1].iter().cloned(); // Iterator::copied needs 1.36
++   |                        ^^^^^^ help: try: `copied`
++
++error: used `cloned` where `copied` could be used instead
++  --> $DIR/cloned_instead_of_copied.rs:38:22
++   |
++LL |     let _ = Some(&1).cloned();
++   |                      ^^^^^^ help: try: `copied`
++
++error: aborting due to 8 previous errors
 +
index 7d53e08345d30244d5a67ab62523934ba7aa101e,0000000000000000000000000000000000000000..1d7a72846419f48bc3c60990672dc3471a52886b
mode 100644,000000..100644
--- /dev/null
@@@ -1,266 -1,0 +1,287 @@@
 +#![warn(clippy::collapsible_match)]
 +#![allow(
 +    clippy::equatable_if_let,
 +    clippy::needless_return,
 +    clippy::no_effect,
 +    clippy::single_match,
 +    clippy::uninlined_format_args
 +)]
 +
 +fn lint_cases(opt_opt: Option<Option<u32>>, res_opt: Result<Option<u32>, String>) {
 +    // match without block
 +    match res_opt {
 +        Ok(val) => match val {
 +            Some(n) => foo(n),
 +            _ => return,
 +        },
 +        _ => return,
 +    }
 +
 +    // match with block
 +    match res_opt {
 +        Ok(val) => match val {
 +            Some(n) => foo(n),
 +            _ => return,
 +        },
 +        _ => return,
 +    }
 +
 +    // if let, if let
 +    if let Ok(val) = res_opt {
 +        if let Some(n) = val {
 +            take(n);
 +        }
 +    }
 +
 +    // if let else, if let else
 +    if let Ok(val) = res_opt {
 +        if let Some(n) = val {
 +            take(n);
 +        } else {
 +            return;
 +        }
 +    } else {
 +        return;
 +    }
 +
 +    // if let, match
 +    if let Ok(val) = res_opt {
 +        match val {
 +            Some(n) => foo(n),
 +            _ => (),
 +        }
 +    }
 +
 +    // match, if let
 +    match res_opt {
 +        Ok(val) => {
 +            if let Some(n) = val {
 +                take(n);
 +            }
 +        },
 +        _ => {},
 +    }
 +
 +    // if let else, match
 +    if let Ok(val) = res_opt {
 +        match val {
 +            Some(n) => foo(n),
 +            _ => return,
 +        }
 +    } else {
 +        return;
 +    }
 +
 +    // match, if let else
 +    match res_opt {
 +        Ok(val) => {
 +            if let Some(n) = val {
 +                take(n);
 +            } else {
 +                return;
 +            }
 +        },
 +        _ => return,
 +    }
 +
 +    // None in inner match same as outer wild branch
 +    match res_opt {
 +        Ok(val) => match val {
 +            Some(n) => foo(n),
 +            None => return,
 +        },
 +        _ => return,
 +    }
 +
 +    // None in outer match same as inner wild branch
 +    match opt_opt {
 +        Some(val) => match val {
 +            Some(n) => foo(n),
 +            _ => return,
 +        },
 +        None => return,
 +    }
 +}
 +
 +fn negative_cases(res_opt: Result<Option<u32>, String>, res_res: Result<Result<u32, String>, String>) {
 +    while let Some(x) = make() {
 +        if let Some(1) = x {
 +            todo!();
 +        }
 +    }
 +    // no wild pattern in outer match
 +    match res_opt {
 +        Ok(val) => match val {
 +            Some(n) => foo(n),
 +            _ => return,
 +        },
 +        Err(_) => return,
 +    }
 +
 +    // inner branch is not wild or None
 +    match res_res {
 +        Ok(val) => match val {
 +            Ok(n) => foo(n),
 +            Err(_) => return,
 +        },
 +        _ => return,
 +    }
 +
 +    // statement before inner match
 +    match res_opt {
 +        Ok(val) => {
 +            "hi buddy";
 +            match val {
 +                Some(n) => foo(n),
 +                _ => return,
 +            }
 +        },
 +        _ => return,
 +    }
 +
 +    // statement after inner match
 +    match res_opt {
 +        Ok(val) => {
 +            match val {
 +                Some(n) => foo(n),
 +                _ => return,
 +            }
 +            "hi buddy";
 +        },
 +        _ => return,
 +    }
 +
 +    // wild branches do not match
 +    match res_opt {
 +        Ok(val) => match val {
 +            Some(n) => foo(n),
 +            _ => {
 +                "sup";
 +                return;
 +            },
 +        },
 +        _ => return,
 +    }
 +
 +    // binding used in if guard
 +    match res_opt {
 +        Ok(val) if val.is_some() => match val {
 +            Some(n) => foo(n),
 +            _ => return,
 +        },
 +        _ => return,
 +    }
 +
 +    // binding used in inner match body
 +    match res_opt {
 +        Ok(val) => match val {
 +            Some(_) => take(val),
 +            _ => return,
 +        },
 +        _ => return,
 +    }
 +
 +    // if guard on inner match
 +    {
 +        match res_opt {
 +            Ok(val) => match val {
 +                Some(n) if make() => foo(n),
 +                _ => return,
 +            },
 +            _ => return,
 +        }
 +        match res_opt {
 +            Ok(val) => match val {
 +                _ => make(),
 +                _ if make() => return,
 +            },
 +            _ => return,
 +        }
 +    }
 +
 +    // differing macro contexts
 +    {
 +        macro_rules! mac {
 +            ($val:ident) => {
 +                match $val {
 +                    Some(n) => foo(n),
 +                    _ => return,
 +                }
 +            };
 +        }
 +        match res_opt {
 +            Ok(val) => mac!(val),
 +            _ => return,
 +        }
 +    }
 +
 +    // OR pattern
 +    enum E<T> {
 +        A(T),
 +        B(T),
 +        C(T),
 +    };
 +    match make::<E<Option<u32>>>() {
 +        E::A(val) | E::B(val) => match val {
 +            Some(n) => foo(n),
 +            _ => return,
 +        },
 +        _ => return,
 +    }
 +    match make::<Option<E<u32>>>() {
 +        Some(val) => match val {
 +            E::A(val) | E::B(val) => foo(val),
 +            _ => return,
 +        },
 +        _ => return,
 +    }
 +    if let Ok(val) = res_opt {
 +        if let Some(n) = val {
 +            let _ = || {
 +                // usage in closure
 +                println!("{:?}", val);
 +            };
 +        }
 +    }
 +    let _: &dyn std::any::Any = match &Some(Some(1)) {
 +        Some(e) => match e {
 +            Some(e) => e,
 +            e => e,
 +        },
 +        // else branch looks the same but the binding is different
 +        e => e,
 +    };
 +}
 +
++pub enum Issue9647 {
++    A { a: Option<Option<u8>>, b: () },
++    B,
++}
++
++pub fn test_1(x: Issue9647) {
++    if let Issue9647::A { a, .. } = x {
++        if let Some(u) = a {
++            println!("{u:?}")
++        }
++    }
++}
++
++pub fn test_2(x: Issue9647) {
++    if let Issue9647::A { a: Some(a), .. } = x {
++        if let Some(u) = a {
++            println!("{u}")
++        }
++    }
++}
++
 +fn make<T>() -> T {
 +    unimplemented!()
 +}
 +
 +fn foo<T, U>(t: T) -> U {
 +    unimplemented!()
 +}
 +
 +fn take<T>(t: T) {}
 +
 +fn main() {}
index 2580bef58091ee0a39a5bc7f93f3b1212960919d,0000000000000000000000000000000000000000..0294be60b43fd2634840eeeaccbcfcdcbe689d8c
mode 100644,000000..100644
--- /dev/null
@@@ -1,179 -1,0 +1,211 @@@
- error: aborting due to 10 previous errors
 +error: this `match` can be collapsed into the outer `match`
 +  --> $DIR/collapsible_match.rs:13:20
 +   |
 +LL |           Ok(val) => match val {
 +   |  ____________________^
 +LL | |             Some(n) => foo(n),
 +LL | |             _ => return,
 +LL | |         },
 +   | |_________^
 +   |
 +help: the outer pattern can be modified to include the inner pattern
 +  --> $DIR/collapsible_match.rs:13:12
 +   |
 +LL |         Ok(val) => match val {
 +   |            ^^^ replace this binding
 +LL |             Some(n) => foo(n),
 +   |             ^^^^^^^ with this pattern
 +   = note: `-D clippy::collapsible-match` implied by `-D warnings`
 +
 +error: this `match` can be collapsed into the outer `match`
 +  --> $DIR/collapsible_match.rs:22:20
 +   |
 +LL |           Ok(val) => match val {
 +   |  ____________________^
 +LL | |             Some(n) => foo(n),
 +LL | |             _ => return,
 +LL | |         },
 +   | |_________^
 +   |
 +help: the outer pattern can be modified to include the inner pattern
 +  --> $DIR/collapsible_match.rs:22:12
 +   |
 +LL |         Ok(val) => match val {
 +   |            ^^^ replace this binding
 +LL |             Some(n) => foo(n),
 +   |             ^^^^^^^ with this pattern
 +
 +error: this `if let` can be collapsed into the outer `if let`
 +  --> $DIR/collapsible_match.rs:31:9
 +   |
 +LL | /         if let Some(n) = val {
 +LL | |             take(n);
 +LL | |         }
 +   | |_________^
 +   |
 +help: the outer pattern can be modified to include the inner pattern
 +  --> $DIR/collapsible_match.rs:30:15
 +   |
 +LL |     if let Ok(val) = res_opt {
 +   |               ^^^ replace this binding
 +LL |         if let Some(n) = val {
 +   |                ^^^^^^^ with this pattern
 +
 +error: this `if let` can be collapsed into the outer `if let`
 +  --> $DIR/collapsible_match.rs:38:9
 +   |
 +LL | /         if let Some(n) = val {
 +LL | |             take(n);
 +LL | |         } else {
 +LL | |             return;
 +LL | |         }
 +   | |_________^
 +   |
 +help: the outer pattern can be modified to include the inner pattern
 +  --> $DIR/collapsible_match.rs:37:15
 +   |
 +LL |     if let Ok(val) = res_opt {
 +   |               ^^^ replace this binding
 +LL |         if let Some(n) = val {
 +   |                ^^^^^^^ with this pattern
 +
 +error: this `match` can be collapsed into the outer `if let`
 +  --> $DIR/collapsible_match.rs:49:9
 +   |
 +LL | /         match val {
 +LL | |             Some(n) => foo(n),
 +LL | |             _ => (),
 +LL | |         }
 +   | |_________^
 +   |
 +help: the outer pattern can be modified to include the inner pattern
 +  --> $DIR/collapsible_match.rs:48:15
 +   |
 +LL |     if let Ok(val) = res_opt {
 +   |               ^^^ replace this binding
 +LL |         match val {
 +LL |             Some(n) => foo(n),
 +   |             ^^^^^^^ with this pattern
 +
 +error: this `if let` can be collapsed into the outer `match`
 +  --> $DIR/collapsible_match.rs:58:13
 +   |
 +LL | /             if let Some(n) = val {
 +LL | |                 take(n);
 +LL | |             }
 +   | |_____________^
 +   |
 +help: the outer pattern can be modified to include the inner pattern
 +  --> $DIR/collapsible_match.rs:57:12
 +   |
 +LL |         Ok(val) => {
 +   |            ^^^ replace this binding
 +LL |             if let Some(n) = val {
 +   |                    ^^^^^^^ with this pattern
 +
 +error: this `match` can be collapsed into the outer `if let`
 +  --> $DIR/collapsible_match.rs:67:9
 +   |
 +LL | /         match val {
 +LL | |             Some(n) => foo(n),
 +LL | |             _ => return,
 +LL | |         }
 +   | |_________^
 +   |
 +help: the outer pattern can be modified to include the inner pattern
 +  --> $DIR/collapsible_match.rs:66:15
 +   |
 +LL |     if let Ok(val) = res_opt {
 +   |               ^^^ replace this binding
 +LL |         match val {
 +LL |             Some(n) => foo(n),
 +   |             ^^^^^^^ with this pattern
 +
 +error: this `if let` can be collapsed into the outer `match`
 +  --> $DIR/collapsible_match.rs:78:13
 +   |
 +LL | /             if let Some(n) = val {
 +LL | |                 take(n);
 +LL | |             } else {
 +LL | |                 return;
 +LL | |             }
 +   | |_____________^
 +   |
 +help: the outer pattern can be modified to include the inner pattern
 +  --> $DIR/collapsible_match.rs:77:12
 +   |
 +LL |         Ok(val) => {
 +   |            ^^^ replace this binding
 +LL |             if let Some(n) = val {
 +   |                    ^^^^^^^ with this pattern
 +
 +error: this `match` can be collapsed into the outer `match`
 +  --> $DIR/collapsible_match.rs:89:20
 +   |
 +LL |           Ok(val) => match val {
 +   |  ____________________^
 +LL | |             Some(n) => foo(n),
 +LL | |             None => return,
 +LL | |         },
 +   | |_________^
 +   |
 +help: the outer pattern can be modified to include the inner pattern
 +  --> $DIR/collapsible_match.rs:89:12
 +   |
 +LL |         Ok(val) => match val {
 +   |            ^^^ replace this binding
 +LL |             Some(n) => foo(n),
 +   |             ^^^^^^^ with this pattern
 +
 +error: this `match` can be collapsed into the outer `match`
 +  --> $DIR/collapsible_match.rs:98:22
 +   |
 +LL |           Some(val) => match val {
 +   |  ______________________^
 +LL | |             Some(n) => foo(n),
 +LL | |             _ => return,
 +LL | |         },
 +   | |_________^
 +   |
 +help: the outer pattern can be modified to include the inner pattern
 +  --> $DIR/collapsible_match.rs:98:14
 +   |
 +LL |         Some(val) => match val {
 +   |              ^^^ replace this binding
 +LL |             Some(n) => foo(n),
 +   |             ^^^^^^^ with this pattern
 +
++error: this `if let` can be collapsed into the outer `if let`
++  --> $DIR/collapsible_match.rs:263:9
++   |
++LL | /         if let Some(u) = a {
++LL | |             println!("{u:?}")
++LL | |         }
++   | |_________^
++   |
++help: the outer pattern can be modified to include the inner pattern
++  --> $DIR/collapsible_match.rs:262:27
++   |
++LL |     if let Issue9647::A { a, .. } = x {
++   |                           ^ replace this binding
++LL |         if let Some(u) = a {
++   |                ^^^^^^^ with this pattern, prefixed by a:
++
++error: this `if let` can be collapsed into the outer `if let`
++  --> $DIR/collapsible_match.rs:271:9
++   |
++LL | /         if let Some(u) = a {
++LL | |             println!("{u}")
++LL | |         }
++   | |_________^
++   |
++help: the outer pattern can be modified to include the inner pattern
++  --> $DIR/collapsible_match.rs:270:35
++   |
++LL |     if let Issue9647::A { a: Some(a), .. } = x {
++   |                                   ^ replace this binding
++LL |         if let Some(u) = a {
++   |                ^^^^^^^ with this pattern
++
++error: aborting due to 12 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a765882b5d8180802c8b82b4410848feca161eda
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++fn main() {
++    let x = &1;
++    let _ = &1 < x && x < &10;
++}
index a28bff76755b62f982bf364d3e8f3fbfaeb01db7,0000000000000000000000000000000000000000..a370ccc76962e9802fed7b14ecc2be8abba88aaa
mode 100644,000000..100644
--- /dev/null
@@@ -1,177 -1,0 +1,186 @@@
 +// 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.;
++        const X: f32 = 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.
 +        };
++
++        const X: f32 = {
++            // 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.
++        };
 +    }
 +}
 +
 +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 b48435cc7b282a049e8c4425381898e7b31b2702,0000000000000000000000000000000000000000..2476fe95141dece897a260b54d29ace5e612c9c5
mode 100644,000000..100644
--- /dev/null
@@@ -1,177 -1,0 +1,186 @@@
 +// 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.;
++        const X: f32 = 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.
 +        };
++
++        const X: f32 = {
++            // 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.
++        };
 +    }
 +}
 +
 +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 f8b6c7746edbb728f1ee699b2b345bb1963ecd66,0000000000000000000000000000000000000000..5df2f642388dc562e0c807babcd6ada427d09be8
mode 100644,000000..100644
--- /dev/null
@@@ -1,147 -1,0 +1,153 @@@
-   --> $DIR/default_numeric_fallback_f64.rs:43:21
 +error: default numeric fallback might occur
 +  --> $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: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: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: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: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: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: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: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: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:25:18
 +   |
 +LL |             _ => 1.,
 +   |                  ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:51:21
++  --> $DIR/default_numeric_fallback_f64.rs:44:21
 +   |
 +LL |             let y = 1.;
 +   |                     ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:57:21
++  --> $DIR/default_numeric_fallback_f64.rs:52:21
 +   |
 +LL |             let y = 1.;
 +   |                     ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:69:9
++  --> $DIR/default_numeric_fallback_f64.rs:58:21
 +   |
 +LL |             let y = 1.;
 +   |                     ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:75:27
++  --> $DIR/default_numeric_fallback_f64.rs:66:21
++   |
++LL |             let y = 1.;
++   |                     ^^ help: consider adding suffix: `1.0_f64`
++
++error: default numeric fallback might occur
++  --> $DIR/default_numeric_fallback_f64.rs:78:9
 +   |
 +LL |         1.
 +   |         ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:79:29
++  --> $DIR/default_numeric_fallback_f64.rs:84:27
 +   |
 +LL |         let f = || -> _ { 1. };
 +   |                           ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:93:21
++  --> $DIR/default_numeric_fallback_f64.rs:88: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:96:32
++  --> $DIR/default_numeric_fallback_f64.rs:102:21
 +   |
 +LL |         generic_arg(1.);
 +   |                     ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:114:28
++  --> $DIR/default_numeric_fallback_f64.rs:105: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:117:36
++  --> $DIR/default_numeric_fallback_f64.rs:123:28
 +   |
 +LL |         GenericStruct { x: 1. };
 +   |                            ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:135:24
++  --> $DIR/default_numeric_fallback_f64.rs:126: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:155:23
++  --> $DIR/default_numeric_fallback_f64.rs:144:24
 +   |
 +LL |         GenericEnum::X(1.);
 +   |                        ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_f64.rs:162:21
++  --> $DIR/default_numeric_fallback_f64.rs:164:23
 +   |
 +LL |         s.generic_arg(1.);
 +   |                       ^^ help: consider adding suffix: `1.0_f64`
 +
 +error: default numeric fallback might occur
- error: aborting due to 23 previous errors
++  --> $DIR/default_numeric_fallback_f64.rs:171: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 24 previous errors
 +
index 55451cf2f7d0fa972bc40a7d48d8709952bdae6e,0000000000000000000000000000000000000000..3f4994f0453b1ef153fea0355d2a1130f9adafdc
mode 100644,000000..100644
--- /dev/null
@@@ -1,182 -1,0 +1,192 @@@
 +// run-rustfix
 +// aux-build:macro_rules.rs
 +
 +#![feature(lint_reasons)]
 +#![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;
++        let x: u64 = 1;
++        const CONST_X: i8 = 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
 +        };
++
++        const CONST_X: i32 = {
++            // 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
++        };
 +    }
 +}
 +
 +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 check_expect_suppression() {
 +    #[expect(clippy::default_numeric_fallback)]
 +    let x = 21;
 +}
 +
 +fn main() {}
index 62d72f2febaaa4fc75c7d80a707fa9e75f475588,0000000000000000000000000000000000000000..2df0e09787f9055d0f5034471acd9ec6b3c3c4ca
mode 100644,000000..100644
--- /dev/null
@@@ -1,182 -1,0 +1,192 @@@
 +// run-rustfix
 +// aux-build:macro_rules.rs
 +
 +#![feature(lint_reasons)]
 +#![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;
++        let x: u64 = 1;
++        const CONST_X: i8 = 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
 +        };
++
++        const CONST_X: i32 = {
++            // 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
++        };
 +    }
 +}
 +
 +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 check_expect_suppression() {
 +    #[expect(clippy::default_numeric_fallback)]
 +    let x = 21;
 +}
 +
 +fn main() {}
index f7c5e724c403cdc4a85658800e89e377b106c62e,0000000000000000000000000000000000000000..6f219c3fc2b0e8a62afe16ed15ad820088d6272a
mode 100644,000000..100644
--- /dev/null
@@@ -1,159 -1,0 +1,165 @@@
-   --> $DIR/default_numeric_fallback_i32.rs:43:21
 +error: default numeric fallback might occur
 +  --> $DIR/default_numeric_fallback_i32.rs:21: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:22: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:22: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:22: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:23: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:23: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:23: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:23: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:24:23
 +   |
 +LL |         let x = match 1 {
 +   |                       ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
 +  --> $DIR/default_numeric_fallback_i32.rs:25:13
 +   |
 +LL |             1 => 1,
 +   |             ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
 +  --> $DIR/default_numeric_fallback_i32.rs:25:18
 +   |
 +LL |             1 => 1,
 +   |                  ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
 +  --> $DIR/default_numeric_fallback_i32.rs:26:18
 +   |
 +LL |             _ => 2,
 +   |                  ^ help: consider adding suffix: `2_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:51:21
++  --> $DIR/default_numeric_fallback_i32.rs:45:21
 +   |
 +LL |             let y = 1;
 +   |                     ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:57:21
++  --> $DIR/default_numeric_fallback_i32.rs:53:21
 +   |
 +LL |             let y = 1;
 +   |                     ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:69:9
++  --> $DIR/default_numeric_fallback_i32.rs:59:21
 +   |
 +LL |             let y = 1;
 +   |                     ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:75:27
++  --> $DIR/default_numeric_fallback_i32.rs:67:21
++   |
++LL |             let y = 1;
++   |                     ^ help: consider adding suffix: `1_i32`
++
++error: default numeric fallback might occur
++  --> $DIR/default_numeric_fallback_i32.rs:79:9
 +   |
 +LL |         1
 +   |         ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:79:29
++  --> $DIR/default_numeric_fallback_i32.rs:85:27
 +   |
 +LL |         let f = || -> _ { 1 };
 +   |                           ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:93:21
++  --> $DIR/default_numeric_fallback_i32.rs:89:29
 +   |
 +LL |         let f = || -> i32 { 1 };
 +   |                             ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:96:32
++  --> $DIR/default_numeric_fallback_i32.rs:103:21
 +   |
 +LL |         generic_arg(1);
 +   |                     ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:114:28
++  --> $DIR/default_numeric_fallback_i32.rs:106: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:117:36
++  --> $DIR/default_numeric_fallback_i32.rs:124:28
 +   |
 +LL |         GenericStruct { x: 1 };
 +   |                            ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:135:24
++  --> $DIR/default_numeric_fallback_i32.rs:127:36
 +   |
 +LL |         let _ = GenericStruct { x: 1 };
 +   |                                    ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:155:23
++  --> $DIR/default_numeric_fallback_i32.rs:145:24
 +   |
 +LL |         GenericEnum::X(1);
 +   |                        ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback_i32.rs:162:21
++  --> $DIR/default_numeric_fallback_i32.rs:165:23
 +   |
 +LL |         s.generic_arg(1);
 +   |                       ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
- error: aborting due to 25 previous errors
++  --> $DIR/default_numeric_fallback_i32.rs:172: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 26 previous errors
 +
index 7e18d70bae4007313c76d384005d255fd921a2d9,0000000000000000000000000000000000000000..3bac738acd65b90412004d73e8719ed885b8c368
mode 100644,000000..100644
--- /dev/null
@@@ -1,14 -1,0 +1,31 @@@
 +// run-rustfix
 +
++#![feature(custom_inner_attributes)]
++#![allow(unused)]
++
 +struct MyTypeNonDebug;
 +
 +#[derive(Debug)]
 +struct MyTypeDebug;
 +
 +fn main() {
 +    let test_debug: Result<MyTypeDebug, u32> = Ok(MyTypeDebug);
 +    test_debug.expect_err("Testing debug type");
 +
 +    let test_non_debug: Result<MyTypeNonDebug, u32> = Ok(MyTypeNonDebug);
 +    test_non_debug.err().expect("Testing non debug type");
 +}
++
++fn msrv_1_16() {
++    #![clippy::msrv = "1.16"]
++
++    let x: Result<u32, &str> = Ok(16);
++    x.err().expect("16");
++}
++
++fn msrv_1_17() {
++    #![clippy::msrv = "1.17"]
++
++    let x: Result<u32, &str> = Ok(17);
++    x.expect_err("17");
++}
index bf8c3c9fb8c98f680adfaeb6a9e9f2fa30fc437a,0000000000000000000000000000000000000000..6e7c47d9ad3cf09f49736c3a092577a5d2aed96e
mode 100644,000000..100644
--- /dev/null
@@@ -1,14 -1,0 +1,31 @@@
 +// run-rustfix
 +
++#![feature(custom_inner_attributes)]
++#![allow(unused)]
++
 +struct MyTypeNonDebug;
 +
 +#[derive(Debug)]
 +struct MyTypeDebug;
 +
 +fn main() {
 +    let test_debug: Result<MyTypeDebug, u32> = Ok(MyTypeDebug);
 +    test_debug.err().expect("Testing debug type");
 +
 +    let test_non_debug: Result<MyTypeNonDebug, u32> = Ok(MyTypeNonDebug);
 +    test_non_debug.err().expect("Testing non debug type");
 +}
++
++fn msrv_1_16() {
++    #![clippy::msrv = "1.16"]
++
++    let x: Result<u32, &str> = Ok(16);
++    x.err().expect("16");
++}
++
++fn msrv_1_17() {
++    #![clippy::msrv = "1.17"]
++
++    let x: Result<u32, &str> = Ok(17);
++    x.err().expect("17");
++}
index ffd97e00a5c09fa21dc16efa4ce028df4704e521,0000000000000000000000000000000000000000..91a6cf8de65fcd190de086f704fb7d534f374fa3
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,16 @@@
-   --> $DIR/err_expect.rs:10:16
 +error: called `.err().expect()` on a `Result` value
- error: aborting due to previous error
++  --> $DIR/err_expect.rs:13:16
 +   |
 +LL |     test_debug.err().expect("Testing debug type");
 +   |                ^^^^^^^^^^^^ help: try: `expect_err`
 +   |
 +   = note: `-D clippy::err-expect` implied by `-D warnings`
 +
++error: called `.err().expect()` on a `Result` value
++  --> $DIR/err_expect.rs:30:7
++   |
++LL |     x.err().expect("17");
++   |       ^^^^^^^^^^^^ help: try: `expect_err`
++
++error: aborting due to 2 previous errors
 +
index c3992d7e92cf37c36d0c5568aa3a462bfcd2278b,0000000000000000000000000000000000000000..41828ddd7acde22e0d7ad9e98a7df008a05b5d7f
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,26 @@@
 +// run-rustfix
 +
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::all, clippy::pedantic)]
++#![allow(unused)]
 +
 +fn main() {
 +    let a = ["1", "lol", "3", "NaN", "5"];
 +
 +    let element: Option<i32> = a.iter().find_map(|s| s.parse().ok());
 +    assert_eq!(element, Some(1));
 +}
++
++fn msrv_1_29() {
++    #![clippy::msrv = "1.29"]
++
++    let a = ["1", "lol", "3", "NaN", "5"];
++    let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
++}
++
++fn msrv_1_30() {
++    #![clippy::msrv = "1.30"]
++
++    let a = ["1", "lol", "3", "NaN", "5"];
++    let _: Option<i32> = a.iter().find_map(|s| s.parse().ok());
++}
index 447219a96839181630629af538bd834624f0bb51,0000000000000000000000000000000000000000..be492a81b45ec3e638c0087c3108705e55ffd9cb
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,26 @@@
 +// run-rustfix
 +
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::all, clippy::pedantic)]
++#![allow(unused)]
 +
 +fn main() {
 +    let a = ["1", "lol", "3", "NaN", "5"];
 +
 +    let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
 +    assert_eq!(element, Some(1));
 +}
++
++fn msrv_1_29() {
++    #![clippy::msrv = "1.29"]
++
++    let a = ["1", "lol", "3", "NaN", "5"];
++    let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
++}
++
++fn msrv_1_30() {
++    #![clippy::msrv = "1.30"]
++
++    let a = ["1", "lol", "3", "NaN", "5"];
++    let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
++}
index 3bb062ffd7a32ab6f20b66770a58963c4ec1e564,0000000000000000000000000000000000000000..e789efeabd5503a16eec2e7d531200f7a8875e1d
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,16 @@@
-   --> $DIR/filter_map_next_fixable.rs:8:32
 +error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead
- error: aborting due to previous error
++  --> $DIR/filter_map_next_fixable.rs:10:32
 +   |
 +LL |     let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
 +   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())`
 +   |
 +   = note: `-D clippy::filter-map-next` implied by `-D warnings`
 +
++error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead
++  --> $DIR/filter_map_next_fixable.rs:25:26
++   |
++LL |     let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())`
++
++error: aborting due to 2 previous errors
 +
index 24cf0847dd5884fbb1cbf3b99b5550a3ef99d05c,0000000000000000000000000000000000000000..825e122be5a5f3d97b628c38833d1340235a63d4
mode 100644,000000..100644
--- /dev/null
@@@ -1,164 -1,0 +1,167 @@@
 +// run-rustfix
 +#![warn(clippy::to_string_in_format_args)]
 +#![allow(unused)]
 +#![allow(
 +    clippy::assertions_on_constants,
++    clippy::double_parens,
 +    clippy::eq_op,
 +    clippy::print_literal,
 +    clippy::uninlined_format_args
 +)]
 +
 +use std::io::{stdout, Write};
 +use std::ops::Deref;
 +use std::panic::Location;
 +
 +struct Somewhere;
 +
 +impl ToString for Somewhere {
 +    fn to_string(&self) -> String {
 +        String::from("somewhere")
 +    }
 +}
 +
 +struct X(u32);
 +
 +impl Deref for X {
 +    type Target = u32;
 +
 +    fn deref(&self) -> &u32 {
 +        &self.0
 +    }
 +}
 +
 +struct Y<'a>(&'a X);
 +
 +impl<'a> Deref for Y<'a> {
 +    type Target = &'a X;
 +
 +    fn deref(&self) -> &Self::Target {
 +        &self.0
 +    }
 +}
 +
 +struct Z(u32);
 +
 +impl Deref for Z {
 +    type Target = u32;
 +
 +    fn deref(&self) -> &u32 {
 +        &self.0
 +    }
 +}
 +
 +impl std::fmt::Display for Z {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 +        write!(f, "Z")
 +    }
 +}
 +
 +macro_rules! my_macro {
 +    () => {
 +        // here be dragons, do not enter (or lint)
 +        println!("error: something failed at {}", Location::caller().to_string());
 +    };
 +}
 +
 +macro_rules! my_other_macro {
 +    () => {
 +        Location::caller().to_string()
 +    };
 +}
 +
 +fn main() {
 +    let x = &X(1);
 +    let x_ref = &x;
 +
 +    let _ = format!("error: something failed at {}", Location::caller());
 +    let _ = write!(
 +        stdout(),
 +        "error: something failed at {}",
 +        Location::caller()
 +    );
 +    let _ = writeln!(
 +        stdout(),
 +        "error: something failed at {}",
 +        Location::caller()
 +    );
 +    print!("error: something failed at {}", Location::caller());
 +    println!("error: something failed at {}", Location::caller());
 +    eprint!("error: something failed at {}", Location::caller());
 +    eprintln!("error: something failed at {}", Location::caller());
 +    let _ = format_args!("error: something failed at {}", Location::caller());
 +    assert!(true, "error: something failed at {}", Location::caller());
 +    assert_eq!(0, 0, "error: something failed at {}", Location::caller());
 +    assert_ne!(0, 0, "error: something failed at {}", Location::caller());
 +    panic!("error: something failed at {}", Location::caller());
 +    println!("{}", *X(1));
 +    println!("{}", ***Y(&X(1)));
 +    println!("{}", Z(1));
 +    println!("{}", **x);
 +    println!("{}", ***x_ref);
 +    // https://github.com/rust-lang/rust-clippy/issues/7903
 +    println!("{foo}{bar}", foo = "foo", bar = "bar");
 +    println!("{foo}{bar}", foo = "foo", bar = "bar");
 +    println!("{foo}{bar}", bar = "bar", foo = "foo");
 +    println!("{foo}{bar}", bar = "bar", foo = "foo");
 +
 +    // negative tests
 +    println!("error: something failed at {}", Somewhere.to_string());
 +    // The next two tests are negative because caching the string might be faster than calling `<X as
 +    // Display>::fmt` twice.
 +    println!("{} and again {0}", x.to_string());
 +    println!("{foo}{foo}", foo = "foo".to_string());
 +    my_macro!();
 +    println!("error: something failed at {}", my_other_macro!());
 +    // https://github.com/rust-lang/rust-clippy/issues/7903
 +    println!("{foo}{foo:?}", foo = "foo".to_string());
++    print!("{}", (Location::caller()));
++    print!("{}", ((Location::caller())));
 +}
 +
 +fn issue8643(vendor_id: usize, product_id: usize, name: &str) {
 +    println!(
 +        "{:<9}  {:<10}  {}",
 +        format!("0x{:x}", vendor_id),
 +        format!("0x{:x}", product_id),
 +        name
 +    );
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8855
 +mod issue_8855 {
 +    #![allow(dead_code)]
 +
 +    struct A {}
 +
 +    impl std::fmt::Display for A {
 +        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 +            write!(f, "test")
 +        }
 +    }
 +
 +    fn main() {
 +        let a = A {};
 +        let b = A {};
 +
 +        let x = format!("{} {}", a, b);
 +        dbg!(x);
 +
 +        let x = format!("{:>6} {:>6}", a, b.to_string());
 +        dbg!(x);
 +    }
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/9256
 +mod issue_9256 {
 +    #![allow(dead_code)]
 +
 +    fn print_substring(original: &str) {
 +        assert!(original.len() > 10);
 +        println!("{}", &original[..10]);
 +    }
 +
 +    fn main() {
 +        print_substring("Hello, world!");
 +    }
 +}
index 753babf0afdc74365c1dba75ad77d56b090b6323,0000000000000000000000000000000000000000..a41e53389e52a7b9f84a37f914217c4baa07d6c5
mode 100644,000000..100644
--- /dev/null
@@@ -1,164 -1,0 +1,167 @@@
 +// run-rustfix
 +#![warn(clippy::to_string_in_format_args)]
 +#![allow(unused)]
 +#![allow(
 +    clippy::assertions_on_constants,
++    clippy::double_parens,
 +    clippy::eq_op,
 +    clippy::print_literal,
 +    clippy::uninlined_format_args
 +)]
 +
 +use std::io::{stdout, Write};
 +use std::ops::Deref;
 +use std::panic::Location;
 +
 +struct Somewhere;
 +
 +impl ToString for Somewhere {
 +    fn to_string(&self) -> String {
 +        String::from("somewhere")
 +    }
 +}
 +
 +struct X(u32);
 +
 +impl Deref for X {
 +    type Target = u32;
 +
 +    fn deref(&self) -> &u32 {
 +        &self.0
 +    }
 +}
 +
 +struct Y<'a>(&'a X);
 +
 +impl<'a> Deref for Y<'a> {
 +    type Target = &'a X;
 +
 +    fn deref(&self) -> &Self::Target {
 +        &self.0
 +    }
 +}
 +
 +struct Z(u32);
 +
 +impl Deref for Z {
 +    type Target = u32;
 +
 +    fn deref(&self) -> &u32 {
 +        &self.0
 +    }
 +}
 +
 +impl std::fmt::Display for Z {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 +        write!(f, "Z")
 +    }
 +}
 +
 +macro_rules! my_macro {
 +    () => {
 +        // here be dragons, do not enter (or lint)
 +        println!("error: something failed at {}", Location::caller().to_string());
 +    };
 +}
 +
 +macro_rules! my_other_macro {
 +    () => {
 +        Location::caller().to_string()
 +    };
 +}
 +
 +fn main() {
 +    let x = &X(1);
 +    let x_ref = &x;
 +
 +    let _ = format!("error: something failed at {}", Location::caller().to_string());
 +    let _ = write!(
 +        stdout(),
 +        "error: something failed at {}",
 +        Location::caller().to_string()
 +    );
 +    let _ = writeln!(
 +        stdout(),
 +        "error: something failed at {}",
 +        Location::caller().to_string()
 +    );
 +    print!("error: something failed at {}", Location::caller().to_string());
 +    println!("error: something failed at {}", Location::caller().to_string());
 +    eprint!("error: something failed at {}", Location::caller().to_string());
 +    eprintln!("error: something failed at {}", Location::caller().to_string());
 +    let _ = format_args!("error: something failed at {}", Location::caller().to_string());
 +    assert!(true, "error: something failed at {}", Location::caller().to_string());
 +    assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string());
 +    assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string());
 +    panic!("error: something failed at {}", Location::caller().to_string());
 +    println!("{}", X(1).to_string());
 +    println!("{}", Y(&X(1)).to_string());
 +    println!("{}", Z(1).to_string());
 +    println!("{}", x.to_string());
 +    println!("{}", x_ref.to_string());
 +    // https://github.com/rust-lang/rust-clippy/issues/7903
 +    println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar");
 +    println!("{foo}{bar}", foo = "foo", bar = "bar".to_string());
 +    println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo");
 +    println!("{foo}{bar}", bar = "bar", foo = "foo".to_string());
 +
 +    // negative tests
 +    println!("error: something failed at {}", Somewhere.to_string());
 +    // The next two tests are negative because caching the string might be faster than calling `<X as
 +    // Display>::fmt` twice.
 +    println!("{} and again {0}", x.to_string());
 +    println!("{foo}{foo}", foo = "foo".to_string());
 +    my_macro!();
 +    println!("error: something failed at {}", my_other_macro!());
 +    // https://github.com/rust-lang/rust-clippy/issues/7903
 +    println!("{foo}{foo:?}", foo = "foo".to_string());
++    print!("{}", (Location::caller().to_string()));
++    print!("{}", ((Location::caller()).to_string()));
 +}
 +
 +fn issue8643(vendor_id: usize, product_id: usize, name: &str) {
 +    println!(
 +        "{:<9}  {:<10}  {}",
 +        format!("0x{:x}", vendor_id),
 +        format!("0x{:x}", product_id),
 +        name
 +    );
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8855
 +mod issue_8855 {
 +    #![allow(dead_code)]
 +
 +    struct A {}
 +
 +    impl std::fmt::Display for A {
 +        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 +            write!(f, "test")
 +        }
 +    }
 +
 +    fn main() {
 +        let a = A {};
 +        let b = A {};
 +
 +        let x = format!("{} {}", a, b.to_string());
 +        dbg!(x);
 +
 +        let x = format!("{:>6} {:>6}", a, b.to_string());
 +        dbg!(x);
 +    }
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/9256
 +mod issue_9256 {
 +    #![allow(dead_code)]
 +
 +    fn print_substring(original: &str) {
 +        assert!(original.len() > 10);
 +        println!("{}", original[..10].to_string());
 +    }
 +
 +    fn main() {
 +        print_substring("Hello, world!");
 +    }
 +}
index 68b0bb9e089e1b83e50f1ce59fb0d279111c752f,0000000000000000000000000000000000000000..f1832b970198a42cac9fa311b2ae00810818723d
mode 100644,000000..100644
--- /dev/null
@@@ -1,142 -1,0 +1,154 @@@
-   --> $DIR/format_args.rs:76:72
 +error: `to_string` applied to a type that implements `Display` in `format!` args
-   --> $DIR/format_args.rs:80:27
++  --> $DIR/format_args.rs:77:72
 +   |
 +LL |     let _ = format!("error: something failed at {}", Location::caller().to_string());
 +   |                                                                        ^^^^^^^^^^^^ help: remove this
 +   |
 +   = note: `-D clippy::to-string-in-format-args` implied by `-D warnings`
 +
 +error: `to_string` applied to a type that implements `Display` in `write!` args
-   --> $DIR/format_args.rs:85:27
++  --> $DIR/format_args.rs:81:27
 +   |
 +LL |         Location::caller().to_string()
 +   |                           ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `writeln!` args
-   --> $DIR/format_args.rs:87:63
++  --> $DIR/format_args.rs:86:27
 +   |
 +LL |         Location::caller().to_string()
 +   |                           ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `print!` args
-   --> $DIR/format_args.rs:88:65
++  --> $DIR/format_args.rs:88:63
 +   |
 +LL |     print!("error: something failed at {}", Location::caller().to_string());
 +   |                                                               ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `println!` args
-   --> $DIR/format_args.rs:89:64
++  --> $DIR/format_args.rs:89:65
 +   |
 +LL |     println!("error: something failed at {}", Location::caller().to_string());
 +   |                                                                 ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `eprint!` args
-   --> $DIR/format_args.rs:90:66
++  --> $DIR/format_args.rs:90:64
 +   |
 +LL |     eprint!("error: something failed at {}", Location::caller().to_string());
 +   |                                                                ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `eprintln!` args
-   --> $DIR/format_args.rs:91:77
++  --> $DIR/format_args.rs:91:66
 +   |
 +LL |     eprintln!("error: something failed at {}", Location::caller().to_string());
 +   |                                                                  ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `format_args!` args
-   --> $DIR/format_args.rs:92:70
++  --> $DIR/format_args.rs:92:77
 +   |
 +LL |     let _ = format_args!("error: something failed at {}", Location::caller().to_string());
 +   |                                                                             ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `assert!` args
-   --> $DIR/format_args.rs:93:73
++  --> $DIR/format_args.rs:93:70
 +   |
 +LL |     assert!(true, "error: something failed at {}", Location::caller().to_string());
 +   |                                                                      ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `assert_eq!` args
-   --> $DIR/format_args.rs:94:73
++  --> $DIR/format_args.rs:94:73
 +   |
 +LL |     assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string());
 +   |                                                                         ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `assert_ne!` args
-   --> $DIR/format_args.rs:95:63
++  --> $DIR/format_args.rs:95:73
 +   |
 +LL |     assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string());
 +   |                                                                         ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `panic!` args
-   --> $DIR/format_args.rs:96:20
++  --> $DIR/format_args.rs:96:63
 +   |
 +LL |     panic!("error: something failed at {}", Location::caller().to_string());
 +   |                                                               ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `println!` args
-   --> $DIR/format_args.rs:97:20
++  --> $DIR/format_args.rs:97:20
 +   |
 +LL |     println!("{}", X(1).to_string());
 +   |                    ^^^^^^^^^^^^^^^^ help: use this: `*X(1)`
 +
 +error: `to_string` applied to a type that implements `Display` in `println!` args
-   --> $DIR/format_args.rs:98:24
++  --> $DIR/format_args.rs:98:20
 +   |
 +LL |     println!("{}", Y(&X(1)).to_string());
 +   |                    ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))`
 +
 +error: `to_string` applied to a type that implements `Display` in `println!` args
-   --> $DIR/format_args.rs:99:20
++  --> $DIR/format_args.rs:99:24
 +   |
 +LL |     println!("{}", Z(1).to_string());
 +   |                        ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `println!` args
-   --> $DIR/format_args.rs:100:20
++  --> $DIR/format_args.rs:100:20
 +   |
 +LL |     println!("{}", x.to_string());
 +   |                    ^^^^^^^^^^^^^ help: use this: `**x`
 +
 +error: `to_string` applied to a type that implements `Display` in `println!` args
-   --> $DIR/format_args.rs:102:39
++  --> $DIR/format_args.rs:101:20
 +   |
 +LL |     println!("{}", x_ref.to_string());
 +   |                    ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref`
 +
 +error: `to_string` applied to a type that implements `Display` in `println!` args
-   --> $DIR/format_args.rs:103:52
++  --> $DIR/format_args.rs:103:39
 +   |
 +LL |     println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar");
 +   |                                       ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `println!` args
-   --> $DIR/format_args.rs:104:39
++  --> $DIR/format_args.rs:104:52
 +   |
 +LL |     println!("{foo}{bar}", foo = "foo", bar = "bar".to_string());
 +   |                                                    ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `println!` args
-   --> $DIR/format_args.rs:105:52
++  --> $DIR/format_args.rs:105:39
 +   |
 +LL |     println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo");
 +   |                                       ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `println!` args
-   --> $DIR/format_args.rs:144:38
++  --> $DIR/format_args.rs:106:52
 +   |
 +LL |     println!("{foo}{bar}", bar = "bar", foo = "foo".to_string());
 +   |                                                    ^^^^^^^^^^^^ help: remove this
 +
++error: `to_string` applied to a type that implements `Display` in `print!` args
++  --> $DIR/format_args.rs:118:37
++   |
++LL |     print!("{}", (Location::caller().to_string()));
++   |                                     ^^^^^^^^^^^^ help: remove this
++
++error: `to_string` applied to a type that implements `Display` in `print!` args
++  --> $DIR/format_args.rs:119:39
++   |
++LL |     print!("{}", ((Location::caller()).to_string()));
++   |                                       ^^^^^^^^^^^^ help: remove this
++
 +error: `to_string` applied to a type that implements `Display` in `format!` args
-   --> $DIR/format_args.rs:158:24
++  --> $DIR/format_args.rs:147:38
 +   |
 +LL |         let x = format!("{} {}", a, b.to_string());
 +   |                                      ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `println!` args
- error: aborting due to 23 previous errors
++  --> $DIR/format_args.rs:161:24
 +   |
 +LL |         println!("{}", original[..10].to_string());
 +   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use this: `&original[..10]`
 +
++error: aborting due to 25 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1cf49ca45f494d1eca6d0a02785ca04781747755
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,87 @@@
++// run-rustfix
++
++#![feature(custom_inner_attributes)]
++#![warn(clippy::from_over_into)]
++#![allow(unused)]
++
++// this should throw an error
++struct StringWrapper(String);
++
++impl From<String> for StringWrapper {
++    fn from(val: String) -> Self {
++        StringWrapper(val)
++    }
++}
++
++struct SelfType(String);
++
++impl From<String> for SelfType {
++    fn from(val: String) -> Self {
++        SelfType(String::new())
++    }
++}
++
++#[derive(Default)]
++struct X;
++
++impl X {
++    const FOO: &'static str = "a";
++}
++
++struct SelfKeywords;
++
++impl From<X> for SelfKeywords {
++    fn from(val: X) -> Self {
++        let _ = X::default();
++        let _ = X::FOO;
++        let _: X = val;
++
++        SelfKeywords
++    }
++}
++
++struct ExplicitPaths(bool);
++
++impl core::convert::From<crate::ExplicitPaths> for bool {
++    fn from(mut val: crate::ExplicitPaths) -> Self {
++        let in_closure = || val.0;
++
++        val.0 = false;
++        val.0
++    }
++}
++
++// this is fine
++struct A(String);
++
++impl From<String> for A {
++    fn from(s: String) -> A {
++        A(s)
++    }
++}
++
++fn msrv_1_40() {
++    #![clippy::msrv = "1.40"]
++
++    struct FromOverInto<T>(Vec<T>);
++
++    impl<T> Into<FromOverInto<T>> for Vec<T> {
++        fn into(self) -> FromOverInto<T> {
++            FromOverInto(self)
++        }
++    }
++}
++
++fn msrv_1_41() {
++    #![clippy::msrv = "1.41"]
++
++    struct FromOverInto<T>(Vec<T>);
++
++    impl<T> From<Vec<T>> for FromOverInto<T> {
++        fn from(val: Vec<T>) -> Self {
++            FromOverInto(val)
++        }
++    }
++}
++
++fn main() {}
index 292d0924fb17a4ca1b32bf0a39fa6a81fdc68e82,0000000000000000000000000000000000000000..d30f3c3fc92567ba78b18523e3b6aa2b891d5de8
mode 100644,000000..100644
--- /dev/null
@@@ -1,21 -1,0 +1,87 @@@
++// run-rustfix
++
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::from_over_into)]
++#![allow(unused)]
 +
 +// this should throw an error
 +struct StringWrapper(String);
 +
 +impl Into<StringWrapper> for String {
 +    fn into(self) -> StringWrapper {
 +        StringWrapper(self)
 +    }
 +}
 +
++struct SelfType(String);
++
++impl Into<SelfType> for String {
++    fn into(self) -> SelfType {
++        SelfType(Self::new())
++    }
++}
++
++#[derive(Default)]
++struct X;
++
++impl X {
++    const FOO: &'static str = "a";
++}
++
++struct SelfKeywords;
++
++impl Into<SelfKeywords> for X {
++    fn into(self) -> SelfKeywords {
++        let _ = Self::default();
++        let _ = Self::FOO;
++        let _: Self = self;
++
++        SelfKeywords
++    }
++}
++
++struct ExplicitPaths(bool);
++
++impl core::convert::Into<bool> for crate::ExplicitPaths {
++    fn into(mut self) -> bool {
++        let in_closure = || self.0;
++
++        self.0 = false;
++        self.0
++    }
++}
++
 +// this is fine
 +struct A(String);
 +
 +impl From<String> for A {
 +    fn from(s: String) -> A {
 +        A(s)
 +    }
 +}
 +
++fn msrv_1_40() {
++    #![clippy::msrv = "1.40"]
++
++    struct FromOverInto<T>(Vec<T>);
++
++    impl<T> Into<FromOverInto<T>> for Vec<T> {
++        fn into(self) -> FromOverInto<T> {
++            FromOverInto(self)
++        }
++    }
++}
++
++fn msrv_1_41() {
++    #![clippy::msrv = "1.41"]
++
++    struct FromOverInto<T>(Vec<T>);
++
++    impl<T> Into<FromOverInto<T>> for Vec<T> {
++        fn into(self) -> FromOverInto<T> {
++            FromOverInto(self)
++        }
++    }
++}
++
 +fn main() {}
index 469adadd2196dd6721abecf7fece4fc2f04e4e81,0000000000000000000000000000000000000000..9c2a7c04c3646a84252479dea7e42a15319d9bb8
mode 100644,000000..100644
--- /dev/null
@@@ -1,11 -1,0 +1,75 @@@
-   --> $DIR/from_over_into.rs:6:1
 +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
-    = help: consider to implement `From<std::string::String>` instead
++  --> $DIR/from_over_into.rs:10:1
 +   |
 +LL | impl Into<StringWrapper> for String {
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
- error: aborting due to previous error
 +   = note: `-D clippy::from-over-into` implied by `-D warnings`
++help: replace the `Into` implentation with `From<std::string::String>`
++   |
++LL ~ impl From<String> for StringWrapper {
++LL ~     fn from(val: String) -> Self {
++LL ~         StringWrapper(val)
++   |
++
++error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
++  --> $DIR/from_over_into.rs:18:1
++   |
++LL | impl Into<SelfType> for String {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: replace the `Into` implentation with `From<std::string::String>`
++   |
++LL ~ impl From<String> for SelfType {
++LL ~     fn from(val: String) -> Self {
++LL ~         SelfType(String::new())
++   |
++
++error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
++  --> $DIR/from_over_into.rs:33:1
++   |
++LL | impl Into<SelfKeywords> for X {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: replace the `Into` implentation with `From<X>`
++   |
++LL ~ impl From<X> for SelfKeywords {
++LL ~     fn from(val: X) -> Self {
++LL ~         let _ = X::default();
++LL ~         let _ = X::FOO;
++LL ~         let _: X = val;
++   |
++
++error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
++  --> $DIR/from_over_into.rs:45:1
++   |
++LL | impl core::convert::Into<bool> for crate::ExplicitPaths {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: `impl From<Local> for Foreign` is allowed by the orphan rules, for more information see
++           https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence
++help: replace the `Into` implentation with `From<ExplicitPaths>`
++   |
++LL ~ impl core::convert::From<crate::ExplicitPaths> for bool {
++LL ~     fn from(mut val: crate::ExplicitPaths) -> Self {
++LL ~         let in_closure = || val.0;
++LL | 
++LL ~         val.0 = false;
++LL ~         val.0
++   |
++
++error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
++  --> $DIR/from_over_into.rs:80:5
++   |
++LL |     impl<T> Into<FromOverInto<T>> for Vec<T> {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: replace the `Into` implentation with `From<std::vec::Vec<T>>`
++   |
++LL ~     impl<T> From<Vec<T>> for FromOverInto<T> {
++LL ~         fn from(val: Vec<T>) -> Self {
++LL ~             FromOverInto(val)
++   |
 +
++error: aborting due to 5 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3b280b7488ae7c8b1bc79b7215407c9cc8e35492
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++#![warn(clippy::from_over_into)]
++
++struct InMacro(String);
++
++macro_rules! in_macro {
++    ($e:ident) => {
++        $e
++    };
++}
++
++impl Into<InMacro> for String {
++    fn into(self) -> InMacro {
++        InMacro(in_macro!(self))
++    }
++}
++
++struct WeirdUpperSelf;
++
++impl Into<WeirdUpperSelf> for &'static [u8] {
++    fn into(self) -> WeirdUpperSelf {
++        let _ = Self::default();
++        WeirdUpperSelf
++    }
++}
++
++struct ContainsVal;
++
++impl Into<u8> for ContainsVal {
++    fn into(self) -> u8 {
++        let val = 1;
++        val + 1
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6f6ce351921be1138cff9de30e36625f2c091ef4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
++  --> $DIR/from_over_into_unfixable.rs:11:1
++   |
++LL | impl Into<InMacro> for String {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: replace the `Into` implentation with `From<std::string::String>`
++   = note: `-D clippy::from-over-into` implied by `-D warnings`
++
++error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
++  --> $DIR/from_over_into_unfixable.rs:19:1
++   |
++LL | impl Into<WeirdUpperSelf> for &'static [u8] {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: replace the `Into` implentation with `From<&'static [u8]>`
++
++error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
++  --> $DIR/from_over_into_unfixable.rs:28:1
++   |
++LL | impl Into<u8> for ContainsVal {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: `impl From<Local> for Foreign` is allowed by the orphan rules, for more information see
++           https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence
++   = help: replace the `Into` implentation with `From<ContainsVal>`
++
++error: aborting due to 3 previous errors
++
index e6f57e9267eac93091e423180bbaef8f9d97315b,0000000000000000000000000000000000000000..93df81b1a7ff0355fa99bb2cf9bdef5d6a11c83d
mode 100644,000000..100644
--- /dev/null
@@@ -1,168 -1,0 +1,218 @@@
 +// run-rustfix
 +#![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)]
 +#![warn(clippy::implicit_saturating_sub)]
 +
++use std::cmp::PartialEq;
++use std::ops::SubAssign;
++// Mock type
++struct Mock;
++
++impl PartialEq<u32> for Mock {
++    fn eq(&self, _: &u32) -> bool {
++        true
++    }
++}
++
++impl SubAssign<u32> for Mock {
++    fn sub_assign(&mut self, _: u32) {}
++}
++
 +fn main() {
 +    // Tests for unsigned integers
 +
 +    let end_8: u8 = 10;
 +    let start_8: u8 = 5;
 +    let mut u_8: u8 = end_8 - start_8;
 +
 +    // Lint
 +    u_8 = u_8.saturating_sub(1);
 +
 +    match end_8 {
 +        10 => {
 +            // Lint
 +            u_8 = u_8.saturating_sub(1);
 +        },
 +        11 => u_8 += 1,
 +        _ => u_8 = 0,
 +    }
 +
 +    let end_16: u16 = 40;
 +    let start_16: u16 = 35;
 +
 +    let mut u_16: u16 = end_16 - start_16;
 +
 +    // Lint
 +    u_16 = u_16.saturating_sub(1);
 +
 +    let mut end_32: u32 = 7010;
 +    let mut start_32: u32 = 7000;
 +
 +    let mut u_32: u32 = end_32 - start_32;
 +
 +    // Lint
 +    u_32 = u_32.saturating_sub(1);
 +
 +    // No Lint
 +    if u_32 > 0 {
 +        u_16 += 1;
 +    }
 +
 +    // No Lint
 +    if u_32 != 0 {
 +        end_32 -= 1;
 +        start_32 += 1;
 +    }
 +
 +    let mut end_64: u64 = 75001;
 +    let mut start_64: u64 = 75000;
 +
 +    let mut u_64: u64 = end_64 - start_64;
 +
 +    // Lint
 +    u_64 = u_64.saturating_sub(1);
 +
 +    // Lint
 +    u_64 = u_64.saturating_sub(1);
 +
 +    // Lint
 +    u_64 = u_64.saturating_sub(1);
 +
 +    // No Lint
 +    if u_64 >= 1 {
 +        u_64 -= 1;
 +    }
 +
 +    // No Lint
 +    if u_64 > 0 {
 +        end_64 -= 1;
 +    }
 +
 +    // Tests for usize
 +    let end_usize: usize = 8054;
 +    let start_usize: usize = 8050;
 +
 +    let mut u_usize: usize = end_usize - start_usize;
 +
 +    // Lint
 +    u_usize = u_usize.saturating_sub(1);
 +
 +    // Tests for signed integers
 +
 +    let endi_8: i8 = 10;
 +    let starti_8: i8 = 50;
 +
 +    let mut i_8: i8 = endi_8 - starti_8;
 +
 +    // Lint
 +    i_8 = i_8.saturating_sub(1);
 +
 +    // Lint
 +    i_8 = i_8.saturating_sub(1);
 +
 +    // Lint
 +    i_8 = i_8.saturating_sub(1);
 +
 +    // Lint
 +    i_8 = i_8.saturating_sub(1);
 +
 +    let endi_16: i16 = 45;
 +    let starti_16: i16 = 44;
 +
 +    let mut i_16: i16 = endi_16 - starti_16;
 +
 +    // Lint
 +    i_16 = i_16.saturating_sub(1);
 +
 +    // Lint
 +    i_16 = i_16.saturating_sub(1);
 +
 +    // Lint
 +    i_16 = i_16.saturating_sub(1);
 +
 +    // Lint
 +    i_16 = i_16.saturating_sub(1);
 +
 +    let endi_32: i32 = 45;
 +    let starti_32: i32 = 44;
 +
 +    let mut i_32: i32 = endi_32 - starti_32;
 +
 +    // Lint
 +    i_32 = i_32.saturating_sub(1);
 +
 +    // Lint
 +    i_32 = i_32.saturating_sub(1);
 +
 +    // Lint
 +    i_32 = i_32.saturating_sub(1);
 +
 +    // Lint
 +    i_32 = i_32.saturating_sub(1);
 +
 +    let endi_64: i64 = 45;
 +    let starti_64: i64 = 44;
 +
 +    let mut i_64: i64 = endi_64 - starti_64;
 +
 +    // Lint
 +    i_64 = i_64.saturating_sub(1);
 +
 +    // Lint
 +    i_64 = i_64.saturating_sub(1);
 +
 +    // Lint
 +    i_64 = i_64.saturating_sub(1);
 +
 +    // No Lint
 +    if i_64 > 0 {
 +        i_64 -= 1;
 +    }
 +
 +    // No Lint
 +    if i_64 != 0 {
 +        i_64 -= 1;
 +    }
 +
 +    // issue #7831
 +    // No Lint
 +    if u_32 > 0 {
 +        u_32 -= 1;
 +    } else {
 +        println!("side effect");
 +    }
++
++    // Extended tests
++    let mut m = Mock;
++    let mut u_32 = 3000;
++    let a = 200;
++    let mut _b = 8;
++
++    if m != 0 {
++        m -= 1;
++    }
++
++    if a > 0 {
++        _b -= 1;
++    }
++
++    if 0 > a {
++        _b -= 1;
++    }
++
++    if u_32 > 0 {
++        u_32 -= 1;
++    } else {
++        println!("don't lint this");
++    }
++
++    if u_32 > 0 {
++        println!("don't lint this");
++        u_32 -= 1;
++    }
++
++    if u_32 > 42 {
++        println!("brace yourself!");
++    } else if u_32 > 0 {
++        u_32 -= 1;
++    }
 +}
index 8bb28d149c62895c210a5e4ba68082daf6ff9813,0000000000000000000000000000000000000000..8340bc8264d5884814037184d1b1cd41cf1cc12a
mode 100644,000000..100644
--- /dev/null
@@@ -1,214 -1,0 +1,264 @@@
 +// run-rustfix
 +#![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)]
 +#![warn(clippy::implicit_saturating_sub)]
 +
++use std::cmp::PartialEq;
++use std::ops::SubAssign;
++// Mock type
++struct Mock;
++
++impl PartialEq<u32> for Mock {
++    fn eq(&self, _: &u32) -> bool {
++        true
++    }
++}
++
++impl SubAssign<u32> for Mock {
++    fn sub_assign(&mut self, _: u32) {}
++}
++
 +fn main() {
 +    // Tests for unsigned integers
 +
 +    let end_8: u8 = 10;
 +    let start_8: u8 = 5;
 +    let mut u_8: u8 = end_8 - start_8;
 +
 +    // Lint
 +    if u_8 > 0 {
 +        u_8 = u_8 - 1;
 +    }
 +
 +    match end_8 {
 +        10 => {
 +            // Lint
 +            if u_8 > 0 {
 +                u_8 -= 1;
 +            }
 +        },
 +        11 => u_8 += 1,
 +        _ => u_8 = 0,
 +    }
 +
 +    let end_16: u16 = 40;
 +    let start_16: u16 = 35;
 +
 +    let mut u_16: u16 = end_16 - start_16;
 +
 +    // Lint
 +    if u_16 > 0 {
 +        u_16 -= 1;
 +    }
 +
 +    let mut end_32: u32 = 7010;
 +    let mut start_32: u32 = 7000;
 +
 +    let mut u_32: u32 = end_32 - start_32;
 +
 +    // Lint
 +    if u_32 != 0 {
 +        u_32 -= 1;
 +    }
 +
 +    // No Lint
 +    if u_32 > 0 {
 +        u_16 += 1;
 +    }
 +
 +    // No Lint
 +    if u_32 != 0 {
 +        end_32 -= 1;
 +        start_32 += 1;
 +    }
 +
 +    let mut end_64: u64 = 75001;
 +    let mut start_64: u64 = 75000;
 +
 +    let mut u_64: u64 = end_64 - start_64;
 +
 +    // Lint
 +    if u_64 > 0 {
 +        u_64 -= 1;
 +    }
 +
 +    // Lint
 +    if 0 < u_64 {
 +        u_64 -= 1;
 +    }
 +
 +    // Lint
 +    if 0 != u_64 {
 +        u_64 -= 1;
 +    }
 +
 +    // No Lint
 +    if u_64 >= 1 {
 +        u_64 -= 1;
 +    }
 +
 +    // No Lint
 +    if u_64 > 0 {
 +        end_64 -= 1;
 +    }
 +
 +    // Tests for usize
 +    let end_usize: usize = 8054;
 +    let start_usize: usize = 8050;
 +
 +    let mut u_usize: usize = end_usize - start_usize;
 +
 +    // Lint
 +    if u_usize > 0 {
 +        u_usize -= 1;
 +    }
 +
 +    // Tests for signed integers
 +
 +    let endi_8: i8 = 10;
 +    let starti_8: i8 = 50;
 +
 +    let mut i_8: i8 = endi_8 - starti_8;
 +
 +    // Lint
 +    if i_8 > i8::MIN {
 +        i_8 -= 1;
 +    }
 +
 +    // Lint
 +    if i_8 > i8::MIN {
 +        i_8 -= 1;
 +    }
 +
 +    // Lint
 +    if i_8 != i8::MIN {
 +        i_8 -= 1;
 +    }
 +
 +    // Lint
 +    if i_8 != i8::MIN {
 +        i_8 -= 1;
 +    }
 +
 +    let endi_16: i16 = 45;
 +    let starti_16: i16 = 44;
 +
 +    let mut i_16: i16 = endi_16 - starti_16;
 +
 +    // Lint
 +    if i_16 > i16::MIN {
 +        i_16 -= 1;
 +    }
 +
 +    // Lint
 +    if i_16 > i16::MIN {
 +        i_16 -= 1;
 +    }
 +
 +    // Lint
 +    if i_16 != i16::MIN {
 +        i_16 -= 1;
 +    }
 +
 +    // Lint
 +    if i_16 != i16::MIN {
 +        i_16 -= 1;
 +    }
 +
 +    let endi_32: i32 = 45;
 +    let starti_32: i32 = 44;
 +
 +    let mut i_32: i32 = endi_32 - starti_32;
 +
 +    // Lint
 +    if i_32 > i32::MIN {
 +        i_32 -= 1;
 +    }
 +
 +    // Lint
 +    if i_32 > i32::MIN {
 +        i_32 -= 1;
 +    }
 +
 +    // Lint
 +    if i_32 != i32::MIN {
 +        i_32 -= 1;
 +    }
 +
 +    // Lint
 +    if i_32 != i32::MIN {
 +        i_32 -= 1;
 +    }
 +
 +    let endi_64: i64 = 45;
 +    let starti_64: i64 = 44;
 +
 +    let mut i_64: i64 = endi_64 - starti_64;
 +
 +    // Lint
 +    if i64::MIN < i_64 {
 +        i_64 -= 1;
 +    }
 +
 +    // Lint
 +    if i64::MIN != i_64 {
 +        i_64 -= 1;
 +    }
 +
 +    // Lint
 +    if i64::MIN < i_64 {
 +        i_64 -= 1;
 +    }
 +
 +    // No Lint
 +    if i_64 > 0 {
 +        i_64 -= 1;
 +    }
 +
 +    // No Lint
 +    if i_64 != 0 {
 +        i_64 -= 1;
 +    }
 +
 +    // issue #7831
 +    // No Lint
 +    if u_32 > 0 {
 +        u_32 -= 1;
 +    } else {
 +        println!("side effect");
 +    }
++
++    // Extended tests
++    let mut m = Mock;
++    let mut u_32 = 3000;
++    let a = 200;
++    let mut _b = 8;
++
++    if m != 0 {
++        m -= 1;
++    }
++
++    if a > 0 {
++        _b -= 1;
++    }
++
++    if 0 > a {
++        _b -= 1;
++    }
++
++    if u_32 > 0 {
++        u_32 -= 1;
++    } else {
++        println!("don't lint this");
++    }
++
++    if u_32 > 0 {
++        println!("don't lint this");
++        u_32 -= 1;
++    }
++
++    if u_32 > 42 {
++        println!("brace yourself!");
++    } else if u_32 > 0 {
++        u_32 -= 1;
++    }
 +}
index 5bb9a606422a13fdb740663f33850c3d3181f34b,0000000000000000000000000000000000000000..5e589d931e431a145b5fd48fdcd87fbee6c4c83c
mode 100644,000000..100644
--- /dev/null
@@@ -1,188 -1,0 +1,188 @@@
-   --> $DIR/implicit_saturating_sub.rs:13:5
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:20:13
++  --> $DIR/implicit_saturating_sub.rs:28:5
 +   |
 +LL | /     if u_8 > 0 {
 +LL | |         u_8 = u_8 - 1;
 +LL | |     }
 +   | |_____^ help: try: `u_8 = u_8.saturating_sub(1);`
 +   |
 +   = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:34:5
++  --> $DIR/implicit_saturating_sub.rs:35:13
 +   |
 +LL | /             if u_8 > 0 {
 +LL | |                 u_8 -= 1;
 +LL | |             }
 +   | |_____________^ help: try: `u_8 = u_8.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:44:5
++  --> $DIR/implicit_saturating_sub.rs:49:5
 +   |
 +LL | /     if u_16 > 0 {
 +LL | |         u_16 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `u_16 = u_16.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:65:5
++  --> $DIR/implicit_saturating_sub.rs:59:5
 +   |
 +LL | /     if u_32 != 0 {
 +LL | |         u_32 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `u_32 = u_32.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:70:5
++  --> $DIR/implicit_saturating_sub.rs:80:5
 +   |
 +LL | /     if u_64 > 0 {
 +LL | |         u_64 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:75:5
++  --> $DIR/implicit_saturating_sub.rs:85:5
 +   |
 +LL | /     if 0 < u_64 {
 +LL | |         u_64 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:96:5
++  --> $DIR/implicit_saturating_sub.rs:90:5
 +   |
 +LL | /     if 0 != u_64 {
 +LL | |         u_64 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:108:5
++  --> $DIR/implicit_saturating_sub.rs:111:5
 +   |
 +LL | /     if u_usize > 0 {
 +LL | |         u_usize -= 1;
 +LL | |     }
 +   | |_____^ help: try: `u_usize = u_usize.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:113:5
++  --> $DIR/implicit_saturating_sub.rs:123:5
 +   |
 +LL | /     if i_8 > i8::MIN {
 +LL | |         i_8 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:118:5
++  --> $DIR/implicit_saturating_sub.rs:128:5
 +   |
 +LL | /     if i_8 > i8::MIN {
 +LL | |         i_8 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:123:5
++  --> $DIR/implicit_saturating_sub.rs:133:5
 +   |
 +LL | /     if i_8 != i8::MIN {
 +LL | |         i_8 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:133:5
++  --> $DIR/implicit_saturating_sub.rs:138:5
 +   |
 +LL | /     if i_8 != i8::MIN {
 +LL | |         i_8 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:138:5
++  --> $DIR/implicit_saturating_sub.rs:148:5
 +   |
 +LL | /     if i_16 > i16::MIN {
 +LL | |         i_16 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:143:5
++  --> $DIR/implicit_saturating_sub.rs:153:5
 +   |
 +LL | /     if i_16 > i16::MIN {
 +LL | |         i_16 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:148:5
++  --> $DIR/implicit_saturating_sub.rs:158:5
 +   |
 +LL | /     if i_16 != i16::MIN {
 +LL | |         i_16 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:158:5
++  --> $DIR/implicit_saturating_sub.rs:163:5
 +   |
 +LL | /     if i_16 != i16::MIN {
 +LL | |         i_16 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:163:5
++  --> $DIR/implicit_saturating_sub.rs:173:5
 +   |
 +LL | /     if i_32 > i32::MIN {
 +LL | |         i_32 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:168:5
++  --> $DIR/implicit_saturating_sub.rs:178:5
 +   |
 +LL | /     if i_32 > i32::MIN {
 +LL | |         i_32 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:173:5
++  --> $DIR/implicit_saturating_sub.rs:183:5
 +   |
 +LL | /     if i_32 != i32::MIN {
 +LL | |         i_32 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:183:5
++  --> $DIR/implicit_saturating_sub.rs:188:5
 +   |
 +LL | /     if i_32 != i32::MIN {
 +LL | |         i_32 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:188:5
++  --> $DIR/implicit_saturating_sub.rs:198:5
 +   |
 +LL | /     if i64::MIN < i_64 {
 +LL | |         i_64 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `i_64 = i_64.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
-   --> $DIR/implicit_saturating_sub.rs:193:5
++  --> $DIR/implicit_saturating_sub.rs:203:5
 +   |
 +LL | /     if i64::MIN != i_64 {
 +LL | |         i_64 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `i_64 = i_64.saturating_sub(1);`
 +
 +error: implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:208:5
 +   |
 +LL | /     if i64::MIN < i_64 {
 +LL | |         i_64 -= 1;
 +LL | |     }
 +   | |_____^ help: try: `i_64 = i_64.saturating_sub(1);`
 +
 +error: aborting due to 23 previous errors
 +
index 0cadd5a3da1983e4436d9f946808a84aa45a55b7,0000000000000000000000000000000000000000..1a646e49ce3ae421eeec82866e98c927853e4b3a
mode 100644,000000..100644
--- /dev/null
@@@ -1,42 -1,0 +1,49 @@@
 +// does not test any rustfixable lints
 +
 +#![warn(clippy::mixed_case_hex_literals)]
 +#![warn(clippy::zero_prefixed_literal)]
 +#![warn(clippy::unseparated_literal_suffix)]
 +#![warn(clippy::separated_literal_suffix)]
 +#![allow(dead_code, overflowing_literals)]
 +
 +fn main() {
 +    let ok1 = 0xABCD;
 +    let ok3 = 0xab_cd;
 +    let ok4 = 0xab_cd_i32;
 +    let ok5 = 0xAB_CD_u32;
 +    let ok5 = 0xAB_CD_isize;
 +    let fail1 = 0xabCD;
 +    let fail2 = 0xabCD_u32;
 +    let fail2 = 0xabCD_isize;
 +    let fail_multi_zero = 000_123usize;
 +
 +    let ok9 = 0;
 +    let ok10 = 0_i64;
 +    let fail8 = 0123;
 +
 +    let ok11 = 0o123;
 +    let ok12 = 0b10_1010;
 +
 +    let ok13 = 0xab_abcd;
 +    let ok14 = 0xBAFE_BAFE;
 +    let ok15 = 0xab_cabc_abca_bcab_cabc;
 +    let ok16 = 0xFE_BAFE_ABAB_ABCD;
 +    let ok17 = 0x123_4567_8901_usize;
 +    let ok18 = 0xF;
 +
 +    let fail19 = 12_3456_21;
 +    let fail22 = 3__4___23;
 +    let fail23 = 3__16___23;
 +
 +    let fail24 = 0xAB_ABC_AB;
 +    let fail25 = 0b01_100_101;
 +    let ok26 = 0x6_A0_BF;
 +    let ok27 = 0b1_0010_0101;
 +}
++
++fn issue9651() {
++    // lint but octal form is not possible here
++    let _ = 08;
++    let _ = 09;
++    let _ = 089;
++}
index 365b240747352d76bc06b3e802e04f0049dabec6,0000000000000000000000000000000000000000..603d47bacca80a0a2188b650042621ed55b0a502
mode 100644,000000..100644
--- /dev/null
@@@ -1,139 -1,0 +1,172 @@@
- error: aborting due to 18 previous errors
 +error: integer type suffix should not be separated by an underscore
 +  --> $DIR/literals.rs:12:15
 +   |
 +LL |     let ok4 = 0xab_cd_i32;
 +   |               ^^^^^^^^^^^ help: remove the underscore: `0xab_cdi32`
 +   |
 +   = note: `-D clippy::separated-literal-suffix` implied by `-D warnings`
 +
 +error: integer type suffix should not be separated by an underscore
 +  --> $DIR/literals.rs:13:15
 +   |
 +LL |     let ok5 = 0xAB_CD_u32;
 +   |               ^^^^^^^^^^^ help: remove the underscore: `0xAB_CDu32`
 +
 +error: integer type suffix should not be separated by an underscore
 +  --> $DIR/literals.rs:14:15
 +   |
 +LL |     let ok5 = 0xAB_CD_isize;
 +   |               ^^^^^^^^^^^^^ help: remove the underscore: `0xAB_CDisize`
 +
 +error: inconsistent casing in hexadecimal literal
 +  --> $DIR/literals.rs:15:17
 +   |
 +LL |     let fail1 = 0xabCD;
 +   |                 ^^^^^^
 +   |
 +   = note: `-D clippy::mixed-case-hex-literals` implied by `-D warnings`
 +
 +error: integer type suffix should not be separated by an underscore
 +  --> $DIR/literals.rs:16:17
 +   |
 +LL |     let fail2 = 0xabCD_u32;
 +   |                 ^^^^^^^^^^ help: remove the underscore: `0xabCDu32`
 +
 +error: inconsistent casing in hexadecimal literal
 +  --> $DIR/literals.rs:16:17
 +   |
 +LL |     let fail2 = 0xabCD_u32;
 +   |                 ^^^^^^^^^^
 +
 +error: integer type suffix should not be separated by an underscore
 +  --> $DIR/literals.rs:17:17
 +   |
 +LL |     let fail2 = 0xabCD_isize;
 +   |                 ^^^^^^^^^^^^ help: remove the underscore: `0xabCDisize`
 +
 +error: inconsistent casing in hexadecimal literal
 +  --> $DIR/literals.rs:17:17
 +   |
 +LL |     let fail2 = 0xabCD_isize;
 +   |                 ^^^^^^^^^^^^
 +
 +error: integer type suffix should be separated by an underscore
 +  --> $DIR/literals.rs:18:27
 +   |
 +LL |     let fail_multi_zero = 000_123usize;
 +   |                           ^^^^^^^^^^^^ help: add an underscore: `000_123_usize`
 +   |
 +   = note: `-D clippy::unseparated-literal-suffix` implied by `-D warnings`
 +
 +error: this is a decimal constant
 +  --> $DIR/literals.rs:18:27
 +   |
 +LL |     let fail_multi_zero = 000_123usize;
 +   |                           ^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::zero-prefixed-literal` implied by `-D warnings`
 +help: if you mean to use a decimal constant, remove the `0` to avoid confusion
 +   |
 +LL |     let fail_multi_zero = 123usize;
 +   |                           ~~~~~~~~
 +help: if you mean to use an octal constant, use `0o`
 +   |
 +LL |     let fail_multi_zero = 0o123usize;
 +   |                           ~~~~~~~~~~
 +
 +error: integer type suffix should not be separated by an underscore
 +  --> $DIR/literals.rs:21:16
 +   |
 +LL |     let ok10 = 0_i64;
 +   |                ^^^^^ help: remove the underscore: `0i64`
 +
 +error: this is a decimal constant
 +  --> $DIR/literals.rs:22:17
 +   |
 +LL |     let fail8 = 0123;
 +   |                 ^^^^
 +   |
 +help: if you mean to use a decimal constant, remove the `0` to avoid confusion
 +   |
 +LL |     let fail8 = 123;
 +   |                 ~~~
 +help: if you mean to use an octal constant, use `0o`
 +   |
 +LL |     let fail8 = 0o123;
 +   |                 ~~~~~
 +
 +error: integer type suffix should not be separated by an underscore
 +  --> $DIR/literals.rs:31:16
 +   |
 +LL |     let ok17 = 0x123_4567_8901_usize;
 +   |                ^^^^^^^^^^^^^^^^^^^^^ help: remove the underscore: `0x123_4567_8901usize`
 +
 +error: digits grouped inconsistently by underscores
 +  --> $DIR/literals.rs:34:18
 +   |
 +LL |     let fail19 = 12_3456_21;
 +   |                  ^^^^^^^^^^ help: consider: `12_345_621`
 +   |
 +   = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings`
 +
 +error: digits grouped inconsistently by underscores
 +  --> $DIR/literals.rs:35:18
 +   |
 +LL |     let fail22 = 3__4___23;
 +   |                  ^^^^^^^^^ help: consider: `3_423`
 +
 +error: digits grouped inconsistently by underscores
 +  --> $DIR/literals.rs:36:18
 +   |
 +LL |     let fail23 = 3__16___23;
 +   |                  ^^^^^^^^^^ help: consider: `31_623`
 +
 +error: digits of hex or binary literal not grouped by four
 +  --> $DIR/literals.rs:38:18
 +   |
 +LL |     let fail24 = 0xAB_ABC_AB;
 +   |                  ^^^^^^^^^^^ help: consider: `0x0ABA_BCAB`
 +   |
 +   = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings`
 +
 +error: digits of hex or binary literal not grouped by four
 +  --> $DIR/literals.rs:39:18
 +   |
 +LL |     let fail25 = 0b01_100_101;
 +   |                  ^^^^^^^^^^^^ help: consider: `0b0110_0101`
 +
++error: this is a decimal constant
++  --> $DIR/literals.rs:46:13
++   |
++LL |     let _ = 08;
++   |             ^^
++   |
++help: if you mean to use a decimal constant, remove the `0` to avoid confusion
++   |
++LL |     let _ = 8;
++   |             ~
++
++error: this is a decimal constant
++  --> $DIR/literals.rs:47:13
++   |
++LL |     let _ = 09;
++   |             ^^
++   |
++help: if you mean to use a decimal constant, remove the `0` to avoid confusion
++   |
++LL |     let _ = 9;
++   |             ~
++
++error: this is a decimal constant
++  --> $DIR/literals.rs:48:13
++   |
++LL |     let _ = 089;
++   |             ^^^
++   |
++help: if you mean to use a decimal constant, remove the `0` to avoid confusion
++   |
++LL |     let _ = 89;
++   |             ~~
++
++error: aborting due to 21 previous errors
 +
index 26e3b8f63e700e2a53f579df14bbaf5ac5728452,0000000000000000000000000000000000000000..c9a819ba535449a1b8d90b2324204f74b318a585
mode 100644,000000..100644
--- /dev/null
@@@ -1,64 -1,0 +1,77 @@@
- // [edition2018] edition:2018
- // [edition2021] edition:2021
 +// revisions: edition2018 edition2021
-     assert!(a.is_empty(), "qwqwq");
++//[edition2018] edition:2018
++//[edition2021] edition:2021
 +// run-rustfix
 +
 +#![warn(clippy::manual_assert)]
 +#![allow(dead_code, unused_doc_comments)]
 +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args)]
 +
 +macro_rules! one {
 +    () => {
 +        1
 +    };
 +}
 +
 +fn main() {
 +    let a = vec![1, 2, 3];
 +    let c = Some(2);
 +    if !a.is_empty()
 +        && a.len() == 3
 +        && c.is_some()
 +        && !a.is_empty()
 +        && a.len() == 3
 +        && !a.is_empty()
 +        && a.len() == 3
 +        && !a.is_empty()
 +        && a.len() == 3
 +    {
 +        panic!("qaqaq{:?}", a);
 +    }
 +    assert!(a.is_empty(), "qaqaq{:?}", a);
-     assert!(!b.is_empty(), "panic1");
-     assert!(!(b.is_empty() && a.is_empty()), "panic2");
-     assert!(!(a.is_empty() && !b.is_empty()), "panic3");
-     assert!(!(b.is_empty() || a.is_empty()), "panic4");
-     assert!(!(a.is_empty() || !b.is_empty()), "panic5");
++    if !a.is_empty() {
++        panic!("qwqwq");
++    }
 +    if a.len() == 3 {
 +        println!("qwq");
 +        println!("qwq");
 +        println!("qwq");
 +    }
 +    if let Some(b) = c {
 +        panic!("orz {}", b);
 +    }
 +    if a.len() == 3 {
 +        panic!("qaqaq");
 +    } else {
 +        println!("qwq");
 +    }
 +    let b = vec![1, 2, 3];
-     // comment
- /* this is a
++    if b.is_empty() {
++        panic!("panic1");
++    }
++    if b.is_empty() && a.is_empty() {
++        panic!("panic2");
++    }
++    if a.is_empty() && !b.is_empty() {
++        panic!("panic3");
++    }
++    if b.is_empty() || a.is_empty() {
++        panic!("panic4");
++    }
++    if a.is_empty() || !b.is_empty() {
++        panic!("panic5");
++    }
 +    assert!(!a.is_empty(), "with expansion {}", one!());
 +}
 +
 +fn issue7730(a: u8) {
 +    // Suggestion should preserve comment
- /// Doc comment
- // comment after `panic!`
- assert!(!(a > 2), "panic with comment");
++    if a > 2 {
++        // comment
++        /* this is a
 +        multiline
 +        comment */
++        /// Doc comment
++        panic!("panic with comment") // comment after `panic!`
++    }
 +}
index 237638ee1344c60274bb31c1accbcf7f7456cee3,0000000000000000000000000000000000000000..1f2e1e3087bd09f7800184fc505a16f6f61a36aa
mode 100644,000000..100644
--- /dev/null
@@@ -1,85 -1,0 +1,20 @@@
- error: only a `panic!` in `if`-then statement
-   --> $DIR/manual_assert.rs:34:5
-    |
- LL | /     if !a.is_empty() {
- LL | |         panic!("qwqwq");
- LL | |     }
-    | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");`
- error: only a `panic!` in `if`-then statement
-   --> $DIR/manual_assert.rs:51:5
-    |
- LL | /     if b.is_empty() {
- LL | |         panic!("panic1");
- LL | |     }
-    | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");`
- error: only a `panic!` in `if`-then statement
-   --> $DIR/manual_assert.rs:54:5
-    |
- LL | /     if b.is_empty() && a.is_empty() {
- LL | |         panic!("panic2");
- LL | |     }
-    | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");`
- error: only a `panic!` in `if`-then statement
-   --> $DIR/manual_assert.rs:57:5
-    |
- LL | /     if a.is_empty() && !b.is_empty() {
- LL | |         panic!("panic3");
- LL | |     }
-    | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");`
- error: only a `panic!` in `if`-then statement
-   --> $DIR/manual_assert.rs:60:5
-    |
- LL | /     if b.is_empty() || a.is_empty() {
- LL | |         panic!("panic4");
- LL | |     }
-    | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");`
- error: only a `panic!` in `if`-then statement
-   --> $DIR/manual_assert.rs:63:5
-    |
- LL | /     if a.is_empty() || !b.is_empty() {
- LL | |         panic!("panic5");
- LL | |     }
-    | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");`
 +error: only a `panic!` in `if`-then statement
 +  --> $DIR/manual_assert.rs:31:5
 +   |
 +LL | /     if !a.is_empty() {
 +LL | |         panic!("qaqaq{:?}", a);
 +LL | |     }
 +   | |_____^ help: try instead: `assert!(a.is_empty(), "qaqaq{:?}", a);`
 +   |
 +   = note: `-D clippy::manual-assert` implied by `-D warnings`
 +
- error: only a `panic!` in `if`-then statement
-   --> $DIR/manual_assert.rs:73:5
-    |
- LL | /     if a > 2 {
- LL | |         // comment
- LL | |         /* this is a
- LL | |         multiline
- ...  |
- LL | |         panic!("panic with comment") // comment after `panic!`
- LL | |     }
-    | |_____^
-    |
- help: try instead
-    |
- LL |     assert!(!(a > 2), "panic with comment");
-    |
- error: aborting due to 9 previous errors
 +error: only a `panic!` in `if`-then statement
 +  --> $DIR/manual_assert.rs:66:5
 +   |
 +LL | /     if a.is_empty() {
 +LL | |         panic!("with expansion {}", one!())
 +LL | |     }
 +   | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());`
 +
++error: aborting due to 2 previous errors
 +
index 26e3b8f63e700e2a53f579df14bbaf5ac5728452,0000000000000000000000000000000000000000..2f62de51cadcd948a98c0b75668aa2f13c6b4a92
mode 100644,000000..100644
--- /dev/null
@@@ -1,64 -1,0 +1,64 @@@
- // [edition2018] edition:2018
- // [edition2021] edition:2021
 +// revisions: edition2018 edition2021
++//[edition2018] edition:2018
++//[edition2021] edition:2021
 +// run-rustfix
 +
 +#![warn(clippy::manual_assert)]
 +#![allow(dead_code, unused_doc_comments)]
 +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args)]
 +
 +macro_rules! one {
 +    () => {
 +        1
 +    };
 +}
 +
 +fn main() {
 +    let a = vec![1, 2, 3];
 +    let c = Some(2);
 +    if !a.is_empty()
 +        && a.len() == 3
 +        && c.is_some()
 +        && !a.is_empty()
 +        && a.len() == 3
 +        && !a.is_empty()
 +        && a.len() == 3
 +        && !a.is_empty()
 +        && a.len() == 3
 +    {
 +        panic!("qaqaq{:?}", a);
 +    }
 +    assert!(a.is_empty(), "qaqaq{:?}", a);
 +    assert!(a.is_empty(), "qwqwq");
 +    if a.len() == 3 {
 +        println!("qwq");
 +        println!("qwq");
 +        println!("qwq");
 +    }
 +    if let Some(b) = c {
 +        panic!("orz {}", b);
 +    }
 +    if a.len() == 3 {
 +        panic!("qaqaq");
 +    } else {
 +        println!("qwq");
 +    }
 +    let b = vec![1, 2, 3];
 +    assert!(!b.is_empty(), "panic1");
 +    assert!(!(b.is_empty() && a.is_empty()), "panic2");
 +    assert!(!(a.is_empty() && !b.is_empty()), "panic3");
 +    assert!(!(b.is_empty() || a.is_empty()), "panic4");
 +    assert!(!(a.is_empty() || !b.is_empty()), "panic5");
 +    assert!(!a.is_empty(), "with expansion {}", one!());
 +}
 +
 +fn issue7730(a: u8) {
 +    // Suggestion should preserve comment
 +    // comment
 +/* this is a
 +        multiline
 +        comment */
 +/// Doc comment
 +// comment after `panic!`
 +assert!(!(a > 2), "panic with comment");
 +}
index 8c37753071dfb9b393d0183b2e3ebbd8b475fbe3,0000000000000000000000000000000000000000..6a4cc2468d4193815b6b43299db873b95d8fb33d
mode 100644,000000..100644
--- /dev/null
@@@ -1,81 -1,0 +1,81 @@@
- // [edition2018] edition:2018
- // [edition2021] edition:2021
 +// revisions: edition2018 edition2021
++//[edition2018] edition:2018
++//[edition2021] edition:2021
 +// run-rustfix
 +
 +#![warn(clippy::manual_assert)]
 +#![allow(dead_code, unused_doc_comments)]
 +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args)]
 +
 +macro_rules! one {
 +    () => {
 +        1
 +    };
 +}
 +
 +fn main() {
 +    let a = vec![1, 2, 3];
 +    let c = Some(2);
 +    if !a.is_empty()
 +        && a.len() == 3
 +        && c.is_some()
 +        && !a.is_empty()
 +        && a.len() == 3
 +        && !a.is_empty()
 +        && a.len() == 3
 +        && !a.is_empty()
 +        && a.len() == 3
 +    {
 +        panic!("qaqaq{:?}", a);
 +    }
 +    if !a.is_empty() {
 +        panic!("qaqaq{:?}", a);
 +    }
 +    if !a.is_empty() {
 +        panic!("qwqwq");
 +    }
 +    if a.len() == 3 {
 +        println!("qwq");
 +        println!("qwq");
 +        println!("qwq");
 +    }
 +    if let Some(b) = c {
 +        panic!("orz {}", b);
 +    }
 +    if a.len() == 3 {
 +        panic!("qaqaq");
 +    } else {
 +        println!("qwq");
 +    }
 +    let b = vec![1, 2, 3];
 +    if b.is_empty() {
 +        panic!("panic1");
 +    }
 +    if b.is_empty() && a.is_empty() {
 +        panic!("panic2");
 +    }
 +    if a.is_empty() && !b.is_empty() {
 +        panic!("panic3");
 +    }
 +    if b.is_empty() || a.is_empty() {
 +        panic!("panic4");
 +    }
 +    if a.is_empty() || !b.is_empty() {
 +        panic!("panic5");
 +    }
 +    if a.is_empty() {
 +        panic!("with expansion {}", one!())
 +    }
 +}
 +
 +fn issue7730(a: u8) {
 +    // Suggestion should preserve comment
 +    if a > 2 {
 +        // comment
 +        /* this is a
 +        multiline
 +        comment */
 +        /// Doc comment
 +        panic!("panic with comment") // comment after `panic!`
 +    }
 +}
index 54fd888af99fa9ccf7a2d26a3c1a21af7c050196,0000000000000000000000000000000000000000..331fd29b74e8edb4bcffddc33b66b3d7948e32d4
mode 100644,000000..100644
--- /dev/null
@@@ -1,304 -1,0 +1,331 @@@
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::manual_clamp)]
 +#![allow(
 +    unused,
 +    dead_code,
 +    clippy::unnecessary_operation,
 +    clippy::no_effect,
 +    clippy::if_same_then_else
 +)]
 +
 +use std::cmp::{max as cmp_max, min as cmp_min};
 +
 +const CONST_MAX: i32 = 10;
 +const CONST_MIN: i32 = 4;
 +
 +const CONST_F64_MAX: f64 = 10.0;
 +const CONST_F64_MIN: f64 = 4.0;
 +
 +fn main() {
 +    let (input, min, max) = (0, -2, 3);
 +    // Lint
 +    let x0 = if max < input {
 +        max
 +    } else if min > input {
 +        min
 +    } else {
 +        input
 +    };
 +
 +    let x1 = if input > max {
 +        max
 +    } else if input < min {
 +        min
 +    } else {
 +        input
 +    };
 +
 +    let x2 = if input < min {
 +        min
 +    } else if input > max {
 +        max
 +    } else {
 +        input
 +    };
 +
 +    let x3 = if min > input {
 +        min
 +    } else if max < input {
 +        max
 +    } else {
 +        input
 +    };
 +
 +    let x4 = input.max(min).min(max);
 +
 +    let x5 = input.min(max).max(min);
 +
 +    let x6 = match input {
 +        x if x > max => max,
 +        x if x < min => min,
 +        x => x,
 +    };
 +
 +    let x7 = match input {
 +        x if x < min => min,
 +        x if x > max => max,
 +        x => x,
 +    };
 +
 +    let x8 = match input {
 +        x if max < x => max,
 +        x if min > x => min,
 +        x => x,
 +    };
 +
 +    let mut x9 = input;
 +    if x9 < min {
 +        x9 = min;
 +    }
 +    if x9 > max {
 +        x9 = max;
 +    }
 +
 +    let x10 = match input {
 +        x if min > x => min,
 +        x if max < x => max,
 +        x => x,
 +    };
 +
 +    let mut x11 = input;
 +    let _ = 1;
 +    if x11 > max {
 +        x11 = max;
 +    }
 +    if x11 < min {
 +        x11 = min;
 +    }
 +
 +    let mut x12 = input;
 +    if min > x12 {
 +        x12 = min;
 +    }
 +    if max < x12 {
 +        x12 = max;
 +    }
 +
 +    let mut x13 = input;
 +    if max < x13 {
 +        x13 = max;
 +    }
 +    if min > x13 {
 +        x13 = min;
 +    }
 +
 +    let x14 = if input > CONST_MAX {
 +        CONST_MAX
 +    } else if input < CONST_MIN {
 +        CONST_MIN
 +    } else {
 +        input
 +    };
 +    {
 +        let (input, min, max) = (0.0f64, -2.0, 3.0);
 +        let x15 = if input > max {
 +            max
 +        } else if input < min {
 +            min
 +        } else {
 +            input
 +        };
 +    }
 +    {
 +        let input: i32 = cmp_min_max(1);
 +        // These can only be detected if exactly one of the arguments to the inner function is const.
 +        let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN);
 +        let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX);
 +        let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX));
 +        let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN));
 +        let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN);
 +        let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX);
 +        let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input));
 +        let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input));
 +        let input: f64 = cmp_min_max(1) as f64;
 +        let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN);
 +        let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX);
 +        let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX));
 +        let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN));
 +        let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN);
 +        let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX);
 +        let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input));
 +        let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input));
 +    }
 +    let mut x32 = input;
 +    if x32 < min {
 +        x32 = min;
 +    } else if x32 > max {
 +        x32 = max;
 +    }
 +
 +    // It's important this be the last set of statements
 +    let mut x33 = input;
 +    if max < x33 {
 +        x33 = max;
 +    }
 +    if min > x33 {
 +        x33 = min;
 +    }
 +}
 +
 +// This code intentionally nonsense.
 +fn no_lint() {
 +    let (input, min, max) = (0, -2, 3);
 +    let x0 = if max < input {
 +        max
 +    } else if min > input {
 +        max
 +    } else {
 +        min
 +    };
 +
 +    let x1 = if input > max {
 +        max
 +    } else if input > min {
 +        min
 +    } else {
 +        max
 +    };
 +
 +    let x2 = if max < min {
 +        min
 +    } else if input > max {
 +        input
 +    } else {
 +        input
 +    };
 +
 +    let x3 = if min > input {
 +        input
 +    } else if max < input {
 +        max
 +    } else {
 +        max
 +    };
 +
 +    let x6 = match input {
 +        x if x < max => x,
 +        x if x < min => x,
 +        x => x,
 +    };
 +
 +    let x7 = match input {
 +        x if x < min => max,
 +        x if x > max => min,
 +        x => x,
 +    };
 +
 +    let x8 = match input {
 +        x if max > x => max,
 +        x if min > x => min,
 +        x => x,
 +    };
 +
 +    let mut x9 = input;
 +    if x9 > min {
 +        x9 = min;
 +    }
 +    if x9 > max {
 +        x9 = max;
 +    }
 +
 +    let x10 = match input {
 +        x if min > x => min,
 +        x if max < x => max,
 +        x => min,
 +    };
 +
 +    let mut x11 = input;
 +    if x11 > max {
 +        x11 = min;
 +    }
 +    if x11 < min {
 +        x11 = max;
 +    }
 +
 +    let mut x12 = input;
 +    if min > x12 {
 +        x12 = max * 3;
 +    }
 +    if max < x12 {
 +        x12 = min;
 +    }
 +
 +    let mut x13 = input;
 +    if max < x13 {
 +        let x13 = max;
 +    }
 +    if min > x13 {
 +        x13 = min;
 +    }
 +    let mut x14 = input;
 +    if x14 < min {
 +        x14 = 3;
 +    } else if x14 > max {
 +        x14 = max;
 +    }
 +    {
 +        let input: i32 = cmp_min_max(1);
 +        // These can only be detected if exactly one of the arguments to the inner function is const.
 +        let x16 = cmp_max(cmp_max(input, CONST_MAX), CONST_MIN);
 +        let x17 = cmp_min(cmp_min(input, CONST_MIN), CONST_MAX);
 +        let x18 = cmp_max(CONST_MIN, cmp_max(input, CONST_MAX));
 +        let x19 = cmp_min(CONST_MAX, cmp_min(input, CONST_MIN));
 +        let x20 = cmp_max(cmp_max(CONST_MAX, input), CONST_MIN);
 +        let x21 = cmp_min(cmp_min(CONST_MIN, input), CONST_MAX);
 +        let x22 = cmp_max(CONST_MIN, cmp_max(CONST_MAX, input));
 +        let x23 = cmp_min(CONST_MAX, cmp_min(CONST_MIN, input));
 +        let input: f64 = cmp_min_max(1) as f64;
 +        let x24 = f64::max(f64::max(input, CONST_F64_MAX), CONST_F64_MIN);
 +        let x25 = f64::min(f64::min(input, CONST_F64_MIN), CONST_F64_MAX);
 +        let x26 = f64::max(CONST_F64_MIN, f64::max(input, CONST_F64_MAX));
 +        let x27 = f64::min(CONST_F64_MAX, f64::min(input, CONST_F64_MIN));
 +        let x28 = f64::max(f64::max(CONST_F64_MAX, input), CONST_F64_MIN);
 +        let x29 = f64::min(f64::min(CONST_F64_MIN, input), CONST_F64_MAX);
 +        let x30 = f64::max(CONST_F64_MIN, f64::max(CONST_F64_MAX, input));
 +        let x31 = f64::min(CONST_F64_MAX, f64::min(CONST_F64_MIN, input));
 +        let x32 = f64::min(CONST_F64_MAX, f64::min(CONST_F64_MIN, CONST_F64_MAX));
 +    }
 +}
 +
 +fn dont_tell_me_what_to_do() {
 +    let (input, min, max) = (0, -2, 3);
 +    let mut x_never = input;
 +    #[allow(clippy::manual_clamp)]
 +    if x_never < min {
 +        x_never = min;
 +    }
 +    if x_never > max {
 +        x_never = max;
 +    }
 +}
 +
 +/// Just to ensure this isn't const evaled
 +fn cmp_min_max(input: i32) -> i32 {
 +    input * 3
 +}
++
++fn msrv_1_49() {
++    #![clippy::msrv = "1.49"]
++
++    let (input, min, max) = (0, -1, 2);
++    let _ = if input < min {
++        min
++    } else if input > max {
++        max
++    } else {
++        input
++    };
++}
++
++fn msrv_1_50() {
++    #![clippy::msrv = "1.50"]
++
++    let (input, min, max) = (0, -1, 2);
++    let _ = if input < min {
++        min
++    } else if input > max {
++        max
++    } else {
++        input
++    };
++}
index 0604f8606c3f3388465690aa456d0026e5c4a55b,0000000000000000000000000000000000000000..70abe28091c94ac5570761805e55f5ecfc21517f
mode 100644,000000..100644
--- /dev/null
@@@ -1,375 -1,0 +1,390 @@@
-   --> $DIR/manual_clamp.rs:76:5
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:91:5
++  --> $DIR/manual_clamp.rs:77:5
 +   |
 +LL | /     if x9 < min {
 +LL | |         x9 = min;
 +LL | |     }
 +LL | |     if x9 > max {
 +LL | |         x9 = max;
 +LL | |     }
 +   | |_____^ help: replace with clamp: `x9 = x9.clamp(min, max);`
 +   |
 +   = note: clamp will panic if max < min
 +   = note: `-D clippy::manual-clamp` implied by `-D warnings`
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:99:5
++  --> $DIR/manual_clamp.rs:92:5
 +   |
 +LL | /     if x11 > max {
 +LL | |         x11 = max;
 +LL | |     }
 +LL | |     if x11 < min {
 +LL | |         x11 = min;
 +LL | |     }
 +   | |_____^ help: replace with clamp: `x11 = x11.clamp(min, max);`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:107:5
++  --> $DIR/manual_clamp.rs:100:5
 +   |
 +LL | /     if min > x12 {
 +LL | |         x12 = min;
 +LL | |     }
 +LL | |     if max < x12 {
 +LL | |         x12 = max;
 +LL | |     }
 +   | |_____^ help: replace with clamp: `x12 = x12.clamp(min, max);`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:161:5
++  --> $DIR/manual_clamp.rs:108:5
 +   |
 +LL | /     if max < x13 {
 +LL | |         x13 = max;
 +LL | |     }
 +LL | |     if min > x13 {
 +LL | |         x13 = min;
 +LL | |     }
 +   | |_____^ help: replace with clamp: `x13 = x13.clamp(min, max);`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:21:14
++  --> $DIR/manual_clamp.rs:162:5
 +   |
 +LL | /     if max < x33 {
 +LL | |         x33 = max;
 +LL | |     }
 +LL | |     if min > x33 {
 +LL | |         x33 = min;
 +LL | |     }
 +   | |_____^ help: replace with clamp: `x33 = x33.clamp(min, max);`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:29:14
++  --> $DIR/manual_clamp.rs:22:14
 +   |
 +LL |       let x0 = if max < input {
 +   |  ______________^
 +LL | |         max
 +LL | |     } else if min > input {
 +LL | |         min
 +LL | |     } else {
 +LL | |         input
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:37:14
++  --> $DIR/manual_clamp.rs:30:14
 +   |
 +LL |       let x1 = if input > max {
 +   |  ______________^
 +LL | |         max
 +LL | |     } else if input < min {
 +LL | |         min
 +LL | |     } else {
 +LL | |         input
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:45:14
++  --> $DIR/manual_clamp.rs:38:14
 +   |
 +LL |       let x2 = if input < min {
 +   |  ______________^
 +LL | |         min
 +LL | |     } else if input > max {
 +LL | |         max
 +LL | |     } else {
 +LL | |         input
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:53:14
++  --> $DIR/manual_clamp.rs:46:14
 +   |
 +LL |       let x3 = if min > input {
 +   |  ______________^
 +LL | |         min
 +LL | |     } else if max < input {
 +LL | |         max
 +LL | |     } else {
 +LL | |         input
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:55:14
++  --> $DIR/manual_clamp.rs:54:14
 +   |
 +LL |     let x4 = input.max(min).min(max);
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:57:14
++  --> $DIR/manual_clamp.rs:56:14
 +   |
 +LL |     let x5 = input.min(max).max(min);
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:63:14
++  --> $DIR/manual_clamp.rs:58:14
 +   |
 +LL |       let x6 = match input {
 +   |  ______________^
 +LL | |         x if x > max => max,
 +LL | |         x if x < min => min,
 +LL | |         x => x,
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:69:14
++  --> $DIR/manual_clamp.rs:64:14
 +   |
 +LL |       let x7 = match input {
 +   |  ______________^
 +LL | |         x if x < min => min,
 +LL | |         x if x > max => max,
 +LL | |         x => x,
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:83:15
++  --> $DIR/manual_clamp.rs:70:14
 +   |
 +LL |       let x8 = match input {
 +   |  ______________^
 +LL | |         x if max < x => max,
 +LL | |         x if min > x => min,
 +LL | |         x => x,
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:114:15
++  --> $DIR/manual_clamp.rs:84:15
 +   |
 +LL |       let x10 = match input {
 +   |  _______________^
 +LL | |         x if min > x => min,
 +LL | |         x if max < x => max,
 +LL | |         x => x,
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:123:19
++  --> $DIR/manual_clamp.rs:115:15
 +   |
 +LL |       let x14 = if input > CONST_MAX {
 +   |  _______________^
 +LL | |         CONST_MAX
 +LL | |     } else if input < CONST_MIN {
 +LL | |         CONST_MIN
 +LL | |     } else {
 +LL | |         input
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:134:19
++  --> $DIR/manual_clamp.rs:124:19
 +   |
 +LL |           let x15 = if input > max {
 +   |  ___________________^
 +LL | |             max
 +LL | |         } else if input < min {
 +LL | |             min
 +LL | |         } else {
 +LL | |             input
 +LL | |         };
 +   | |_________^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = note: clamp returns NaN if the input is NaN
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:135:19
++  --> $DIR/manual_clamp.rs:135:19
 +   |
 +LL |         let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN);
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:136:19
++  --> $DIR/manual_clamp.rs:136:19
 +   |
 +LL |         let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX);
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:137:19
++  --> $DIR/manual_clamp.rs:137:19
 +   |
 +LL |         let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:138:19
++  --> $DIR/manual_clamp.rs:138:19
 +   |
 +LL |         let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:139:19
++  --> $DIR/manual_clamp.rs:139:19
 +   |
 +LL |         let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN);
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:140:19
++  --> $DIR/manual_clamp.rs:140:19
 +   |
 +LL |         let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX);
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:141:19
++  --> $DIR/manual_clamp.rs:141:19
 +   |
 +LL |         let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:143:19
++  --> $DIR/manual_clamp.rs:142:19
 +   |
 +LL |         let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:144:19
++  --> $DIR/manual_clamp.rs:144:19
 +   |
 +LL |         let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN);
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = note: clamp returns NaN if the input is NaN
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:145:19
++  --> $DIR/manual_clamp.rs:145:19
 +   |
 +LL |         let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX);
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = note: clamp returns NaN if the input is NaN
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:146:19
++  --> $DIR/manual_clamp.rs:146:19
 +   |
 +LL |         let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = note: clamp returns NaN if the input is NaN
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:147:19
++  --> $DIR/manual_clamp.rs:147:19
 +   |
 +LL |         let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = note: clamp returns NaN if the input is NaN
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:148:19
++  --> $DIR/manual_clamp.rs:148:19
 +   |
 +LL |         let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN);
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = note: clamp returns NaN if the input is NaN
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:149:19
++  --> $DIR/manual_clamp.rs:149:19
 +   |
 +LL |         let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX);
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = note: clamp returns NaN if the input is NaN
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:150:19
++  --> $DIR/manual_clamp.rs:150:19
 +   |
 +LL |         let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = note: clamp returns NaN if the input is NaN
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:153:5
++  --> $DIR/manual_clamp.rs:151:19
 +   |
 +LL |         let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = note: clamp returns NaN if the input is NaN
 +
 +error: clamp-like pattern without using clamp function
- error: aborting due to 34 previous errors
++  --> $DIR/manual_clamp.rs:154:5
 +   |
 +LL | /     if x32 < min {
 +LL | |         x32 = min;
 +LL | |     } else if x32 > max {
 +LL | |         x32 = max;
 +LL | |     }
 +   | |_____^ help: replace with clamp: `x32 = x32.clamp(min, max);`
 +   |
 +   = note: clamp will panic if max < min
 +
++error: clamp-like pattern without using clamp function
++  --> $DIR/manual_clamp.rs:324:13
++   |
++LL |       let _ = if input < min {
++   |  _____________^
++LL | |         min
++LL | |     } else if input > max {
++LL | |         max
++LL | |     } else {
++LL | |         input
++LL | |     };
++   | |_____^ help: replace with clamp: `input.clamp(min, max)`
++   |
++   = note: clamp will panic if max < min
++
++error: aborting due to 35 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3553291b87df6731aca2e71f905ed63eef21cda7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,119 @@@
++// run-rustfix
++
++#![warn(clippy::manual_filter)]
++#![allow(unused_variables, dead_code)]
++
++fn main() {
++    Some(0).filter(|&x| x <= 0);
++
++    Some(1).filter(|&x| x <= 0);
++
++    Some(2).filter(|&x| x <= 0);
++
++    Some(3).filter(|&x| x > 0);
++
++    let y = Some(4);
++    y.filter(|&x| x <= 0);
++
++    Some(5).filter(|&x| x > 0);
++
++    Some(6).as_ref().filter(|&x| x > &0);
++
++    let external_cond = true;
++    Some(String::new()).filter(|x| external_cond);
++
++    Some(7).filter(|&x| external_cond);
++
++    Some(8).filter(|&x| x != 0);
++
++    Some(9).filter(|&x| x > 10 && x < 100);
++
++    const fn f1() {
++        // Don't lint, `.filter` is not const
++        match Some(10) {
++            Some(x) => {
++                if x > 10 && x < 100 {
++                    Some(x)
++                } else {
++                    None
++                }
++            },
++            None => None,
++        };
++    }
++
++    #[allow(clippy::blocks_in_if_conditions)]
++    Some(11).filter(|&x| {
++                println!("foo");
++                x > 10 && x < 100
++            });
++
++    match Some(12) {
++        // Don't Lint, statement is lost by `.filter`
++        Some(x) => {
++            if x > 10 && x < 100 {
++                println!("foo");
++                Some(x)
++            } else {
++                None
++            }
++        },
++        None => None,
++    };
++
++    match Some(13) {
++        // Don't Lint, because of `None => Some(1)`
++        Some(x) => {
++            if x > 10 && x < 100 {
++                println!("foo");
++                Some(x)
++            } else {
++                None
++            }
++        },
++        None => Some(1),
++    };
++
++    unsafe fn f(x: u32) -> bool {
++        true
++    }
++    let _ = Some(14).filter(|&x| unsafe { f(x) });
++    let _ = Some(15).filter(|&x| unsafe { f(x) });
++
++    #[allow(clippy::redundant_pattern_matching)]
++    if let Some(_) = Some(16) {
++        Some(16)
++    } else { Some(16).filter(|&x| x % 2 == 0) };
++
++    match Some((17, 17)) {
++        // Not linted for now could be
++        Some((x, y)) => {
++            if y != x {
++                Some((x, y))
++            } else {
++                None
++            }
++        },
++        None => None,
++    };
++
++    struct NamedTuple {
++        pub x: u8,
++        pub y: (i32, u32),
++    }
++
++    match Some(NamedTuple {
++        // Not linted for now could be
++        x: 17,
++        y: (18, 19),
++    }) {
++        Some(NamedTuple { x, y }) => {
++            if y.1 != x as u32 {
++                Some(NamedTuple { x, y })
++            } else {
++                None
++            }
++        },
++        None => None,
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aa9f90f752b1790b1cb3ef52d6052d12221503c6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,243 @@@
++// run-rustfix
++
++#![warn(clippy::manual_filter)]
++#![allow(unused_variables, dead_code)]
++
++fn main() {
++    match Some(0) {
++        None => None,
++        Some(x) => {
++            if x > 0 {
++                None
++            } else {
++                Some(x)
++            }
++        },
++    };
++
++    match Some(1) {
++        Some(x) => {
++            if x > 0 {
++                None
++            } else {
++                Some(x)
++            }
++        },
++        None => None,
++    };
++
++    match Some(2) {
++        Some(x) => {
++            if x > 0 {
++                None
++            } else {
++                Some(x)
++            }
++        },
++        _ => None,
++    };
++
++    match Some(3) {
++        Some(x) => {
++            if x > 0 {
++                Some(x)
++            } else {
++                None
++            }
++        },
++        None => None,
++    };
++
++    let y = Some(4);
++    match y {
++        // Some(4)
++        None => None,
++        Some(x) => {
++            if x > 0 {
++                None
++            } else {
++                Some(x)
++            }
++        },
++    };
++
++    match Some(5) {
++        Some(x) => {
++            if x > 0 {
++                Some(x)
++            } else {
++                None
++            }
++        },
++        _ => None,
++    };
++
++    match Some(6) {
++        Some(ref x) => {
++            if x > &0 {
++                Some(x)
++            } else {
++                None
++            }
++        },
++        _ => None,
++    };
++
++    let external_cond = true;
++    match Some(String::new()) {
++        Some(x) => {
++            if external_cond {
++                Some(x)
++            } else {
++                None
++            }
++        },
++        _ => None,
++    };
++
++    if let Some(x) = Some(7) {
++        if external_cond { Some(x) } else { None }
++    } else {
++        None
++    };
++
++    match &Some(8) {
++        &Some(x) => {
++            if x != 0 {
++                Some(x)
++            } else {
++                None
++            }
++        },
++        _ => None,
++    };
++
++    match Some(9) {
++        Some(x) => {
++            if x > 10 && x < 100 {
++                Some(x)
++            } else {
++                None
++            }
++        },
++        None => None,
++    };
++
++    const fn f1() {
++        // Don't lint, `.filter` is not const
++        match Some(10) {
++            Some(x) => {
++                if x > 10 && x < 100 {
++                    Some(x)
++                } else {
++                    None
++                }
++            },
++            None => None,
++        };
++    }
++
++    #[allow(clippy::blocks_in_if_conditions)]
++    match Some(11) {
++        // Lint, statement is preserved by `.filter`
++        Some(x) => {
++            if {
++                println!("foo");
++                x > 10 && x < 100
++            } {
++                Some(x)
++            } else {
++                None
++            }
++        },
++        None => None,
++    };
++
++    match Some(12) {
++        // Don't Lint, statement is lost by `.filter`
++        Some(x) => {
++            if x > 10 && x < 100 {
++                println!("foo");
++                Some(x)
++            } else {
++                None
++            }
++        },
++        None => None,
++    };
++
++    match Some(13) {
++        // Don't Lint, because of `None => Some(1)`
++        Some(x) => {
++            if x > 10 && x < 100 {
++                println!("foo");
++                Some(x)
++            } else {
++                None
++            }
++        },
++        None => Some(1),
++    };
++
++    unsafe fn f(x: u32) -> bool {
++        true
++    }
++    let _ = match Some(14) {
++        Some(x) => {
++            if unsafe { f(x) } {
++                Some(x)
++            } else {
++                None
++            }
++        },
++        None => None,
++    };
++    let _ = match Some(15) {
++        Some(x) => unsafe {
++            if f(x) { Some(x) } else { None }
++        },
++        None => None,
++    };
++
++    #[allow(clippy::redundant_pattern_matching)]
++    if let Some(_) = Some(16) {
++        Some(16)
++    } else if let Some(x) = Some(16) {
++        // Lint starting from here
++        if x % 2 == 0 { Some(x) } else { None }
++    } else {
++        None
++    };
++
++    match Some((17, 17)) {
++        // Not linted for now could be
++        Some((x, y)) => {
++            if y != x {
++                Some((x, y))
++            } else {
++                None
++            }
++        },
++        None => None,
++    };
++
++    struct NamedTuple {
++        pub x: u8,
++        pub y: (i32, u32),
++    }
++
++    match Some(NamedTuple {
++        // Not linted for now could be
++        x: 17,
++        y: (18, 19),
++    }) {
++        Some(NamedTuple { x, y }) => {
++            if y.1 != x as u32 {
++                Some(NamedTuple { x, y })
++            } else {
++                None
++            }
++        },
++        None => None,
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..53dea9229306bb2dc2ac21f4eedc46ed8e735e19
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,191 @@@
++error: manual implementation of `Option::filter`
++  --> $DIR/manual_filter.rs:7:5
++   |
++LL | /     match Some(0) {
++LL | |         None => None,
++LL | |         Some(x) => {
++LL | |             if x > 0 {
++...  |
++LL | |         },
++LL | |     };
++   | |_____^ help: try this: `Some(0).filter(|&x| x <= 0)`
++   |
++   = note: `-D clippy::manual-filter` implied by `-D warnings`
++
++error: manual implementation of `Option::filter`
++  --> $DIR/manual_filter.rs:18:5
++   |
++LL | /     match Some(1) {
++LL | |         Some(x) => {
++LL | |             if x > 0 {
++LL | |                 None
++...  |
++LL | |         None => None,
++LL | |     };
++   | |_____^ help: try this: `Some(1).filter(|&x| x <= 0)`
++
++error: manual implementation of `Option::filter`
++  --> $DIR/manual_filter.rs:29:5
++   |
++LL | /     match Some(2) {
++LL | |         Some(x) => {
++LL | |             if x > 0 {
++LL | |                 None
++...  |
++LL | |         _ => None,
++LL | |     };
++   | |_____^ help: try this: `Some(2).filter(|&x| x <= 0)`
++
++error: manual implementation of `Option::filter`
++  --> $DIR/manual_filter.rs:40:5
++   |
++LL | /     match Some(3) {
++LL | |         Some(x) => {
++LL | |             if x > 0 {
++LL | |                 Some(x)
++...  |
++LL | |         None => None,
++LL | |     };
++   | |_____^ help: try this: `Some(3).filter(|&x| x > 0)`
++
++error: manual implementation of `Option::filter`
++  --> $DIR/manual_filter.rs:52:5
++   |
++LL | /     match y {
++LL | |         // Some(4)
++LL | |         None => None,
++LL | |         Some(x) => {
++...  |
++LL | |         },
++LL | |     };
++   | |_____^ help: try this: `y.filter(|&x| x <= 0)`
++
++error: manual implementation of `Option::filter`
++  --> $DIR/manual_filter.rs:64:5
++   |
++LL | /     match Some(5) {
++LL | |         Some(x) => {
++LL | |             if x > 0 {
++LL | |                 Some(x)
++...  |
++LL | |         _ => None,
++LL | |     };
++   | |_____^ help: try this: `Some(5).filter(|&x| x > 0)`
++
++error: manual implementation of `Option::filter`
++  --> $DIR/manual_filter.rs:75:5
++   |
++LL | /     match Some(6) {
++LL | |         Some(ref x) => {
++LL | |             if x > &0 {
++LL | |                 Some(x)
++...  |
++LL | |         _ => None,
++LL | |     };
++   | |_____^ help: try this: `Some(6).as_ref().filter(|&x| x > &0)`
++
++error: manual implementation of `Option::filter`
++  --> $DIR/manual_filter.rs:87:5
++   |
++LL | /     match Some(String::new()) {
++LL | |         Some(x) => {
++LL | |             if external_cond {
++LL | |                 Some(x)
++...  |
++LL | |         _ => None,
++LL | |     };
++   | |_____^ help: try this: `Some(String::new()).filter(|x| external_cond)`
++
++error: manual implementation of `Option::filter`
++  --> $DIR/manual_filter.rs:98:5
++   |
++LL | /     if let Some(x) = Some(7) {
++LL | |         if external_cond { Some(x) } else { None }
++LL | |     } else {
++LL | |         None
++LL | |     };
++   | |_____^ help: try this: `Some(7).filter(|&x| external_cond)`
++
++error: manual implementation of `Option::filter`
++  --> $DIR/manual_filter.rs:104:5
++   |
++LL | /     match &Some(8) {
++LL | |         &Some(x) => {
++LL | |             if x != 0 {
++LL | |                 Some(x)
++...  |
++LL | |         _ => None,
++LL | |     };
++   | |_____^ help: try this: `Some(8).filter(|&x| x != 0)`
++
++error: manual implementation of `Option::filter`
++  --> $DIR/manual_filter.rs:115:5
++   |
++LL | /     match Some(9) {
++LL | |         Some(x) => {
++LL | |             if x > 10 && x < 100 {
++LL | |                 Some(x)
++...  |
++LL | |         None => None,
++LL | |     };
++   | |_____^ help: try this: `Some(9).filter(|&x| x > 10 && x < 100)`
++
++error: manual implementation of `Option::filter`
++  --> $DIR/manual_filter.rs:141:5
++   |
++LL | /     match Some(11) {
++LL | |         // Lint, statement is preserved by `.filter`
++LL | |         Some(x) => {
++LL | |             if {
++...  |
++LL | |         None => None,
++LL | |     };
++   | |_____^
++   |
++help: try this
++   |
++LL ~     Some(11).filter(|&x| {
++LL +                 println!("foo");
++LL +                 x > 10 && x < 100
++LL ~             });
++   |
++
++error: manual implementation of `Option::filter`
++  --> $DIR/manual_filter.rs:185:13
++   |
++LL |       let _ = match Some(14) {
++   |  _____________^
++LL | |         Some(x) => {
++LL | |             if unsafe { f(x) } {
++LL | |                 Some(x)
++...  |
++LL | |         None => None,
++LL | |     };
++   | |_____^ help: try this: `Some(14).filter(|&x| unsafe { f(x) })`
++
++error: manual implementation of `Option::filter`
++  --> $DIR/manual_filter.rs:195:13
++   |
++LL |       let _ = match Some(15) {
++   |  _____________^
++LL | |         Some(x) => unsafe {
++LL | |             if f(x) { Some(x) } else { None }
++LL | |         },
++LL | |         None => None,
++LL | |     };
++   | |_____^ help: try this: `Some(15).filter(|&x| unsafe { f(x) })`
++
++error: manual implementation of `Option::filter`
++  --> $DIR/manual_filter.rs:205:12
++   |
++LL |       } else if let Some(x) = Some(16) {
++   |  ____________^
++LL | |         // Lint starting from here
++LL | |         if x % 2 == 0 { Some(x) } else { None }
++LL | |     } else {
++LL | |         None
++LL | |     };
++   | |_____^ help: try this: `{ Some(16).filter(|&x| x % 2 == 0) }`
++
++error: aborting due to 15 previous errors
++
index 5601c96c10b28fe4af39b4301a263bfd748d4946,0000000000000000000000000000000000000000..b942fbfe93056aa45c24dc06c12678db0be8a202
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,85 @@@
 +// run-rustfix
 +// aux-build:macro_rules.rs
 +
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::manual_rem_euclid)]
 +
 +#[macro_use]
 +extern crate macro_rules;
 +
 +macro_rules! internal_rem_euclid {
 +    () => {
 +        let value: i32 = 5;
 +        let _: i32 = value.rem_euclid(4);
 +    };
 +}
 +
 +fn main() {
 +    let value: i32 = 5;
 +
 +    let _: i32 = value.rem_euclid(4);
 +    let _: i32 = value.rem_euclid(4);
 +    let _: i32 = value.rem_euclid(4);
 +    let _: i32 = value.rem_euclid(4);
 +    let _: i32 = 1 + value.rem_euclid(4);
 +
 +    let _: i32 = (3 + value % 4) % 4;
 +    let _: i32 = (-4 + value % -4) % -4;
 +    let _: i32 = ((5 % 4) + 4) % 4;
 +
 +    // Make sure the lint does not trigger if it would cause an error, like with an ambiguous
 +    // integer type
 +    let not_annotated = 24;
 +    let _ = ((not_annotated % 4) + 4) % 4;
 +    let inferred: _ = 24;
 +    let _ = ((inferred % 4) + 4) % 4;
 +
 +    // For lint to apply the constant must always be on the RHS of the previous value for %
 +    let _: i32 = 4 % ((value % 4) + 4);
 +    let _: i32 = ((4 % value) + 4) % 4;
 +
 +    // Lint in internal macros
 +    internal_rem_euclid!();
 +
 +    // Do not lint in external macros
 +    manual_rem_euclid!();
 +}
 +
 +// Should lint for params too
 +pub fn rem_euclid_4(num: i32) -> i32 {
 +    num.rem_euclid(4)
 +}
 +
 +// Constant version came later, should still lint
 +pub const fn const_rem_euclid_4(num: i32) -> i32 {
 +    num.rem_euclid(4)
 +}
++
++pub fn msrv_1_37() {
++    #![clippy::msrv = "1.37"]
++
++    let x: i32 = 10;
++    let _: i32 = ((x % 4) + 4) % 4;
++}
++
++pub fn msrv_1_38() {
++    #![clippy::msrv = "1.38"]
++
++    let x: i32 = 10;
++    let _: i32 = x.rem_euclid(4);
++}
++
++// For const fns:
++pub const fn msrv_1_51() {
++    #![clippy::msrv = "1.51"]
++
++    let x: i32 = 10;
++    let _: i32 = ((x % 4) + 4) % 4;
++}
++
++pub const fn msrv_1_52() {
++    #![clippy::msrv = "1.52"]
++
++    let x: i32 = 10;
++    let _: i32 = x.rem_euclid(4);
++}
index 52135be26b73c290640ab8cde7eefb7dc0111dd5,0000000000000000000000000000000000000000..7462d532169f6fb890a4b62131821330fdd96b04
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,85 @@@
 +// run-rustfix
 +// aux-build:macro_rules.rs
 +
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::manual_rem_euclid)]
 +
 +#[macro_use]
 +extern crate macro_rules;
 +
 +macro_rules! internal_rem_euclid {
 +    () => {
 +        let value: i32 = 5;
 +        let _: i32 = ((value % 4) + 4) % 4;
 +    };
 +}
 +
 +fn main() {
 +    let value: i32 = 5;
 +
 +    let _: i32 = ((value % 4) + 4) % 4;
 +    let _: i32 = (4 + (value % 4)) % 4;
 +    let _: i32 = (value % 4 + 4) % 4;
 +    let _: i32 = (4 + value % 4) % 4;
 +    let _: i32 = 1 + (4 + value % 4) % 4;
 +
 +    let _: i32 = (3 + value % 4) % 4;
 +    let _: i32 = (-4 + value % -4) % -4;
 +    let _: i32 = ((5 % 4) + 4) % 4;
 +
 +    // Make sure the lint does not trigger if it would cause an error, like with an ambiguous
 +    // integer type
 +    let not_annotated = 24;
 +    let _ = ((not_annotated % 4) + 4) % 4;
 +    let inferred: _ = 24;
 +    let _ = ((inferred % 4) + 4) % 4;
 +
 +    // For lint to apply the constant must always be on the RHS of the previous value for %
 +    let _: i32 = 4 % ((value % 4) + 4);
 +    let _: i32 = ((4 % value) + 4) % 4;
 +
 +    // Lint in internal macros
 +    internal_rem_euclid!();
 +
 +    // Do not lint in external macros
 +    manual_rem_euclid!();
 +}
 +
 +// Should lint for params too
 +pub fn rem_euclid_4(num: i32) -> i32 {
 +    ((num % 4) + 4) % 4
 +}
 +
 +// Constant version came later, should still lint
 +pub const fn const_rem_euclid_4(num: i32) -> i32 {
 +    ((num % 4) + 4) % 4
 +}
++
++pub fn msrv_1_37() {
++    #![clippy::msrv = "1.37"]
++
++    let x: i32 = 10;
++    let _: i32 = ((x % 4) + 4) % 4;
++}
++
++pub fn msrv_1_38() {
++    #![clippy::msrv = "1.38"]
++
++    let x: i32 = 10;
++    let _: i32 = ((x % 4) + 4) % 4;
++}
++
++// For const fns:
++pub const fn msrv_1_51() {
++    #![clippy::msrv = "1.51"]
++
++    let x: i32 = 10;
++    let _: i32 = ((x % 4) + 4) % 4;
++}
++
++pub const fn msrv_1_52() {
++    #![clippy::msrv = "1.52"]
++
++    let x: i32 = 10;
++    let _: i32 = ((x % 4) + 4) % 4;
++}
index a237fd0213c1eeb187fea7ab79183d2b15950ee7,0000000000000000000000000000000000000000..d51bac03b565afaa278696da4742e04fc6a894da
mode 100644,000000..100644
--- /dev/null
@@@ -1,57 -1,0 +1,69 @@@
-   --> $DIR/manual_rem_euclid.rs:19:18
 +error: manual `rem_euclid` implementation
-   --> $DIR/manual_rem_euclid.rs:20:18
++  --> $DIR/manual_rem_euclid.rs:20:18
 +   |
 +LL |     let _: i32 = ((value % 4) + 4) % 4;
 +   |                  ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 +   |
 +   = note: `-D clippy::manual-rem-euclid` implied by `-D warnings`
 +
 +error: manual `rem_euclid` implementation
-   --> $DIR/manual_rem_euclid.rs:21:18
++  --> $DIR/manual_rem_euclid.rs:21:18
 +   |
 +LL |     let _: i32 = (4 + (value % 4)) % 4;
 +   |                  ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 +
 +error: manual `rem_euclid` implementation
-   --> $DIR/manual_rem_euclid.rs:22:18
++  --> $DIR/manual_rem_euclid.rs:22:18
 +   |
 +LL |     let _: i32 = (value % 4 + 4) % 4;
 +   |                  ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 +
 +error: manual `rem_euclid` implementation
-   --> $DIR/manual_rem_euclid.rs:23:22
++  --> $DIR/manual_rem_euclid.rs:23:18
 +   |
 +LL |     let _: i32 = (4 + value % 4) % 4;
 +   |                  ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 +
 +error: manual `rem_euclid` implementation
-   --> $DIR/manual_rem_euclid.rs:12:22
++  --> $DIR/manual_rem_euclid.rs:24:22
 +   |
 +LL |     let _: i32 = 1 + (4 + value % 4) % 4;
 +   |                      ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 +
 +error: manual `rem_euclid` implementation
-   --> $DIR/manual_rem_euclid.rs:49:5
++  --> $DIR/manual_rem_euclid.rs:13:22
 +   |
 +LL |         let _: i32 = ((value % 4) + 4) % 4;
 +   |                      ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 +...
 +LL |     internal_rem_euclid!();
 +   |     ---------------------- in this macro invocation
 +   |
 +   = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: manual `rem_euclid` implementation
-   --> $DIR/manual_rem_euclid.rs:54:5
++  --> $DIR/manual_rem_euclid.rs:50:5
 +   |
 +LL |     ((num % 4) + 4) % 4
 +   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
 +
 +error: manual `rem_euclid` implementation
- error: aborting due to 8 previous errors
++  --> $DIR/manual_rem_euclid.rs:55:5
 +   |
 +LL |     ((num % 4) + 4) % 4
 +   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
 +
++error: manual `rem_euclid` implementation
++  --> $DIR/manual_rem_euclid.rs:69:18
++   |
++LL |     let _: i32 = ((x % 4) + 4) % 4;
++   |                  ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
++
++error: manual `rem_euclid` implementation
++  --> $DIR/manual_rem_euclid.rs:84:18
++   |
++LL |     let _: i32 = ((x % 4) + 4) % 4;
++   |                  ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
++
++error: aborting due to 10 previous errors
 +
index cbb84eb5c7e3709411156d5c5483cdf1020fe567,0000000000000000000000000000000000000000..85009d78558ba7d68abd82b3b76680fad600adf7
mode 100644,000000..100644
--- /dev/null
@@@ -1,66 -1,0 +1,85 @@@
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::manual_strip)]
 +
 +fn main() {
 +    let s = "abc";
 +
 +    if s.starts_with("ab") {
 +        str::to_string(&s["ab".len()..]);
 +        s["ab".len()..].to_string();
 +
 +        str::to_string(&s[2..]);
 +        s[2..].to_string();
 +    }
 +
 +    if s.ends_with("bc") {
 +        str::to_string(&s[..s.len() - "bc".len()]);
 +        s[..s.len() - "bc".len()].to_string();
 +
 +        str::to_string(&s[..s.len() - 2]);
 +        s[..s.len() - 2].to_string();
 +    }
 +
 +    // Character patterns
 +    if s.starts_with('a') {
 +        str::to_string(&s[1..]);
 +        s[1..].to_string();
 +    }
 +
 +    // Variable prefix
 +    let prefix = "ab";
 +    if s.starts_with(prefix) {
 +        str::to_string(&s[prefix.len()..]);
 +    }
 +
 +    // Constant prefix
 +    const PREFIX: &str = "ab";
 +    if s.starts_with(PREFIX) {
 +        str::to_string(&s[PREFIX.len()..]);
 +        str::to_string(&s[2..]);
 +    }
 +
 +    // Constant target
 +    const TARGET: &str = "abc";
 +    if TARGET.starts_with(prefix) {
 +        str::to_string(&TARGET[prefix.len()..]);
 +    }
 +
 +    // String target - not mutated.
 +    let s1: String = "abc".into();
 +    if s1.starts_with("ab") {
 +        s1[2..].to_uppercase();
 +    }
 +
 +    // String target - mutated. (Don't lint.)
 +    let mut s2: String = "abc".into();
 +    if s2.starts_with("ab") {
 +        s2.push('d');
 +        s2[2..].to_uppercase();
 +    }
 +
 +    // Target not stripped. (Don't lint.)
 +    let s3 = String::from("abcd");
 +    let s4 = String::from("efgh");
 +    if s3.starts_with("ab") {
 +        s4[2..].to_string();
 +    }
 +}
++
++fn msrv_1_44() {
++    #![clippy::msrv = "1.44"]
++
++    let s = "abc";
++    if s.starts_with('a') {
++        s[1..].to_string();
++    }
++}
++
++fn msrv_1_45() {
++    #![clippy::msrv = "1.45"]
++
++    let s = "abc";
++    if s.starts_with('a') {
++        s[1..].to_string();
++    }
++}
index 2191ccb85dd5d86fcd8712d64d0da45e2d8bdb2f,0000000000000000000000000000000000000000..ad2a362f3e76ddb2d3d404952da30db254f8288b
mode 100644,000000..100644
--- /dev/null
@@@ -1,132 -1,0 +1,149 @@@
-   --> $DIR/manual_strip.rs:7:24
 +error: stripping a prefix manually
-   --> $DIR/manual_strip.rs:6:5
++  --> $DIR/manual_strip.rs:8:24
 +   |
 +LL |         str::to_string(&s["ab".len()..]);
 +   |                        ^^^^^^^^^^^^^^^^
 +   |
 +note: the prefix was tested here
-   --> $DIR/manual_strip.rs:15:24
++  --> $DIR/manual_strip.rs:7:5
 +   |
 +LL |     if s.starts_with("ab") {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^
 +   = note: `-D clippy::manual-strip` implied by `-D warnings`
 +help: try using the `strip_prefix` method
 +   |
 +LL ~     if let Some(<stripped>) = s.strip_prefix("ab") {
 +LL ~         str::to_string(<stripped>);
 +LL ~         <stripped>.to_string();
 +LL | 
 +LL ~         str::to_string(<stripped>);
 +LL ~         <stripped>.to_string();
 +   |
 +
 +error: stripping a suffix manually
-   --> $DIR/manual_strip.rs:14:5
++  --> $DIR/manual_strip.rs:16:24
 +   |
 +LL |         str::to_string(&s[..s.len() - "bc".len()]);
 +   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: the suffix was tested here
-   --> $DIR/manual_strip.rs:24:24
++  --> $DIR/manual_strip.rs:15:5
 +   |
 +LL |     if s.ends_with("bc") {
 +   |     ^^^^^^^^^^^^^^^^^^^^^
 +help: try using the `strip_suffix` method
 +   |
 +LL ~     if let Some(<stripped>) = s.strip_suffix("bc") {
 +LL ~         str::to_string(<stripped>);
 +LL ~         <stripped>.to_string();
 +LL | 
 +LL ~         str::to_string(<stripped>);
 +LL ~         <stripped>.to_string();
 +   |
 +
 +error: stripping a prefix manually
-   --> $DIR/manual_strip.rs:23:5
++  --> $DIR/manual_strip.rs:25:24
 +   |
 +LL |         str::to_string(&s[1..]);
 +   |                        ^^^^^^^
 +   |
 +note: the prefix was tested here
-   --> $DIR/manual_strip.rs:31:24
++  --> $DIR/manual_strip.rs:24:5
 +   |
 +LL |     if s.starts_with('a') {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^
 +help: try using the `strip_prefix` method
 +   |
 +LL ~     if let Some(<stripped>) = s.strip_prefix('a') {
 +LL ~         str::to_string(<stripped>);
 +LL ~         <stripped>.to_string();
 +   |
 +
 +error: stripping a prefix manually
-   --> $DIR/manual_strip.rs:30:5
++  --> $DIR/manual_strip.rs:32:24
 +   |
 +LL |         str::to_string(&s[prefix.len()..]);
 +   |                        ^^^^^^^^^^^^^^^^^^
 +   |
 +note: the prefix was tested here
-   --> $DIR/manual_strip.rs:37:24
++  --> $DIR/manual_strip.rs:31:5
 +   |
 +LL |     if s.starts_with(prefix) {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 +help: try using the `strip_prefix` method
 +   |
 +LL ~     if let Some(<stripped>) = s.strip_prefix(prefix) {
 +LL ~         str::to_string(<stripped>);
 +   |
 +
 +error: stripping a prefix manually
-   --> $DIR/manual_strip.rs:36:5
++  --> $DIR/manual_strip.rs:38:24
 +   |
 +LL |         str::to_string(&s[PREFIX.len()..]);
 +   |                        ^^^^^^^^^^^^^^^^^^
 +   |
 +note: the prefix was tested here
-   --> $DIR/manual_strip.rs:44:24
++  --> $DIR/manual_strip.rs:37:5
 +   |
 +LL |     if s.starts_with(PREFIX) {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 +help: try using the `strip_prefix` method
 +   |
 +LL ~     if let Some(<stripped>) = s.strip_prefix(PREFIX) {
 +LL ~         str::to_string(<stripped>);
 +LL ~         str::to_string(<stripped>);
 +   |
 +
 +error: stripping a prefix manually
-   --> $DIR/manual_strip.rs:43:5
++  --> $DIR/manual_strip.rs:45:24
 +   |
 +LL |         str::to_string(&TARGET[prefix.len()..]);
 +   |                        ^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: the prefix was tested here
-   --> $DIR/manual_strip.rs:50:9
++  --> $DIR/manual_strip.rs:44:5
 +   |
 +LL |     if TARGET.starts_with(prefix) {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +help: try using the `strip_prefix` method
 +   |
 +LL ~     if let Some(<stripped>) = TARGET.strip_prefix(prefix) {
 +LL ~         str::to_string(<stripped>);
 +   |
 +
 +error: stripping a prefix manually
-   --> $DIR/manual_strip.rs:49:5
++  --> $DIR/manual_strip.rs:51:9
 +   |
 +LL |         s1[2..].to_uppercase();
 +   |         ^^^^^^^
 +   |
 +note: the prefix was tested here
- error: aborting due to 7 previous errors
++  --> $DIR/manual_strip.rs:50:5
 +   |
 +LL |     if s1.starts_with("ab") {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^
 +help: try using the `strip_prefix` method
 +   |
 +LL ~     if let Some(<stripped>) = s1.strip_prefix("ab") {
 +LL ~         <stripped>.to_uppercase();
 +   |
 +
++error: stripping a prefix manually
++  --> $DIR/manual_strip.rs:83:9
++   |
++LL |         s[1..].to_string();
++   |         ^^^^^^
++   |
++note: the prefix was tested here
++  --> $DIR/manual_strip.rs:82:5
++   |
++LL |     if s.starts_with('a') {
++   |     ^^^^^^^^^^^^^^^^^^^^^^
++help: try using the `strip_prefix` method
++   |
++LL ~     if let Some(<stripped>) = s.strip_prefix('a') {
++LL ~         <stripped>.to_string();
++   |
++
++error: aborting due to 8 previous errors
 +
index 5429fb4e454e3b429bf2f5d895b8a602d4da2d49,0000000000000000000000000000000000000000..396b22a9abb390f520d911a773d688b8bb2f3992
mode 100644,000000..100644
--- /dev/null
@@@ -1,81 -1,0 +1,99 @@@
- #![allow(clippy::uninlined_format_args)]
 +// aux-build:option_helpers.rs
++
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::map_unwrap_or)]
++#![allow(clippy::uninlined_format_args, clippy::unnecessary_lazy_evaluations)]
 +
 +#[macro_use]
 +extern crate option_helpers;
 +
 +use std::collections::HashMap;
 +
 +#[rustfmt::skip]
 +fn option_methods() {
 +    let opt = Some(1);
 +
 +    // Check for `option.map(_).unwrap_or(_)` use.
 +    // Single line case.
 +    let _ = opt.map(|x| x + 1)
 +        // Should lint even though this call is on a separate line.
 +        .unwrap_or(0);
 +    // Multi-line cases.
 +    let _ = opt.map(|x| {
 +        x + 1
 +    }
 +    ).unwrap_or(0);
 +    let _ = opt.map(|x| x + 1)
 +        .unwrap_or({
 +            0
 +        });
 +    // Single line `map(f).unwrap_or(None)` case.
 +    let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
 +    // Multi-line `map(f).unwrap_or(None)` cases.
 +    let _ = opt.map(|x| {
 +        Some(x + 1)
 +    }
 +    ).unwrap_or(None);
 +    let _ = opt
 +        .map(|x| Some(x + 1))
 +        .unwrap_or(None);
 +    // macro case
 +    let _ = opt_map!(opt, |x| x + 1).unwrap_or(0); // should not lint
 +
 +    // Should not lint if not copyable
 +    let id: String = "identifier".to_string();
 +    let _ = Some("prefix").map(|p| format!("{}.{}", p, id)).unwrap_or(id);
 +    // ...but DO lint if the `unwrap_or` argument is not used in the `map`
 +    let id: String = "identifier".to_string();
 +    let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
 +
 +    // Check for `option.map(_).unwrap_or_else(_)` use.
 +    // Multi-line cases.
 +    let _ = opt.map(|x| {
 +        x + 1
 +    }
 +    ).unwrap_or_else(|| 0);
 +    let _ = opt.map(|x| x + 1)
 +        .unwrap_or_else(||
 +            0
 +        );
 +}
 +
 +#[rustfmt::skip]
 +fn result_methods() {
 +    let res: Result<i32, ()> = Ok(1);
 +
 +    // Check for `result.map(_).unwrap_or_else(_)` use.
 +    // multi line cases
 +    let _ = res.map(|x| {
 +        x + 1
 +    }
 +    ).unwrap_or_else(|_e| 0);
 +    let _ = res.map(|x| x + 1)
 +        .unwrap_or_else(|_e| {
 +            0
 +        });
 +    // macro case
 +    let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint
 +}
 +
 +fn main() {
 +    option_methods();
 +    result_methods();
 +}
++
++fn msrv_1_40() {
++    #![clippy::msrv = "1.40"]
++
++    let res: Result<i32, ()> = Ok(1);
++
++    let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0);
++}
++
++fn msrv_1_41() {
++    #![clippy::msrv = "1.41"]
++
++    let res: Result<i32, ()> = Ok(1);
++
++    let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0);
++}
index abc9c1ece327aaec9831d88941c4cd05c6ea9ae7,0000000000000000000000000000000000000000..d17d24a403ea5d578c01d51f725559553f2d75ee
mode 100644,000000..100644
--- /dev/null
@@@ -1,150 -1,0 +1,156 @@@
-   --> $DIR/map_unwrap_or.rs:16:13
 +error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
-   --> $DIR/map_unwrap_or.rs:20:13
++  --> $DIR/map_unwrap_or.rs:18:13
 +   |
 +LL |       let _ = opt.map(|x| x + 1)
 +   |  _____________^
 +LL | |         // Should lint even though this call is on a separate line.
 +LL | |         .unwrap_or(0);
 +   | |_____________________^
 +   |
 +   = note: `-D clippy::map-unwrap-or` implied by `-D warnings`
 +help: use `map_or(<a>, <f>)` instead
 +   |
 +LL -     let _ = opt.map(|x| x + 1)
 +LL +     let _ = opt.map_or(0, |x| x + 1);
 +   |
 +
 +error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
-   --> $DIR/map_unwrap_or.rs:24:13
++  --> $DIR/map_unwrap_or.rs:22:13
 +   |
 +LL |       let _ = opt.map(|x| {
 +   |  _____________^
 +LL | |         x + 1
 +LL | |     }
 +LL | |     ).unwrap_or(0);
 +   | |__________________^
 +   |
 +help: use `map_or(<a>, <f>)` instead
 +   |
 +LL ~     let _ = opt.map_or(0, |x| {
 +LL |         x + 1
 +LL |     }
 +LL ~     );
 +   |
 +
 +error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
-   --> $DIR/map_unwrap_or.rs:29:13
++  --> $DIR/map_unwrap_or.rs:26:13
 +   |
 +LL |       let _ = opt.map(|x| x + 1)
 +   |  _____________^
 +LL | |         .unwrap_or({
 +LL | |             0
 +LL | |         });
 +   | |__________^
 +   |
 +help: use `map_or(<a>, <f>)` instead
 +   |
 +LL ~     let _ = opt.map_or({
 +LL +             0
 +LL ~         }, |x| x + 1);
 +   |
 +
 +error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
-   --> $DIR/map_unwrap_or.rs:31:13
++  --> $DIR/map_unwrap_or.rs:31:13
 +   |
 +LL |     let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: use `and_then(<f>)` instead
 +   |
 +LL -     let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
 +LL +     let _ = opt.and_then(|x| Some(x + 1));
 +   |
 +
 +error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
-   --> $DIR/map_unwrap_or.rs:35:13
++  --> $DIR/map_unwrap_or.rs:33:13
 +   |
 +LL |       let _ = opt.map(|x| {
 +   |  _____________^
 +LL | |         Some(x + 1)
 +LL | |     }
 +LL | |     ).unwrap_or(None);
 +   | |_____________________^
 +   |
 +help: use `and_then(<f>)` instead
 +   |
 +LL ~     let _ = opt.and_then(|x| {
 +LL |         Some(x + 1)
 +LL |     }
 +LL ~     );
 +   |
 +
 +error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
-   --> $DIR/map_unwrap_or.rs:46:13
++  --> $DIR/map_unwrap_or.rs:37:13
 +   |
 +LL |       let _ = opt
 +   |  _____________^
 +LL | |         .map(|x| Some(x + 1))
 +LL | |         .unwrap_or(None);
 +   | |________________________^
 +   |
 +help: use `and_then(<f>)` instead
 +   |
 +LL -         .map(|x| Some(x + 1))
 +LL +         .and_then(|x| Some(x + 1));
 +   |
 +
 +error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
-   --> $DIR/map_unwrap_or.rs:50:13
++  --> $DIR/map_unwrap_or.rs:48:13
 +   |
 +LL |     let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: use `map_or(<a>, <f>)` instead
 +   |
 +LL -     let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
 +LL +     let _ = Some("prefix").map_or(id, |p| format!("{}.", p));
 +   |
 +
 +error: called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead
-   --> $DIR/map_unwrap_or.rs:54:13
++  --> $DIR/map_unwrap_or.rs:52:13
 +   |
 +LL |       let _ = opt.map(|x| {
 +   |  _____________^
 +LL | |         x + 1
 +LL | |     }
 +LL | |     ).unwrap_or_else(|| 0);
 +   | |__________________________^
 +
 +error: called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead
-   --> $DIR/map_unwrap_or.rs:66:13
++  --> $DIR/map_unwrap_or.rs:56:13
 +   |
 +LL |       let _ = opt.map(|x| x + 1)
 +   |  _____________^
 +LL | |         .unwrap_or_else(||
 +LL | |             0
 +LL | |         );
 +   | |_________^
 +
 +error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
-   --> $DIR/map_unwrap_or.rs:70:13
++  --> $DIR/map_unwrap_or.rs:68:13
 +   |
 +LL |       let _ = res.map(|x| {
 +   |  _____________^
 +LL | |         x + 1
 +LL | |     }
 +LL | |     ).unwrap_or_else(|_e| 0);
 +   | |____________________________^
 +
 +error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
- error: aborting due to 11 previous errors
++  --> $DIR/map_unwrap_or.rs:72:13
 +   |
 +LL |       let _ = res.map(|x| x + 1)
 +   |  _____________^
 +LL | |         .unwrap_or_else(|_e| {
 +LL | |             0
 +LL | |         });
 +   | |__________^
 +
++error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
++  --> $DIR/map_unwrap_or.rs:98:13
++   |
++LL |     let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)`
++
++error: aborting due to 12 previous errors
 +
index 95ca571d07bfbf88fcce5d626814d6f210156ee2,0000000000000000000000000000000000000000..2498007694c560ec3c32679db92621ef1fdefbb4
mode 100644,000000..100644
--- /dev/null
@@@ -1,195 -1,0 +1,211 @@@
 +// run-rustfix
 +
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::match_like_matches_macro)]
 +#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)]
 +
 +fn main() {
 +    let x = Some(5);
 +
 +    // Lint
 +    let _y = matches!(x, Some(0));
 +
 +    // Lint
 +    let _w = matches!(x, Some(_));
 +
 +    // Turn into is_none
 +    let _z = x.is_none();
 +
 +    // Lint
 +    let _zz = !matches!(x, Some(r) if r == 0);
 +
 +    // Lint
 +    let _zzz = matches!(x, Some(5));
 +
 +    // No lint
 +    let _a = match x {
 +        Some(_) => false,
 +        _ => false,
 +    };
 +
 +    // No lint
 +    let _ab = match x {
 +        Some(0) => false,
 +        _ => true,
 +        None => false,
 +    };
 +
 +    enum E {
 +        A(u32),
 +        B(i32),
 +        C,
 +        D,
 +    }
 +    let x = E::A(2);
 +    {
 +        // lint
 +        let _ans = matches!(x, E::A(_) | E::B(_));
 +    }
 +    {
 +        // lint
 +        // skip rustfmt to prevent removing block for first pattern
 +        #[rustfmt::skip]
 +        let _ans = matches!(x, E::A(_) | E::B(_));
 +    }
 +    {
 +        // lint
 +        let _ans = !matches!(x, E::B(_) | E::C);
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => false,
 +            E::C => true,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => true,
 +            E::B(_) => false,
 +            E::C => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) if a < 10 => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) => a == 10,
 +            E::B(_) => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        // should print "z" in suggestion (#6503)
 +        let z = &Some(3);
 +        let _z = matches!(z, Some(3));
 +    }
 +
 +    {
 +        // this could also print "z" in suggestion..?
 +        let z = Some(3);
 +        let _z = matches!(&z, Some(3));
 +    }
 +
 +    {
 +        enum AnEnum {
 +            X,
 +            Y,
 +        }
 +
 +        fn foo(_x: AnEnum) {}
 +
 +        fn main() {
 +            let z = AnEnum::X;
 +            // we can't remove the reference here!
 +            let _ = matches!(&z, AnEnum::X);
 +            foo(z);
 +        }
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        // we need the reference here because later val is consumed by fun()
 +        let _res = matches!(&val, &Some(ref _a));
 +        fun(val);
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        let _res = matches!(&val, &Some(ref _a));
 +        fun(val);
 +    }
 +
 +    {
 +        enum E {
 +            A,
 +            B,
 +            C,
 +        }
 +
 +        let _ = match E::A {
 +            E::B => true,
 +            #[cfg(feature = "foo")]
 +            E::A => true,
 +            _ => false,
 +        };
 +    }
 +
 +    let x = ' ';
 +    // ignore if match block contains comment
 +    let _line_comments = match x {
 +        // numbers are bad!
 +        '1' | '2' | '3' => true,
 +        // spaces are very important to be true.
 +        ' ' => true,
 +        // as are dots
 +        '.' => true,
 +        _ => false,
 +    };
 +
 +    let _block_comments = match x {
 +        /* numbers are bad!
 +         */
 +        '1' | '2' | '3' => true,
 +        /* spaces are very important to be true.
 +         */
 +        ' ' => true,
 +        /* as are dots
 +         */
 +        '.' => true,
 +        _ => false,
 +    };
 +}
++
++fn msrv_1_41() {
++    #![clippy::msrv = "1.41"]
++
++    let _y = match Some(5) {
++        Some(0) => true,
++        _ => false,
++    };
++}
++
++fn msrv_1_42() {
++    #![clippy::msrv = "1.42"]
++
++    let _y = matches!(Some(5), Some(0));
++}
index 3b9c8cadadcc417ebbe731a3735dd98580837e46,0000000000000000000000000000000000000000..b4e48499bd0fb193fca123c306f129d24543e904
mode 100644,000000..100644
--- /dev/null
@@@ -1,236 -1,0 +1,255 @@@
 +// run-rustfix
 +
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::match_like_matches_macro)]
 +#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)]
 +
 +fn main() {
 +    let x = Some(5);
 +
 +    // Lint
 +    let _y = match x {
 +        Some(0) => true,
 +        _ => false,
 +    };
 +
 +    // Lint
 +    let _w = match x {
 +        Some(_) => true,
 +        _ => false,
 +    };
 +
 +    // Turn into is_none
 +    let _z = match x {
 +        Some(_) => false,
 +        None => true,
 +    };
 +
 +    // Lint
 +    let _zz = match x {
 +        Some(r) if r == 0 => false,
 +        _ => true,
 +    };
 +
 +    // Lint
 +    let _zzz = if let Some(5) = x { true } else { false };
 +
 +    // No lint
 +    let _a = match x {
 +        Some(_) => false,
 +        _ => false,
 +    };
 +
 +    // No lint
 +    let _ab = match x {
 +        Some(0) => false,
 +        _ => true,
 +        None => false,
 +    };
 +
 +    enum E {
 +        A(u32),
 +        B(i32),
 +        C,
 +        D,
 +    }
 +    let x = E::A(2);
 +    {
 +        // lint
 +        let _ans = match x {
 +            E::A(_) => true,
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
 +    {
 +        // lint
 +        // skip rustfmt to prevent removing block for first pattern
 +        #[rustfmt::skip]
 +        let _ans = match x {
 +            E::A(_) => {
 +                true
 +            }
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
 +    {
 +        // lint
 +        let _ans = match x {
 +            E::B(_) => false,
 +            E::C => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => false,
 +            E::C => true,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => true,
 +            E::B(_) => false,
 +            E::C => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) if a < 10 => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) => a == 10,
 +            E::B(_) => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        // should print "z" in suggestion (#6503)
 +        let z = &Some(3);
 +        let _z = match &z {
 +            Some(3) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        // this could also print "z" in suggestion..?
 +        let z = Some(3);
 +        let _z = match &z {
 +            Some(3) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        enum AnEnum {
 +            X,
 +            Y,
 +        }
 +
 +        fn foo(_x: AnEnum) {}
 +
 +        fn main() {
 +            let z = AnEnum::X;
 +            // we can't remove the reference here!
 +            let _ = match &z {
 +                AnEnum::X => true,
 +                _ => false,
 +            };
 +            foo(z);
 +        }
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        // we need the reference here because later val is consumed by fun()
 +        let _res = match &val {
 +            &Some(ref _a) => true,
 +            _ => false,
 +        };
 +        fun(val);
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        let _res = match &val {
 +            &Some(ref _a) => true,
 +            _ => false,
 +        };
 +        fun(val);
 +    }
 +
 +    {
 +        enum E {
 +            A,
 +            B,
 +            C,
 +        }
 +
 +        let _ = match E::A {
 +            E::B => true,
 +            #[cfg(feature = "foo")]
 +            E::A => true,
 +            _ => false,
 +        };
 +    }
 +
 +    let x = ' ';
 +    // ignore if match block contains comment
 +    let _line_comments = match x {
 +        // numbers are bad!
 +        '1' | '2' | '3' => true,
 +        // spaces are very important to be true.
 +        ' ' => true,
 +        // as are dots
 +        '.' => true,
 +        _ => false,
 +    };
 +
 +    let _block_comments = match x {
 +        /* numbers are bad!
 +         */
 +        '1' | '2' | '3' => true,
 +        /* spaces are very important to be true.
 +         */
 +        ' ' => true,
 +        /* as are dots
 +         */
 +        '.' => true,
 +        _ => false,
 +    };
 +}
++
++fn msrv_1_41() {
++    #![clippy::msrv = "1.41"]
++
++    let _y = match Some(5) {
++        Some(0) => true,
++        _ => false,
++    };
++}
++
++fn msrv_1_42() {
++    #![clippy::msrv = "1.42"]
++
++    let _y = match Some(5) {
++        Some(0) => true,
++        _ => false,
++    };
++}
index e94555e27448b0d131c6e34d6d0cb5bdac330b4e,0000000000000000000000000000000000000000..f1d1c23aeb0de7b477a497e536a66e8bd6dcb2b4
mode 100644,000000..100644
--- /dev/null
@@@ -1,137 -1,0 +1,147 @@@
-   --> $DIR/match_expr_like_matches_macro.rs:10:14
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:16:14
++  --> $DIR/match_expr_like_matches_macro.rs:11:14
 +   |
 +LL |       let _y = match x {
 +   |  ______________^
 +LL | |         Some(0) => true,
 +LL | |         _ => false,
 +LL | |     };
 +   | |_____^ help: try this: `matches!(x, Some(0))`
 +   |
 +   = note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
 +
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:22:14
++  --> $DIR/match_expr_like_matches_macro.rs:17:14
 +   |
 +LL |       let _w = match x {
 +   |  ______________^
 +LL | |         Some(_) => true,
 +LL | |         _ => false,
 +LL | |     };
 +   | |_____^ help: try this: `matches!(x, Some(_))`
 +
 +error: redundant pattern matching, consider using `is_none()`
-   --> $DIR/match_expr_like_matches_macro.rs:28:15
++  --> $DIR/match_expr_like_matches_macro.rs:23:14
 +   |
 +LL |       let _z = match x {
 +   |  ______________^
 +LL | |         Some(_) => false,
 +LL | |         None => true,
 +LL | |     };
 +   | |_____^ help: try this: `x.is_none()`
 +   |
 +   = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
 +
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:34:16
++  --> $DIR/match_expr_like_matches_macro.rs:29:15
 +   |
 +LL |       let _zz = match x {
 +   |  _______________^
 +LL | |         Some(r) if r == 0 => false,
 +LL | |         _ => true,
 +LL | |     };
 +   | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)`
 +
 +error: if let .. else expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:58:20
++  --> $DIR/match_expr_like_matches_macro.rs:35:16
 +   |
 +LL |     let _zzz = if let Some(5) = x { true } else { false };
 +   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))`
 +
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:68:20
++  --> $DIR/match_expr_like_matches_macro.rs:59:20
 +   |
 +LL |           let _ans = match x {
 +   |  ____________________^
 +LL | |             E::A(_) => true,
 +LL | |             E::B(_) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
 +
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:78:20
++  --> $DIR/match_expr_like_matches_macro.rs:69:20
 +   |
 +LL |           let _ans = match x {
 +   |  ____________________^
 +LL | |             E::A(_) => {
 +LL | |                 true
 +LL | |             }
 +LL | |             E::B(_) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
 +
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:138:18
++  --> $DIR/match_expr_like_matches_macro.rs:79:20
 +   |
 +LL |           let _ans = match x {
 +   |  ____________________^
 +LL | |             E::B(_) => false,
 +LL | |             E::C => false,
 +LL | |             _ => true,
 +LL | |         };
 +   | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)`
 +
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:147:18
++  --> $DIR/match_expr_like_matches_macro.rs:139:18
 +   |
 +LL |           let _z = match &z {
 +   |  __________________^
 +LL | |             Some(3) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(z, Some(3))`
 +
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:164:21
++  --> $DIR/match_expr_like_matches_macro.rs:148:18
 +   |
 +LL |           let _z = match &z {
 +   |  __________________^
 +LL | |             Some(3) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(&z, Some(3))`
 +
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:178:20
++  --> $DIR/match_expr_like_matches_macro.rs:165:21
 +   |
 +LL |               let _ = match &z {
 +   |  _____________________^
 +LL | |                 AnEnum::X => true,
 +LL | |                 _ => false,
 +LL | |             };
 +   | |_____________^ help: try this: `matches!(&z, AnEnum::X)`
 +
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:190:20
++  --> $DIR/match_expr_like_matches_macro.rs:179:20
 +   |
 +LL |           let _res = match &val {
 +   |  ____________________^
 +LL | |             &Some(ref _a) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(&val, &Some(ref _a))`
 +
 +error: match expression looks like `matches!` macro
- error: aborting due to 13 previous errors
++  --> $DIR/match_expr_like_matches_macro.rs:191:20
 +   |
 +LL |           let _res = match &val {
 +   |  ____________________^
 +LL | |             &Some(ref _a) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(&val, &Some(ref _a))`
 +
++error: match expression looks like `matches!` macro
++  --> $DIR/match_expr_like_matches_macro.rs:251:14
++   |
++LL |       let _y = match Some(5) {
++   |  ______________^
++LL | |         Some(0) => true,
++LL | |         _ => false,
++LL | |     };
++   | |_____^ help: try this: `matches!(Some(5), Some(0))`
++
++error: aborting due to 14 previous errors
 +
index 22b04b208f87b09648234f72a143bdcb5fdcd2eb,0000000000000000000000000000000000000000..b4097fa96045e8bba47501bc2714557a1f55ba39
mode 100644,000000..100644
--- /dev/null
@@@ -1,135 -1,0 +1,134 @@@
 +#![feature(exclusive_range_pattern)]
 +#![warn(clippy::match_overlapping_arm)]
 +#![allow(clippy::redundant_pattern_matching)]
 +#![allow(clippy::if_same_then_else, clippy::equatable_if_let)]
 +
 +/// Tests for match_overlapping_arm
 +
 +fn overlapping() {
 +    const FOO: u64 = 2;
 +
 +    match 42 {
 +        0..=10 => println!("0..=10"),
 +        0..=11 => println!("0..=11"),
 +        _ => (),
 +    }
 +
 +    match 42 {
 +        0..=5 => println!("0..=5"),
 +        6..=7 => println!("6..=7"),
 +        FOO..=11 => println!("FOO..=11"),
 +        _ => (),
 +    }
 +
 +    match 42 {
 +        2 => println!("2"),
 +        0..=5 => println!("0..=5"),
 +        _ => (),
 +    }
 +
 +    match 42 {
 +        2 => println!("2"),
 +        0..=2 => println!("0..=2"),
 +        _ => (),
 +    }
 +
 +    match 42 {
 +        0..=10 => println!("0..=10"),
 +        11..=50 => println!("11..=50"),
 +        _ => (),
 +    }
 +
 +    match 42 {
 +        2 => println!("2"),
 +        0..2 => println!("0..2"),
 +        _ => (),
 +    }
 +
 +    match 42 {
 +        0..10 => println!("0..10"),
 +        10..50 => println!("10..50"),
 +        _ => (),
 +    }
 +
 +    match 42 {
 +        0..11 => println!("0..11"),
 +        0..=11 => println!("0..=11"),
 +        _ => (),
 +    }
 +
 +    match 42 {
 +        5..7 => println!("5..7"),
 +        0..10 => println!("0..10"),
 +        _ => (),
 +    }
 +
 +    match 42 {
 +        5..10 => println!("5..10"),
 +        0..=10 => println!("0..=10"),
 +        _ => (),
 +    }
 +
 +    match 42 {
 +        0..14 => println!("0..14"),
 +        5..10 => println!("5..10"),
 +        _ => (),
 +    }
 +
 +    match 42 {
 +        5..14 => println!("5..14"),
 +        0..=10 => println!("0..=10"),
 +        _ => (),
 +    }
 +
 +    match 42 {
 +        0..7 => println!("0..7"),
 +        0..=10 => println!("0..=10"),
 +        _ => (),
 +    }
 +
 +    match 42 {
 +        3.. => println!("3.."),
 +        0.. => println!("0.."),
 +        _ => (),
 +    }
 +
 +    match 42 {
 +        ..=23 => println!("..=23"),
 +        ..26 => println!("..26"),
 +        _ => (),
 +    }
 +
 +    // Issue #7816 - overlap after included range
 +    match 42 {
 +        5..=10 => (),
 +        0..=20 => (),
 +        21..=30 => (),
 +        21..=40 => (),
 +        _ => (),
 +    }
 +
 +    // Issue #7829
 +    match 0 {
 +        -1..=1 => (),
 +        -2..=2 => (),
 +        _ => (),
 +    }
 +
 +    // Only warn about the first if there are multiple overlaps
 +    match 42u128 {
 +        0..=0x0000_0000_0000_00ff => (),
 +        0..=0x0000_0000_0000_ffff => (),
 +        0..=0x0000_0000_ffff_ffff => (),
 +        0..=0xffff_ffff_ffff_ffff => (),
 +        _ => (),
 +    }
 +
 +    if let None = Some(42) {
 +        // nothing
 +    } else if let None = Some(42) {
 +        // another nothing :-)
 +    }
 +}
 +
 +fn main() {}
index a72becbeb669bb739cb1b09eeb26ce8c4fa48573,0000000000000000000000000000000000000000..b98d4799e42cad2c4370b4635f408cdc43bcb646
mode 100644,000000..100644
--- /dev/null
@@@ -1,99 -1,0 +1,99 @@@
-   --> $DIR/match_overlapping_arm.rs:13:9
 +error: some ranges overlap
-   --> $DIR/match_overlapping_arm.rs:14:9
++  --> $DIR/match_overlapping_arm.rs:12:9
 +   |
 +LL |         0..=10 => println!("0..=10"),
 +   |         ^^^^^^
 +   |
 +note: overlaps with this
-   --> $DIR/match_overlapping_arm.rs:19:9
++  --> $DIR/match_overlapping_arm.rs:13:9
 +   |
 +LL |         0..=11 => println!("0..=11"),
 +   |         ^^^^^^
 +   = note: `-D clippy::match-overlapping-arm` implied by `-D warnings`
 +
 +error: some ranges overlap
-   --> $DIR/match_overlapping_arm.rs:21:9
++  --> $DIR/match_overlapping_arm.rs:18:9
 +   |
 +LL |         0..=5 => println!("0..=5"),
 +   |         ^^^^^
 +   |
 +note: overlaps with this
-   --> $DIR/match_overlapping_arm.rs:56:9
++  --> $DIR/match_overlapping_arm.rs:20:9
 +   |
 +LL |         FOO..=11 => println!("FOO..=11"),
 +   |         ^^^^^^^^
 +
 +error: some ranges overlap
-   --> $DIR/match_overlapping_arm.rs:57:9
++  --> $DIR/match_overlapping_arm.rs:55:9
 +   |
 +LL |         0..11 => println!("0..11"),
 +   |         ^^^^^
 +   |
 +note: overlaps with this
-   --> $DIR/match_overlapping_arm.rs:81:9
++  --> $DIR/match_overlapping_arm.rs:56:9
 +   |
 +LL |         0..=11 => println!("0..=11"),
 +   |         ^^^^^^
 +
 +error: some ranges overlap
-   --> $DIR/match_overlapping_arm.rs:80:9
++  --> $DIR/match_overlapping_arm.rs:80:9
 +   |
 +LL |         0..=10 => println!("0..=10"),
 +   |         ^^^^^^
 +   |
 +note: overlaps with this
-   --> $DIR/match_overlapping_arm.rs:86:9
++  --> $DIR/match_overlapping_arm.rs:79:9
 +   |
 +LL |         5..14 => println!("5..14"),
 +   |         ^^^^^
 +
 +error: some ranges overlap
-   --> $DIR/match_overlapping_arm.rs:87:9
++  --> $DIR/match_overlapping_arm.rs:85:9
 +   |
 +LL |         0..7 => println!("0..7"),
 +   |         ^^^^
 +   |
 +note: overlaps with this
-   --> $DIR/match_overlapping_arm.rs:98:9
++  --> $DIR/match_overlapping_arm.rs:86:9
 +   |
 +LL |         0..=10 => println!("0..=10"),
 +   |         ^^^^^^
 +
 +error: some ranges overlap
-   --> $DIR/match_overlapping_arm.rs:99:9
++  --> $DIR/match_overlapping_arm.rs:97:9
 +   |
 +LL |         ..=23 => println!("..=23"),
 +   |         ^^^^^
 +   |
 +note: overlaps with this
-   --> $DIR/match_overlapping_arm.rs:107:9
++  --> $DIR/match_overlapping_arm.rs:98:9
 +   |
 +LL |         ..26 => println!("..26"),
 +   |         ^^^^
 +
 +error: some ranges overlap
-   --> $DIR/match_overlapping_arm.rs:108:9
++  --> $DIR/match_overlapping_arm.rs:106:9
 +   |
 +LL |         21..=30 => (),
 +   |         ^^^^^^^
 +   |
 +note: overlaps with this
-   --> $DIR/match_overlapping_arm.rs:121:9
++  --> $DIR/match_overlapping_arm.rs:107:9
 +   |
 +LL |         21..=40 => (),
 +   |         ^^^^^^^
 +
 +error: some ranges overlap
-   --> $DIR/match_overlapping_arm.rs:122:9
++  --> $DIR/match_overlapping_arm.rs:120:9
 +   |
 +LL |         0..=0x0000_0000_0000_00ff => (),
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: overlaps with this
++  --> $DIR/match_overlapping_arm.rs:121:9
 +   |
 +LL |         0..=0x0000_0000_0000_ffff => (),
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: aborting due to 8 previous errors
 +
index 951f552eb32b63adb3dc3bd95d6988b4ef88c664,0000000000000000000000000000000000000000..a6e315e4773a27907382516d8e37efe5d3d8476b
mode 100644,000000..100644
--- /dev/null
@@@ -1,126 -1,0 +1,135 @@@
 +// run-rustfix
 +#![warn(clippy::match_single_binding)]
 +#![allow(unused_variables)]
 +#![allow(clippy::toplevel_ref_arg, clippy::uninlined_format_args)]
 +
 +struct Point {
 +    x: i32,
 +    y: i32,
 +}
 +
 +fn coords() -> Point {
 +    Point { x: 1, y: 2 }
 +}
 +
 +macro_rules! foo {
 +    ($param:expr) => {
 +        match $param {
 +            _ => println!("whatever"),
 +        }
 +    };
 +}
 +
 +fn main() {
 +    let a = 1;
 +    let b = 2;
 +    let c = 3;
 +    // Lint
 +    let (x, y, z) = (a, b, c);
 +    {
 +        println!("{} {} {}", x, y, z);
 +    }
 +    // Lint
 +    let (x, y, z) = (a, b, c);
 +    println!("{} {} {}", x, y, z);
 +    // Ok
 +    foo!(a);
 +    // Ok
 +    match a {
 +        2 => println!("2"),
 +        _ => println!("Not 2"),
 +    }
 +    // Ok
 +    let d = Some(5);
 +    match d {
 +        Some(d) => println!("{}", d),
 +        _ => println!("None"),
 +    }
 +    // Lint
 +    println!("whatever");
 +    // Lint
 +    {
 +        let x = 29;
 +        println!("x has a value of {}", x);
 +    }
 +    // Lint
 +    {
 +        let e = 5 * a;
 +        if e >= 5 {
 +            println!("e is superior to 5");
 +        }
 +    }
 +    // Lint
 +    let p = Point { x: 0, y: 7 };
 +    let Point { x, y } = p;
 +    println!("Coords: ({}, {})", x, y);
 +    // Lint
 +    let Point { x: x1, y: y1 } = p;
 +    println!("Coords: ({}, {})", x1, y1);
 +    // Lint
 +    let x = 5;
 +    let ref r = x;
 +    println!("Got a reference to {}", r);
 +    // Lint
 +    let mut x = 5;
 +    let ref mut mr = x;
 +    println!("Got a mutable reference to {}", mr);
 +    // Lint
 +    let Point { x, y } = coords();
 +    let product = x * y;
 +    // Lint
 +    let v = vec![Some(1), Some(2), Some(3), Some(4)];
 +    #[allow(clippy::let_and_return)]
 +    let _ = v
 +        .iter()
 +        .map(|i| {
 +            let unwrapped = i.unwrap();
 +            unwrapped
 +        })
 +        .collect::<Vec<u8>>();
 +    // Ok
 +    let x = 1;
 +    match x {
 +        #[cfg(disabled_feature)]
 +        0 => println!("Disabled branch"),
 +        _ => println!("Enabled branch"),
 +    }
 +
 +    // Ok
 +    let x = 1;
 +    let y = 1;
 +    match match y {
 +        0 => 1,
 +        _ => 2,
 +    } {
 +        #[cfg(disabled_feature)]
 +        0 => println!("Array index start"),
 +        _ => println!("Not an array index start"),
 +    }
 +
 +    // Lint
 +    let x = 1;
 +    println!("Not an array index start");
 +}
 +
 +#[allow(dead_code)]
 +fn issue_8723() {
 +    let (mut val, idx) = ("a b", 1);
 +
 +    let (pre, suf) = val.split_at(idx);
 +    val = {
 +        println!("{}", pre);
 +        suf
 +    };
 +
 +    let _ = val;
 +}
++
++#[allow(dead_code)]
++fn issue_9575() {
++    fn side_effects() {}
++    let _ = || {
++        side_effects();
++        println!("Needs curlies");
++    };
++}
index 19c0fee8fd688aa15942a326ddfa4475b3fbc04a,0000000000000000000000000000000000000000..cecbd703e56698cd9292ea24de374cffa03b3e47
mode 100644,000000..100644
--- /dev/null
@@@ -1,142 -1,0 +1,150 @@@
 +// run-rustfix
 +#![warn(clippy::match_single_binding)]
 +#![allow(unused_variables)]
 +#![allow(clippy::toplevel_ref_arg, clippy::uninlined_format_args)]
 +
 +struct Point {
 +    x: i32,
 +    y: i32,
 +}
 +
 +fn coords() -> Point {
 +    Point { x: 1, y: 2 }
 +}
 +
 +macro_rules! foo {
 +    ($param:expr) => {
 +        match $param {
 +            _ => println!("whatever"),
 +        }
 +    };
 +}
 +
 +fn main() {
 +    let a = 1;
 +    let b = 2;
 +    let c = 3;
 +    // Lint
 +    match (a, b, c) {
 +        (x, y, z) => {
 +            println!("{} {} {}", x, y, z);
 +        },
 +    }
 +    // Lint
 +    match (a, b, c) {
 +        (x, y, z) => println!("{} {} {}", x, y, z),
 +    }
 +    // Ok
 +    foo!(a);
 +    // Ok
 +    match a {
 +        2 => println!("2"),
 +        _ => println!("Not 2"),
 +    }
 +    // Ok
 +    let d = Some(5);
 +    match d {
 +        Some(d) => println!("{}", d),
 +        _ => println!("None"),
 +    }
 +    // Lint
 +    match a {
 +        _ => println!("whatever"),
 +    }
 +    // Lint
 +    match a {
 +        _ => {
 +            let x = 29;
 +            println!("x has a value of {}", x);
 +        },
 +    }
 +    // Lint
 +    match a {
 +        _ => {
 +            let e = 5 * a;
 +            if e >= 5 {
 +                println!("e is superior to 5");
 +            }
 +        },
 +    }
 +    // Lint
 +    let p = Point { x: 0, y: 7 };
 +    match p {
 +        Point { x, y } => println!("Coords: ({}, {})", x, y),
 +    }
 +    // Lint
 +    match p {
 +        Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1),
 +    }
 +    // Lint
 +    let x = 5;
 +    match x {
 +        ref r => println!("Got a reference to {}", r),
 +    }
 +    // Lint
 +    let mut x = 5;
 +    match x {
 +        ref mut mr => println!("Got a mutable reference to {}", mr),
 +    }
 +    // Lint
 +    let product = match coords() {
 +        Point { x, y } => x * y,
 +    };
 +    // Lint
 +    let v = vec![Some(1), Some(2), Some(3), Some(4)];
 +    #[allow(clippy::let_and_return)]
 +    let _ = v
 +        .iter()
 +        .map(|i| match i.unwrap() {
 +            unwrapped => unwrapped,
 +        })
 +        .collect::<Vec<u8>>();
 +    // Ok
 +    let x = 1;
 +    match x {
 +        #[cfg(disabled_feature)]
 +        0 => println!("Disabled branch"),
 +        _ => println!("Enabled branch"),
 +    }
 +
 +    // Ok
 +    let x = 1;
 +    let y = 1;
 +    match match y {
 +        0 => 1,
 +        _ => 2,
 +    } {
 +        #[cfg(disabled_feature)]
 +        0 => println!("Array index start"),
 +        _ => println!("Not an array index start"),
 +    }
 +
 +    // Lint
 +    let x = 1;
 +    match x {
 +        // =>
 +        _ => println!("Not an array index start"),
 +    }
 +}
 +
 +#[allow(dead_code)]
 +fn issue_8723() {
 +    let (mut val, idx) = ("a b", 1);
 +
 +    val = match val.split_at(idx) {
 +        (pre, suf) => {
 +            println!("{}", pre);
 +            suf
 +        },
 +    };
 +
 +    let _ = val;
 +}
++
++#[allow(dead_code)]
++fn issue_9575() {
++    fn side_effects() {}
++    let _ = || match side_effects() {
++        _ => println!("Needs curlies"),
++    };
++}
index 5d4e7314b2137b9bf96fff539596dfa74b22e944,0000000000000000000000000000000000000000..2b9ec7ee7026e4a31f6fdfa7c4f06d06825acde3
mode 100644,000000..100644
--- /dev/null
@@@ -1,200 -1,0 +1,217 @@@
- error: aborting due to 13 previous errors
 +error: this match could be written as a `let` statement
 +  --> $DIR/match_single_binding.rs:28:5
 +   |
 +LL | /     match (a, b, c) {
 +LL | |         (x, y, z) => {
 +LL | |             println!("{} {} {}", x, y, z);
 +LL | |         },
 +LL | |     }
 +   | |_____^
 +   |
 +   = note: `-D clippy::match-single-binding` implied by `-D warnings`
 +help: consider using a `let` statement
 +   |
 +LL ~     let (x, y, z) = (a, b, c);
 +LL +     {
 +LL +         println!("{} {} {}", x, y, z);
 +LL +     }
 +   |
 +
 +error: this match could be written as a `let` statement
 +  --> $DIR/match_single_binding.rs:34:5
 +   |
 +LL | /     match (a, b, c) {
 +LL | |         (x, y, z) => println!("{} {} {}", x, y, z),
 +LL | |     }
 +   | |_____^
 +   |
 +help: consider using a `let` statement
 +   |
 +LL ~     let (x, y, z) = (a, b, c);
 +LL +     println!("{} {} {}", x, y, z);
 +   |
 +
 +error: this match could be replaced by its body itself
 +  --> $DIR/match_single_binding.rs:51:5
 +   |
 +LL | /     match a {
 +LL | |         _ => println!("whatever"),
 +LL | |     }
 +   | |_____^ help: consider using the match body instead: `println!("whatever");`
 +
 +error: this match could be replaced by its body itself
 +  --> $DIR/match_single_binding.rs:55:5
 +   |
 +LL | /     match a {
 +LL | |         _ => {
 +LL | |             let x = 29;
 +LL | |             println!("x has a value of {}", x);
 +LL | |         },
 +LL | |     }
 +   | |_____^
 +   |
 +help: consider using the match body instead
 +   |
 +LL ~     {
 +LL +         let x = 29;
 +LL +         println!("x has a value of {}", x);
 +LL +     }
 +   |
 +
 +error: this match could be replaced by its body itself
 +  --> $DIR/match_single_binding.rs:62:5
 +   |
 +LL | /     match a {
 +LL | |         _ => {
 +LL | |             let e = 5 * a;
 +LL | |             if e >= 5 {
 +...  |
 +LL | |         },
 +LL | |     }
 +   | |_____^
 +   |
 +help: consider using the match body instead
 +   |
 +LL ~     {
 +LL +         let e = 5 * a;
 +LL +         if e >= 5 {
 +LL +             println!("e is superior to 5");
 +LL +         }
 +LL +     }
 +   |
 +
 +error: this match could be written as a `let` statement
 +  --> $DIR/match_single_binding.rs:72:5
 +   |
 +LL | /     match p {
 +LL | |         Point { x, y } => println!("Coords: ({}, {})", x, y),
 +LL | |     }
 +   | |_____^
 +   |
 +help: consider using a `let` statement
 +   |
 +LL ~     let Point { x, y } = p;
 +LL +     println!("Coords: ({}, {})", x, y);
 +   |
 +
 +error: this match could be written as a `let` statement
 +  --> $DIR/match_single_binding.rs:76:5
 +   |
 +LL | /     match p {
 +LL | |         Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1),
 +LL | |     }
 +   | |_____^
 +   |
 +help: consider using a `let` statement
 +   |
 +LL ~     let Point { x: x1, y: y1 } = p;
 +LL +     println!("Coords: ({}, {})", x1, y1);
 +   |
 +
 +error: this match could be written as a `let` statement
 +  --> $DIR/match_single_binding.rs:81:5
 +   |
 +LL | /     match x {
 +LL | |         ref r => println!("Got a reference to {}", r),
 +LL | |     }
 +   | |_____^
 +   |
 +help: consider using a `let` statement
 +   |
 +LL ~     let ref r = x;
 +LL +     println!("Got a reference to {}", r);
 +   |
 +
 +error: this match could be written as a `let` statement
 +  --> $DIR/match_single_binding.rs:86:5
 +   |
 +LL | /     match x {
 +LL | |         ref mut mr => println!("Got a mutable reference to {}", mr),
 +LL | |     }
 +   | |_____^
 +   |
 +help: consider using a `let` statement
 +   |
 +LL ~     let ref mut mr = x;
 +LL +     println!("Got a mutable reference to {}", mr);
 +   |
 +
 +error: this match could be written as a `let` statement
 +  --> $DIR/match_single_binding.rs:90:5
 +   |
 +LL | /     let product = match coords() {
 +LL | |         Point { x, y } => x * y,
 +LL | |     };
 +   | |______^
 +   |
 +help: consider using a `let` statement
 +   |
 +LL ~     let Point { x, y } = coords();
 +LL +     let product = x * y;
 +   |
 +
 +error: this match could be written as a `let` statement
 +  --> $DIR/match_single_binding.rs:98:18
 +   |
 +LL |           .map(|i| match i.unwrap() {
 +   |  __________________^
 +LL | |             unwrapped => unwrapped,
 +LL | |         })
 +   | |_________^
 +   |
 +help: consider using a `let` statement
 +   |
 +LL ~         .map(|i| {
 +LL +             let unwrapped = i.unwrap();
 +LL +             unwrapped
 +LL ~         })
 +   |
 +
 +error: this match could be replaced by its body itself
 +  --> $DIR/match_single_binding.rs:124:5
 +   |
 +LL | /     match x {
 +LL | |         // =>
 +LL | |         _ => println!("Not an array index start"),
 +LL | |     }
 +   | |_____^ help: consider using the match body instead: `println!("Not an array index start");`
 +
 +error: this assignment could be simplified
 +  --> $DIR/match_single_binding.rs:134:5
 +   |
 +LL | /     val = match val.split_at(idx) {
 +LL | |         (pre, suf) => {
 +LL | |             println!("{}", pre);
 +LL | |             suf
 +LL | |         },
 +LL | |     };
 +   | |_____^
 +   |
 +help: consider removing the `match` expression
 +   |
 +LL ~     let (pre, suf) = val.split_at(idx);
 +LL +     val = {
 +LL +         println!("{}", pre);
 +LL +         suf
 +LL ~     };
 +   |
 +
++error: this match could be replaced by its scrutinee and body
++  --> $DIR/match_single_binding.rs:147:16
++   |
++LL |       let _ = || match side_effects() {
++   |  ________________^
++LL | |         _ => println!("Needs curlies"),
++LL | |     };
++   | |_____^
++   |
++help: consider using the scrutinee and body instead
++   |
++LL ~     let _ = || {
++LL +         side_effects();
++LL +         println!("Needs curlies");
++LL ~     };
++   |
++
++error: aborting due to 14 previous errors
 +
index 0a86144b95d5bcc59b148e298783caa02d017217,0000000000000000000000000000000000000000..823be65efe065703b8481ced833636d15b245373
mode 100644,000000..100644
--- /dev/null
@@@ -1,68 -1,0 +1,65 @@@
- // revisions: edition2018 edition2021
- // [edition2018] edition:2018
- // [edition2021] edition:2021
 +#![feature(exclusive_range_pattern)]
 +#![allow(clippy::match_same_arms)]
 +#![warn(clippy::match_wild_err_arm)]
 +
 +fn match_wild_err_arm() {
 +    let x: Result<i32, &str> = Ok(3);
 +
 +    match x {
 +        Ok(3) => println!("ok"),
 +        Ok(_) => println!("ok"),
 +        Err(_) => panic!("err"),
 +    }
 +
 +    match x {
 +        Ok(3) => println!("ok"),
 +        Ok(_) => println!("ok"),
 +        Err(_) => panic!(),
 +    }
 +
 +    match x {
 +        Ok(3) => println!("ok"),
 +        Ok(_) => println!("ok"),
 +        Err(_) => {
 +            panic!();
 +        },
 +    }
 +
 +    match x {
 +        Ok(3) => println!("ok"),
 +        Ok(_) => println!("ok"),
 +        Err(_e) => panic!(),
 +    }
 +
 +    // Allowed when used in `panic!`.
 +    match x {
 +        Ok(3) => println!("ok"),
 +        Ok(_) => println!("ok"),
 +        Err(_e) => panic!("{}", _e),
 +    }
 +
 +    // Allowed when not with `panic!` block.
 +    match x {
 +        Ok(3) => println!("ok"),
 +        Ok(_) => println!("ok"),
 +        Err(_) => println!("err"),
 +    }
 +
 +    // Allowed when used with `unreachable!`.
 +    match x {
 +        Ok(3) => println!("ok"),
 +        Ok(_) => println!("ok"),
 +        Err(_) => unreachable!(),
 +    }
 +
 +    // Allowed when used with `unreachable!`.
 +    match x {
 +        Ok(3) => println!("ok"),
 +        Ok(_) => println!("ok"),
 +        Err(_) => {
 +            unreachable!();
 +        },
 +    }
 +}
 +
 +fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b016d682698c834a25a1c8dfd6fdfae1771a2fa4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++error: `Err(_)` matches all errors
++  --> $DIR/match_wild_err_arm.rs:11:9
++   |
++LL |         Err(_) => panic!("err"),
++   |         ^^^^^^
++   |
++   = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable
++   = note: `-D clippy::match-wild-err-arm` implied by `-D warnings`
++
++error: `Err(_)` matches all errors
++  --> $DIR/match_wild_err_arm.rs:17:9
++   |
++LL |         Err(_) => panic!(),
++   |         ^^^^^^
++   |
++   = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable
++
++error: `Err(_)` matches all errors
++  --> $DIR/match_wild_err_arm.rs:23:9
++   |
++LL |         Err(_) => {
++   |         ^^^^^^
++   |
++   = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable
++
++error: `Err(_e)` matches all errors
++  --> $DIR/match_wild_err_arm.rs:31:9
++   |
++LL |         Err(_e) => panic!(),
++   |         ^^^^^^^
++   |
++   = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable
++
++error: aborting due to 4 previous errors
++
index b609ba659467f527f4bcc0b54120c75d5bf66791,0000000000000000000000000000000000000000..ae237395b95fc06d5db17bbc417a9d3c345c1974
mode 100644,000000..100644
--- /dev/null
@@@ -1,79 -1,0 +1,95 @@@
- #![allow(unused_imports)]
 +// run-rustfix
++
++#![feature(custom_inner_attributes)]
++#![allow(unused)]
 +#![warn(
 +    clippy::all,
 +    clippy::style,
 +    clippy::mem_replace_option_with_none,
 +    clippy::mem_replace_with_default
 +)]
 +
 +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
 +use std::mem;
 +
 +fn replace_option_with_none() {
 +    let mut an_option = Some(1);
 +    let _ = an_option.take();
 +    let an_option = &mut Some(1);
 +    let _ = an_option.take();
 +}
 +
 +fn replace_with_default() {
 +    let mut s = String::from("foo");
 +    let _ = std::mem::take(&mut s);
 +
 +    let s = &mut String::from("foo");
 +    let _ = std::mem::take(s);
 +    let _ = std::mem::take(s);
 +
 +    let mut v = vec![123];
 +    let _ = std::mem::take(&mut v);
 +    let _ = std::mem::take(&mut v);
 +    let _ = std::mem::take(&mut v);
 +    let _ = std::mem::take(&mut v);
 +
 +    let mut hash_map: HashMap<i32, i32> = HashMap::new();
 +    let _ = std::mem::take(&mut hash_map);
 +
 +    let mut btree_map: BTreeMap<i32, i32> = BTreeMap::new();
 +    let _ = std::mem::take(&mut btree_map);
 +
 +    let mut vd: VecDeque<i32> = VecDeque::new();
 +    let _ = std::mem::take(&mut vd);
 +
 +    let mut hash_set: HashSet<&str> = HashSet::new();
 +    let _ = std::mem::take(&mut hash_set);
 +
 +    let mut btree_set: BTreeSet<&str> = BTreeSet::new();
 +    let _ = std::mem::take(&mut btree_set);
 +
 +    let mut list: LinkedList<i32> = LinkedList::new();
 +    let _ = std::mem::take(&mut list);
 +
 +    let mut binary_heap: BinaryHeap<i32> = BinaryHeap::new();
 +    let _ = std::mem::take(&mut binary_heap);
 +
 +    let mut tuple = (vec![1, 2], BinaryHeap::<i32>::new());
 +    let _ = std::mem::take(&mut tuple);
 +
 +    let mut refstr = "hello";
 +    let _ = std::mem::take(&mut refstr);
 +
 +    let mut slice: &[i32] = &[1, 2, 3];
 +    let _ = std::mem::take(&mut slice);
 +}
 +
 +// lint is disabled for primitives because in this case `take`
 +// has no clear benefit over `replace` and sometimes is harder to read
 +fn dont_lint_primitive() {
 +    let mut pbool = true;
 +    let _ = std::mem::replace(&mut pbool, false);
 +
 +    let mut pint = 5;
 +    let _ = std::mem::replace(&mut pint, 0);
 +}
 +
 +fn main() {
 +    replace_option_with_none();
 +    replace_with_default();
 +    dont_lint_primitive();
 +}
++
++fn msrv_1_39() {
++    #![clippy::msrv = "1.39"]
++
++    let mut s = String::from("foo");
++    let _ = std::mem::replace(&mut s, String::default());
++}
++
++fn msrv_1_40() {
++    #![clippy::msrv = "1.40"]
++
++    let mut s = String::from("foo");
++    let _ = std::mem::take(&mut s);
++}
index 93f6dcdec83b96b3a8ad49ce9d8c043e24bb71e6,0000000000000000000000000000000000000000..3202e99e0be9e99cd0957041eced7010b551fa6e
mode 100644,000000..100644
--- /dev/null
@@@ -1,79 -1,0 +1,95 @@@
- #![allow(unused_imports)]
 +// run-rustfix
++
++#![feature(custom_inner_attributes)]
++#![allow(unused)]
 +#![warn(
 +    clippy::all,
 +    clippy::style,
 +    clippy::mem_replace_option_with_none,
 +    clippy::mem_replace_with_default
 +)]
 +
 +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
 +use std::mem;
 +
 +fn replace_option_with_none() {
 +    let mut an_option = Some(1);
 +    let _ = mem::replace(&mut an_option, None);
 +    let an_option = &mut Some(1);
 +    let _ = mem::replace(an_option, None);
 +}
 +
 +fn replace_with_default() {
 +    let mut s = String::from("foo");
 +    let _ = std::mem::replace(&mut s, String::default());
 +
 +    let s = &mut String::from("foo");
 +    let _ = std::mem::replace(s, String::default());
 +    let _ = std::mem::replace(s, Default::default());
 +
 +    let mut v = vec![123];
 +    let _ = std::mem::replace(&mut v, Vec::default());
 +    let _ = std::mem::replace(&mut v, Default::default());
 +    let _ = std::mem::replace(&mut v, Vec::new());
 +    let _ = std::mem::replace(&mut v, vec![]);
 +
 +    let mut hash_map: HashMap<i32, i32> = HashMap::new();
 +    let _ = std::mem::replace(&mut hash_map, HashMap::new());
 +
 +    let mut btree_map: BTreeMap<i32, i32> = BTreeMap::new();
 +    let _ = std::mem::replace(&mut btree_map, BTreeMap::new());
 +
 +    let mut vd: VecDeque<i32> = VecDeque::new();
 +    let _ = std::mem::replace(&mut vd, VecDeque::new());
 +
 +    let mut hash_set: HashSet<&str> = HashSet::new();
 +    let _ = std::mem::replace(&mut hash_set, HashSet::new());
 +
 +    let mut btree_set: BTreeSet<&str> = BTreeSet::new();
 +    let _ = std::mem::replace(&mut btree_set, BTreeSet::new());
 +
 +    let mut list: LinkedList<i32> = LinkedList::new();
 +    let _ = std::mem::replace(&mut list, LinkedList::new());
 +
 +    let mut binary_heap: BinaryHeap<i32> = BinaryHeap::new();
 +    let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new());
 +
 +    let mut tuple = (vec![1, 2], BinaryHeap::<i32>::new());
 +    let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new()));
 +
 +    let mut refstr = "hello";
 +    let _ = std::mem::replace(&mut refstr, "");
 +
 +    let mut slice: &[i32] = &[1, 2, 3];
 +    let _ = std::mem::replace(&mut slice, &[]);
 +}
 +
 +// lint is disabled for primitives because in this case `take`
 +// has no clear benefit over `replace` and sometimes is harder to read
 +fn dont_lint_primitive() {
 +    let mut pbool = true;
 +    let _ = std::mem::replace(&mut pbool, false);
 +
 +    let mut pint = 5;
 +    let _ = std::mem::replace(&mut pint, 0);
 +}
 +
 +fn main() {
 +    replace_option_with_none();
 +    replace_with_default();
 +    dont_lint_primitive();
 +}
++
++fn msrv_1_39() {
++    #![clippy::msrv = "1.39"]
++
++    let mut s = String::from("foo");
++    let _ = std::mem::replace(&mut s, String::default());
++}
++
++fn msrv_1_40() {
++    #![clippy::msrv = "1.40"]
++
++    let mut s = String::from("foo");
++    let _ = std::mem::replace(&mut s, String::default());
++}
index 90dc6c95f858295cef40d0fc6f27fa6b60fa7e85,0000000000000000000000000000000000000000..dd8a50dab9002f1649d970a1af348d99993bbb0f
mode 100644,000000..100644
--- /dev/null
@@@ -1,120 -1,0 +1,126 @@@
-   --> $DIR/mem_replace.rs:15:13
 +error: replacing an `Option` with `None`
-   --> $DIR/mem_replace.rs:17:13
++  --> $DIR/mem_replace.rs:17:13
 +   |
 +LL |     let _ = mem::replace(&mut an_option, None);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
 +   |
 +   = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings`
 +
 +error: replacing an `Option` with `None`
-   --> $DIR/mem_replace.rs:22:13
++  --> $DIR/mem_replace.rs:19:13
 +   |
 +LL |     let _ = mem::replace(an_option, None);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-   --> $DIR/mem_replace.rs:25:13
++  --> $DIR/mem_replace.rs:24:13
 +   |
 +LL |     let _ = std::mem::replace(&mut s, String::default());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
 +   |
 +   = note: `-D clippy::mem-replace-with-default` implied by `-D warnings`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-   --> $DIR/mem_replace.rs:26:13
++  --> $DIR/mem_replace.rs:27:13
 +   |
 +LL |     let _ = std::mem::replace(s, String::default());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-   --> $DIR/mem_replace.rs:29:13
++  --> $DIR/mem_replace.rs:28:13
 +   |
 +LL |     let _ = std::mem::replace(s, Default::default());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-   --> $DIR/mem_replace.rs:30:13
++  --> $DIR/mem_replace.rs:31:13
 +   |
 +LL |     let _ = std::mem::replace(&mut v, Vec::default());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-   --> $DIR/mem_replace.rs:31:13
++  --> $DIR/mem_replace.rs:32:13
 +   |
 +LL |     let _ = std::mem::replace(&mut v, Default::default());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-   --> $DIR/mem_replace.rs:32:13
++  --> $DIR/mem_replace.rs:33:13
 +   |
 +LL |     let _ = std::mem::replace(&mut v, Vec::new());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-   --> $DIR/mem_replace.rs:35:13
++  --> $DIR/mem_replace.rs:34:13
 +   |
 +LL |     let _ = std::mem::replace(&mut v, vec![]);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-   --> $DIR/mem_replace.rs:38:13
++  --> $DIR/mem_replace.rs:37:13
 +   |
 +LL |     let _ = std::mem::replace(&mut hash_map, HashMap::new());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-   --> $DIR/mem_replace.rs:41:13
++  --> $DIR/mem_replace.rs:40:13
 +   |
 +LL |     let _ = std::mem::replace(&mut btree_map, BTreeMap::new());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-   --> $DIR/mem_replace.rs:44:13
++  --> $DIR/mem_replace.rs:43:13
 +   |
 +LL |     let _ = std::mem::replace(&mut vd, VecDeque::new());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-   --> $DIR/mem_replace.rs:47:13
++  --> $DIR/mem_replace.rs:46:13
 +   |
 +LL |     let _ = std::mem::replace(&mut hash_set, HashSet::new());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-   --> $DIR/mem_replace.rs:50:13
++  --> $DIR/mem_replace.rs:49:13
 +   |
 +LL |     let _ = std::mem::replace(&mut btree_set, BTreeSet::new());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-   --> $DIR/mem_replace.rs:53:13
++  --> $DIR/mem_replace.rs:52:13
 +   |
 +LL |     let _ = std::mem::replace(&mut list, LinkedList::new());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-   --> $DIR/mem_replace.rs:56:13
++  --> $DIR/mem_replace.rs:55:13
 +   |
 +LL |     let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-   --> $DIR/mem_replace.rs:59:13
++  --> $DIR/mem_replace.rs:58:13
 +   |
 +LL |     let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new()));
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-   --> $DIR/mem_replace.rs:62:13
++  --> $DIR/mem_replace.rs:61:13
 +   |
 +LL |     let _ = std::mem::replace(&mut refstr, "");
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- error: aborting due to 19 previous errors
++  --> $DIR/mem_replace.rs:64:13
 +   |
 +LL |     let _ = std::mem::replace(&mut slice, &[]);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)`
 +
++error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
++  --> $DIR/mem_replace.rs:94:13
++   |
++LL |     let _ = std::mem::replace(&mut s, String::default());
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
++
++error: aborting due to 20 previous errors
 +
index c4c6391bb4c1dfebd2dd9d738614c3ea633f6801,0000000000000000000000000000000000000000..cd148063bf065c304ff683fe19998885a56a3a70
mode 100644,000000..100644
--- /dev/null
@@@ -1,240 -1,0 +1,29 @@@
- #![clippy::msrv = "1.0.0"]
 +#![allow(clippy::redundant_clone)]
 +#![feature(custom_inner_attributes)]
- use std::ops::{Deref, RangeFrom};
 +
- fn approx_const() {
++fn main() {}
 +
-     let log10_2 = 0.301029995663981;
++fn just_under_msrv() {
++    #![clippy::msrv = "1.42.0"]
 +    let log2_10 = 3.321928094887362;
- fn cloned_instead_of_copied() {
-     let _ = [1].iter().cloned();
- }
- fn option_as_ref_deref() {
-     let mut opt = Some(String::from("123"));
-     let _ = opt.as_ref().map(String::as_str);
-     let _ = opt.as_ref().map(|x| x.as_str());
-     let _ = opt.as_mut().map(String::as_mut_str);
-     let _ = opt.as_mut().map(|x| x.as_mut_str());
- }
- fn match_like_matches() {
-     let _y = match Some(5) {
-         Some(0) => true,
-         _ => false,
-     };
- }
- fn match_same_arms() {
-     match (1, 2, 3) {
-         (1, .., 3) => 42,
-         (.., 3) => 42, //~ ERROR match arms have same body
-         _ => 0,
-     };
- }
- fn match_same_arms2() {
-     let _ = match Some(42) {
-         Some(_) => 24,
-         None => 24, //~ ERROR match arms have same body
-     };
- }
- pub fn manual_strip_msrv() {
-     let s = "hello, world!";
-     if s.starts_with("hello, ") {
-         assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
-     }
- }
- pub fn redundant_fieldnames() {
-     let start = 0;
-     let _ = RangeFrom { start: start };
- }
- pub fn redundant_static_lifetime() {
-     const VAR_ONE: &'static str = "Test constant #1";
- }
- pub fn checked_conversion() {
-     let value: i64 = 42;
-     let _ = value <= (u32::max_value() as i64) && value >= 0;
-     let _ = value <= (u32::MAX as i64) && value >= 0;
- }
- pub struct FromOverInto(String);
- impl Into<FromOverInto> for String {
-     fn into(self) -> FromOverInto {
-         FromOverInto(self)
-     }
- }
- pub fn filter_map_next() {
-     let a = ["1", "lol", "3", "NaN", "5"];
-     #[rustfmt::skip]
-     let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
-         .into_iter()
-         .filter_map(|x| {
-             if x == 2 {
-                 Some(x * 2)
-             } else {
-                 None
-             }
-         })
-         .next();
- }
- #[allow(clippy::no_effect)]
- #[allow(clippy::short_circuit_statement)]
- #[allow(clippy::unnecessary_operation)]
- pub fn manual_range_contains() {
-     let x = 5;
-     x >= 8 && x < 12;
- }
- pub fn use_self() {
-     struct Foo;
-     impl Foo {
-         fn new() -> Foo {
-             Foo {}
-         }
-         fn test() -> Foo {
-             Foo::new()
-         }
-     }
- }
- fn replace_with_default() {
-     let mut s = String::from("foo");
-     let _ = std::mem::replace(&mut s, String::default());
- }
- fn map_unwrap_or() {
-     let opt = Some(1);
-     // Check for `option.map(_).unwrap_or(_)` use.
-     // Single line case.
-     let _ = opt
-         .map(|x| x + 1)
-         // Should lint even though this call is on a separate line.
-         .unwrap_or(0);
- }
- // Could be const
- fn missing_const_for_fn() -> i32 {
-     1
- }
- fn unnest_or_patterns() {
-     struct TS(u8, u8);
-     if let TS(0, x) | TS(1, x) = TS(0, 0) {}
- }
- #[cfg_attr(rustfmt, rustfmt_skip)]
- fn deprecated_cfg_attr() {}
- #[warn(clippy::cast_lossless)]
- fn int_from_bool() -> u8 {
-     true as u8
- }
- fn err_expect() {
-     let x: Result<u32, &str> = Ok(10);
-     x.err().expect("Testing expect_err");
- }
- fn cast_abs_to_unsigned() {
-     let x: i32 = 10;
-     assert_eq!(10u32, x.abs() as u32);
- }
- fn manual_rem_euclid() {
-     let x: i32 = 10;
-     let _: i32 = ((x % 4) + 4) % 4;
- }
- fn manual_clamp() {
-     let (input, min, max) = (0, -1, 2);
-     let _ = if input < min {
-         min
-     } else if input > max {
-         max
-     } else {
-         input
-     };
- }
- fn main() {
-     filter_map_next();
-     checked_conversion();
-     redundant_fieldnames();
-     redundant_static_lifetime();
-     option_as_ref_deref();
-     match_like_matches();
-     match_same_arms();
-     match_same_arms2();
-     manual_strip_msrv();
-     manual_range_contains();
-     use_self();
-     replace_with_default();
-     map_unwrap_or();
-     missing_const_for_fn();
-     unnest_or_patterns();
-     int_from_bool();
-     err_expect();
-     cast_abs_to_unsigned();
-     manual_rem_euclid();
-     manual_clamp();
 +}
 +
- mod just_under_msrv {
-     #![feature(custom_inner_attributes)]
++fn meets_msrv() {
++    #![clippy::msrv = "1.43.0"]
++    let log2_10 = 3.321928094887362;
 +}
 +
-     fn main() {
-         let s = "hello, world!";
-         if s.starts_with("hello, ") {
-             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
-         }
-     }
- }
- mod meets_msrv {
-     #![feature(custom_inner_attributes)]
-     #![clippy::msrv = "1.45.0"]
-     fn main() {
-         let s = "hello, world!";
-         if s.starts_with("hello, ") {
-             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
-         }
-     }
++fn just_above_msrv() {
 +    #![clippy::msrv = "1.44.0"]
- mod just_above_msrv {
-     #![feature(custom_inner_attributes)]
-     #![clippy::msrv = "1.46.0"]
-     fn main() {
-         let s = "hello, world!";
-         if s.starts_with("hello, ") {
-             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
-         }
-     }
++    let log2_10 = 3.321928094887362;
 +}
 +
- mod const_rem_euclid {
-     #![feature(custom_inner_attributes)]
-     #![clippy::msrv = "1.50.0"]
-     pub const fn const_rem_euclid_4(num: i32) -> i32 {
-         ((num % 4) + 4) % 4
-     }
++fn no_patch_under() {
++    #![clippy::msrv = "1.42"]
++    let log2_10 = 3.321928094887362;
 +}
 +
++fn no_patch_meets() {
++    #![clippy::msrv = "1.43"]
++    let log2_10 = 3.321928094887362;
 +}
index d1cffc26a831880ebd74eb871aaab295560e31fc,0000000000000000000000000000000000000000..68aa58748190be3552ef5ad7455f95b32dc7fce8
mode 100644,000000..100644
--- /dev/null
@@@ -1,37 -1,0 +1,27 @@@
- error: stripping a prefix manually
-   --> $DIR/min_rust_version_attr.rs:216:24
++error: approximate value of `f{32, 64}::consts::LOG2_10` found
++  --> $DIR/min_rust_version_attr.rs:13:19
 +   |
- LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
-    |                        ^^^^^^^^^^^^^^^^^^^^
-    |
- note: the prefix was tested here
-   --> $DIR/min_rust_version_attr.rs:215:9
-    |
- LL |         if s.starts_with("hello, ") {
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-    = note: `-D clippy::manual-strip` implied by `-D warnings`
- help: try using the `strip_prefix` method
-    |
- LL ~         if let Some(<stripped>) = s.strip_prefix("hello, ") {
- LL ~             assert_eq!(<stripped>.to_uppercase(), "WORLD!");
++LL |     let log2_10 = 3.321928094887362;
++   |                   ^^^^^^^^^^^^^^^^^
 +   |
++   = help: consider using the constant directly
++   = note: `#[deny(clippy::approx_constant)]` on by default
 +
- error: stripping a prefix manually
-   --> $DIR/min_rust_version_attr.rs:228:24
++error: approximate value of `f{32, 64}::consts::LOG2_10` found
++  --> $DIR/min_rust_version_attr.rs:18:19
 +   |
- LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
-    |                        ^^^^^^^^^^^^^^^^^^^^
++LL |     let log2_10 = 3.321928094887362;
++   |                   ^^^^^^^^^^^^^^^^^
 +   |
- note: the prefix was tested here
-   --> $DIR/min_rust_version_attr.rs:227:9
-    |
- LL |         if s.starts_with("hello, ") {
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- help: try using the `strip_prefix` method
++   = help: consider using the constant directly
++
++error: approximate value of `f{32, 64}::consts::LOG2_10` found
++  --> $DIR/min_rust_version_attr.rs:28:19
 +   |
- LL ~         if let Some(<stripped>) = s.strip_prefix("hello, ") {
- LL ~             assert_eq!(<stripped>.to_uppercase(), "WORLD!");
++LL |     let log2_10 = 3.321928094887362;
++   |                   ^^^^^^^^^^^^^^^^^
 +   |
++   = help: consider using the constant directly
 +
- error: aborting due to 2 previous errors
++error: aborting due to 3 previous errors
 +
index f20841891a7429f213294e520dc32159697d4869,0000000000000000000000000000000000000000..02892f329af67d0f9cf1e0fafa870d5f530ab98a
mode 100644,000000..100644
--- /dev/null
@@@ -1,4 -1,0 +1,18 @@@
 +#![feature(custom_inner_attributes)]
 +#![clippy::msrv = "invalid.version"]
 +
 +fn main() {}
++
++#[clippy::msrv = "invalid.version"]
++fn outer_attr() {}
++
++mod multiple {
++    #![clippy::msrv = "1.40"]
++    #![clippy::msrv = "=1.35.0"]
++    #![clippy::msrv = "1.10.1"]
++
++    mod foo {
++        #![clippy::msrv = "1"]
++        #![clippy::msrv = "1.0.0"]
++    }
++}
index 6ff88ca56f8b92fac4ba9446420e0f19d20602d2,0000000000000000000000000000000000000000..93370a0fa9c912ce45cf972743c38714c5bcaff7
mode 100644,000000..100644
--- /dev/null
@@@ -1,8 -1,0 +1,50 @@@
- error: aborting due to previous error
 +error: `invalid.version` is not a valid Rust version
 +  --> $DIR/min_rust_version_invalid_attr.rs:2:1
 +   |
 +LL | #![clippy::msrv = "invalid.version"]
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
++error: `msrv` cannot be an outer attribute
++  --> $DIR/min_rust_version_invalid_attr.rs:6:1
++   |
++LL | #[clippy::msrv = "invalid.version"]
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: `msrv` is defined multiple times
++  --> $DIR/min_rust_version_invalid_attr.rs:11:5
++   |
++LL |     #![clippy::msrv = "=1.35.0"]
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++note: first definition found here
++  --> $DIR/min_rust_version_invalid_attr.rs:10:5
++   |
++LL |     #![clippy::msrv = "1.40"]
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: `msrv` is defined multiple times
++  --> $DIR/min_rust_version_invalid_attr.rs:12:5
++   |
++LL |     #![clippy::msrv = "1.10.1"]
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++note: first definition found here
++  --> $DIR/min_rust_version_invalid_attr.rs:10:5
++   |
++LL |     #![clippy::msrv = "1.40"]
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: `msrv` is defined multiple times
++  --> $DIR/min_rust_version_invalid_attr.rs:16:9
++   |
++LL |         #![clippy::msrv = "1.0.0"]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++note: first definition found here
++  --> $DIR/min_rust_version_invalid_attr.rs:15:9
++   |
++LL |         #![clippy::msrv = "1"]
++   |         ^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 5 previous errors
 +
index 88f6935d224aec502122cc5ebf5e06d8b70a0a7f,0000000000000000000000000000000000000000..b85e88784918d50401858472574b07902aadf6e0
mode 100644,000000..100644
--- /dev/null
@@@ -1,81 -1,0 +1,93 @@@
 +#![warn(clippy::missing_const_for_fn)]
 +#![allow(incomplete_features, clippy::let_and_return)]
 +#![feature(custom_inner_attributes)]
 +
 +use std::mem::transmute;
 +
 +struct Game {
 +    guess: i32,
 +}
 +
 +impl Game {
 +    // Could be const
 +    pub fn new() -> Self {
 +        Self { guess: 42 }
 +    }
 +
 +    fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
 +        b
 +    }
 +}
 +
 +// Could be const
 +fn one() -> i32 {
 +    1
 +}
 +
 +// Could also be const
 +fn two() -> i32 {
 +    let abc = 2;
 +    abc
 +}
 +
 +// Could be const (since Rust 1.39)
 +fn string() -> String {
 +    String::new()
 +}
 +
 +// Could be const
 +unsafe fn four() -> i32 {
 +    4
 +}
 +
 +// Could also be const
 +fn generic<T>(t: T) -> T {
 +    t
 +}
 +
 +fn sub(x: u32) -> usize {
 +    unsafe { transmute(&x) }
 +}
 +
 +fn generic_arr<T: Copy>(t: [T; 1]) -> T {
 +    t[0]
 +}
 +
 +mod with_drop {
 +    pub struct A;
 +    pub struct B;
 +    impl Drop for A {
 +        fn drop(&mut self) {}
 +    }
 +
 +    impl B {
 +        // This can be const, because `a` is passed by reference
 +        pub fn b(self, a: &A) -> B {
 +            B
 +        }
 +    }
 +}
 +
 +mod const_fn_stabilized_before_msrv {
 +    #![clippy::msrv = "1.47.0"]
 +
 +    // This could be const because `u8::is_ascii_digit` is a stable const function in 1.47.
 +    fn const_fn_stabilized_before_msrv(byte: u8) {
 +        byte.is_ascii_digit();
 +    }
 +}
 +
++fn msrv_1_45() -> i32 {
++    #![clippy::msrv = "1.45"]
++
++    45
++}
++
++fn msrv_1_46() -> i32 {
++    #![clippy::msrv = "1.46"]
++
++    46
++}
++
 +// Should not be const
 +fn main() {}
index 3eb52b6827475d1b52f189cb0f876c68e6dc8191,0000000000000000000000000000000000000000..f8e221c82f1a5c4ba575433c3cdeb301577c60d3
mode 100644,000000..100644
--- /dev/null
@@@ -1,85 -1,0 +1,95 @@@
- error: aborting due to 10 previous errors
 +error: this could be a `const fn`
 +  --> $DIR/could_be_const.rs:13:5
 +   |
 +LL | /     pub fn new() -> Self {
 +LL | |         Self { guess: 42 }
 +LL | |     }
 +   | |_____^
 +   |
 +   = note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
 +
 +error: this could be a `const fn`
 +  --> $DIR/could_be_const.rs:17:5
 +   |
 +LL | /     fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
 +LL | |         b
 +LL | |     }
 +   | |_____^
 +
 +error: this could be a `const fn`
 +  --> $DIR/could_be_const.rs:23:1
 +   |
 +LL | / fn one() -> i32 {
 +LL | |     1
 +LL | | }
 +   | |_^
 +
 +error: this could be a `const fn`
 +  --> $DIR/could_be_const.rs:28:1
 +   |
 +LL | / fn two() -> i32 {
 +LL | |     let abc = 2;
 +LL | |     abc
 +LL | | }
 +   | |_^
 +
 +error: this could be a `const fn`
 +  --> $DIR/could_be_const.rs:34:1
 +   |
 +LL | / fn string() -> String {
 +LL | |     String::new()
 +LL | | }
 +   | |_^
 +
 +error: this could be a `const fn`
 +  --> $DIR/could_be_const.rs:39:1
 +   |
 +LL | / unsafe fn four() -> i32 {
 +LL | |     4
 +LL | | }
 +   | |_^
 +
 +error: this could be a `const fn`
 +  --> $DIR/could_be_const.rs:44:1
 +   |
 +LL | / fn generic<T>(t: T) -> T {
 +LL | |     t
 +LL | | }
 +   | |_^
 +
 +error: this could be a `const fn`
 +  --> $DIR/could_be_const.rs:52:1
 +   |
 +LL | / fn generic_arr<T: Copy>(t: [T; 1]) -> T {
 +LL | |     t[0]
 +LL | | }
 +   | |_^
 +
 +error: this could be a `const fn`
 +  --> $DIR/could_be_const.rs:65:9
 +   |
 +LL | /         pub fn b(self, a: &A) -> B {
 +LL | |             B
 +LL | |         }
 +   | |_________^
 +
 +error: this could be a `const fn`
 +  --> $DIR/could_be_const.rs:75:5
 +   |
 +LL | /     fn const_fn_stabilized_before_msrv(byte: u8) {
 +LL | |         byte.is_ascii_digit();
 +LL | |     }
 +   | |_____^
 +
++error: this could be a `const fn`
++  --> $DIR/could_be_const.rs:86:1
++   |
++LL | / fn msrv_1_46() -> i32 {
++LL | |     #![clippy::msrv = "1.46"]
++LL | |
++LL | |     46
++LL | | }
++   | |_^
++
++error: aborting due to 11 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8df885919a3e6005fdc380e3298b03d47372c391
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++#![allow(unused, clippy::needless_lifetimes)]
++#![warn(clippy::missing_trait_methods)]
++
++trait A {
++    fn provided() {}
++}
++
++trait B {
++    fn required();
++
++    fn a(_: usize) -> usize {
++        1
++    }
++
++    fn b<'a, T: AsRef<[u8]>>(a: &'a T) -> &'a [u8] {
++        a.as_ref()
++    }
++}
++
++struct Partial;
++
++impl A for Partial {}
++
++impl B for Partial {
++    fn required() {}
++
++    fn a(_: usize) -> usize {
++        2
++    }
++}
++
++struct Complete;
++
++impl A for Complete {
++    fn provided() {}
++}
++
++impl B for Complete {
++    fn required() {}
++
++    fn a(_: usize) -> usize {
++        2
++    }
++
++    fn b<T: AsRef<[u8]>>(a: &T) -> &[u8] {
++        a.as_ref()
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0c5205e19657256cadafc92710f52d3d69072866
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++error: missing trait method provided by default: `provided`
++  --> $DIR/missing_trait_methods.rs:22:1
++   |
++LL | impl A for Partial {}
++   | ^^^^^^^^^^^^^^^^^^
++   |
++help: implement the method
++  --> $DIR/missing_trait_methods.rs:5:5
++   |
++LL |     fn provided() {}
++   |     ^^^^^^^^^^^^^
++   = note: `-D clippy::missing-trait-methods` implied by `-D warnings`
++
++error: missing trait method provided by default: `b`
++  --> $DIR/missing_trait_methods.rs:24:1
++   |
++LL | impl B for Partial {
++   | ^^^^^^^^^^^^^^^^^^
++   |
++help: implement the method
++  --> $DIR/missing_trait_methods.rs:15:5
++   |
++LL |     fn b<'a, T: AsRef<[u8]>>(a: &'a T) -> &'a [u8] {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
index aa2687159ef47bfd25f4654075a5d8afecc2e0cc,0000000000000000000000000000000000000000..340e89d2db1d2c25d97f14fd242ede26de1bb2eb
mode 100644,000000..100644
--- /dev/null
@@@ -1,329 -1,0 +1,387 @@@
- #[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)]
 +// run-rustfix
 +#![feature(custom_inner_attributes, lint_reasons)]
 +
 +#[warn(clippy::all, clippy::needless_borrow)]
 +#[allow(unused_variables)]
-         takes_iter(&mut x)
++#[allow(
++    clippy::uninlined_format_args,
++    clippy::unnecessary_mut_passed,
++    clippy::unnecessary_to_owned
++)]
 +fn main() {
 +    let a = 5;
 +    let ref_a = &a;
 +    let _ = x(&a); // no warning
 +    let _ = x(&a); // warn
 +
 +    let mut b = 5;
 +    mut_ref(&mut b); // no warning
 +    mut_ref(&mut b); // warn
 +
 +    let s = &String::from("hi");
 +    let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
 +    let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
 +    let vec = Vec::new();
 +    let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
 +    h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
 +    let garbl = match 42 {
 +        44 => &a,
 +        45 => {
 +            println!("foo");
 +            &a
 +        },
 +        46 => &a,
 +        47 => {
 +            println!("foo");
 +            loop {
 +                println!("{}", a);
 +                if a == 25 {
 +                    break ref_a;
 +                }
 +            }
 +        },
 +        _ => panic!(),
 +    };
 +
 +    let _ = x(&a);
 +    let _ = x(&a);
 +    let _ = x(&mut b);
 +    let _ = x(ref_a);
 +    {
 +        let b = &mut b;
 +        x(b);
 +    }
 +
 +    // Issue #8191
 +    let mut x = 5;
 +    let mut x = &mut x;
 +
 +    mut_ref(x);
 +    mut_ref(x);
 +    let y: &mut i32 = x;
 +    let y: &mut i32 = x;
 +
 +    let y = match 0 {
 +        // Don't lint. Removing the borrow would move 'x'
 +        0 => &mut x,
 +        _ => &mut *x,
 +    };
 +    let y: &mut i32 = match 0 {
 +        // Lint here. The type given above triggers auto-borrow.
 +        0 => x,
 +        _ => &mut *x,
 +    };
 +    fn ref_mut_i32(_: &mut i32) {}
 +    ref_mut_i32(match 0 {
 +        // Lint here. The type given above triggers auto-borrow.
 +        0 => x,
 +        _ => &mut *x,
 +    });
 +    // use 'x' after to make sure it's still usable in the fixed code.
 +    *x = 5;
 +
 +    let s = String::new();
 +    // let _ = (&s).len();
 +    // let _ = (&s).capacity();
 +    // let _ = (&&s).capacity();
 +
 +    let x = (1, 2);
 +    let _ = x.0;
 +    let x = &x as *const (i32, i32);
 +    let _ = unsafe { (*x).0 };
 +
 +    // Issue #8367
 +    trait Foo {
 +        fn foo(self);
 +    }
 +    impl Foo for &'_ () {
 +        fn foo(self) {}
 +    }
 +    (&()).foo(); // Don't lint. `()` doesn't implement `Foo`
 +    (&()).foo();
 +
 +    impl Foo for i32 {
 +        fn foo(self) {}
 +    }
 +    impl Foo for &'_ i32 {
 +        fn foo(self) {}
 +    }
 +    (&5).foo(); // Don't lint. `5` will call `<i32 as Foo>::foo`
 +    (&5).foo();
 +
 +    trait FooRef {
 +        fn foo_ref(&self);
 +    }
 +    impl FooRef for () {
 +        fn foo_ref(&self) {}
 +    }
 +    impl FooRef for &'_ () {
 +        fn foo_ref(&self) {}
 +    }
 +    (&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
 +
 +    struct S;
 +    impl From<S> for u32 {
 +        fn from(s: S) -> Self {
 +            (&s).into()
 +        }
 +    }
 +    impl From<&S> for u32 {
 +        fn from(s: &S) -> Self {
 +            0
 +        }
 +    }
 +
 +    let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
 +    let _ = std::path::Path::new(".").join(".");
 +    deref_target_is_x(X);
 +    multiple_constraints([[""]]);
 +    multiple_constraints_normalizes_to_same(X, X);
 +    let _ = Some("").unwrap_or("");
++    let _ = std::fs::write("x", "".to_string());
 +
 +    only_sized(&""); // Don't lint. `Sized` is only bound
 +    let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound
 +    let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
 +    ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
 +    refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
 +    multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
 +}
 +
 +#[allow(clippy::needless_borrowed_reference)]
 +fn x(y: &i32) -> i32 {
 +    *y
 +}
 +
 +fn mut_ref(y: &mut i32) {
 +    *y = 5;
 +}
 +
 +fn f<T: Copy>(y: &T) -> T {
 +    *y
 +}
 +
 +fn g(y: &[u8]) -> u8 {
 +    y[0]
 +}
 +
 +trait Trait {}
 +
 +impl<'a> Trait for &'a str {}
 +
 +fn h(_: &dyn Trait) {}
 +
 +#[allow(dead_code)]
 +fn check_expect_suppression() {
 +    let a = 5;
 +    #[expect(clippy::needless_borrow)]
 +    let _ = x(&&a);
 +}
 +
 +#[allow(dead_code)]
 +mod issue9160 {
 +    pub struct S<F> {
 +        f: F,
 +    }
 +
 +    impl<T, F> S<F>
 +    where
 +        F: Fn() -> T,
 +    {
 +        fn calls_field(&self) -> T {
 +            (self.f)()
 +        }
 +    }
 +
 +    impl<T, F> S<F>
 +    where
 +        F: FnMut() -> T,
 +    {
 +        fn calls_mut_field(&mut self) -> T {
 +            (self.f)()
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +struct X;
 +
 +impl std::ops::Deref for X {
 +    type Target = X;
 +    fn deref(&self) -> &Self::Target {
 +        self
 +    }
 +}
 +
 +fn deref_target_is_x<T>(_: T)
 +where
 +    T: std::ops::Deref<Target = X>,
 +{
 +}
 +
 +fn multiple_constraints<T, U, V, X, Y>(_: T)
 +where
 +    T: IntoIterator<Item = U> + IntoIterator<Item = X>,
 +    U: IntoIterator<Item = V>,
 +    V: AsRef<str>,
 +    X: IntoIterator<Item = Y>,
 +    Y: AsRef<std::ffi::OsStr>,
 +{
 +}
 +
 +fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
 +where
 +    T: std::ops::Deref<Target = U>,
 +    U: std::ops::Deref<Target = V>,
 +{
 +}
 +
 +fn only_sized<T>(_: T) {}
 +
 +fn ref_as_ref_path<T: 'static>(_: &'static T)
 +where
 +    &'static T: AsRef<std::path::Path>,
 +{
 +}
 +
 +trait RefsOnly {
 +    type Referent;
 +}
 +
 +impl<T> RefsOnly for &T {
 +    type Referent = T;
 +}
 +
 +fn refs_only<T, U>(_: T)
 +where
 +    T: RefsOnly<Referent = U>,
 +{
 +}
 +
 +fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
 +where
 +    T: IntoIterator<Item = U>,
 +    U: IntoIterator<Item = V>,
 +    V: AsRef<str>,
 +{
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
 +#[allow(dead_code)]
 +mod copyable_iterator {
 +    #[derive(Clone, Copy)]
 +    struct Iter;
 +    impl Iterator for Iter {
 +        type Item = ();
 +        fn next(&mut self) -> Option<Self::Item> {
 +            None
 +        }
 +    }
 +    fn takes_iter(_: impl Iterator) {}
 +    fn dont_warn(mut x: Iter) {
 +        takes_iter(&mut x);
 +    }
++    #[allow(unused_mut)]
 +    fn warn(mut x: &mut Iter) {
++        takes_iter(x)
 +    }
 +}
 +
 +mod under_msrv {
 +    #![allow(dead_code)]
 +    #![clippy::msrv = "1.52.0"]
 +
 +    fn foo() {
 +        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
 +    }
 +}
 +
 +mod meets_msrv {
 +    #![allow(dead_code)]
 +    #![clippy::msrv = "1.53.0"]
 +
 +    fn foo() {
 +        let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
 +    }
 +}
 +
 +#[allow(unused)]
 +fn issue9383() {
 +    // Should not lint because unions need explicit deref when accessing field
 +    use std::mem::ManuallyDrop;
 +
 +    union Coral {
 +        crab: ManuallyDrop<Vec<i32>>,
 +    }
 +
 +    union Ocean {
 +        coral: ManuallyDrop<Coral>,
 +    }
 +
 +    let mut ocean = Ocean {
 +        coral: ManuallyDrop::new(Coral {
 +            crab: ManuallyDrop::new(vec![1, 2, 3]),
 +        }),
 +    };
 +
 +    unsafe {
 +        ManuallyDrop::drop(&mut (&mut ocean.coral).crab);
 +
 +        (*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]);
 +        ManuallyDrop::drop(&mut (*ocean.coral).crab);
 +
 +        ManuallyDrop::drop(&mut ocean.coral);
 +    }
 +}
++
++#[allow(dead_code)]
++fn closure_test() {
++    let env = "env".to_owned();
++    let arg = "arg".to_owned();
++    let f = |arg| {
++        let loc = "loc".to_owned();
++        let _ = std::fs::write("x", &env); // Don't lint. In environment
++        let _ = std::fs::write("x", arg);
++        let _ = std::fs::write("x", loc);
++    };
++    let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
++    f(arg);
++}
++
++#[allow(dead_code)]
++mod significant_drop {
++    #[derive(Debug)]
++    struct X;
++
++    #[derive(Debug)]
++    struct Y;
++
++    impl Drop for Y {
++        fn drop(&mut self) {}
++    }
++
++    fn foo(x: X, y: Y) {
++        debug(x);
++        debug(&y); // Don't lint. Has significant drop
++    }
++
++    fn debug(_: impl std::fmt::Debug) {}
++}
++
++#[allow(dead_code)]
++mod used_exactly_once {
++    fn foo(x: String) {
++        use_x(x);
++    }
++    fn use_x(_: impl AsRef<str>) {}
++}
++
++#[allow(dead_code)]
++mod used_more_than_once {
++    fn foo(x: String) {
++        use_x(&x);
++        use_x_again(&x);
++    }
++    fn use_x(_: impl AsRef<str>) {}
++    fn use_x_again(_: impl AsRef<str>) {}
++}
index d41251e8f6aac604f0ebd1e9bdc0db9474f2ba91,0000000000000000000000000000000000000000..c93711ac8e28490feb37a078d14f66da07e904a7
mode 100644,000000..100644
--- /dev/null
@@@ -1,329 -1,0 +1,387 @@@
- #[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)]
 +// run-rustfix
 +#![feature(custom_inner_attributes, lint_reasons)]
 +
 +#[warn(clippy::all, clippy::needless_borrow)]
 +#[allow(unused_variables)]
++#[allow(
++    clippy::uninlined_format_args,
++    clippy::unnecessary_mut_passed,
++    clippy::unnecessary_to_owned
++)]
 +fn main() {
 +    let a = 5;
 +    let ref_a = &a;
 +    let _ = x(&a); // no warning
 +    let _ = x(&&a); // warn
 +
 +    let mut b = 5;
 +    mut_ref(&mut b); // no warning
 +    mut_ref(&mut &mut b); // warn
 +
 +    let s = &String::from("hi");
 +    let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
 +    let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
 +    let vec = Vec::new();
 +    let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
 +    h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
 +    let garbl = match 42 {
 +        44 => &a,
 +        45 => {
 +            println!("foo");
 +            &&a
 +        },
 +        46 => &&a,
 +        47 => {
 +            println!("foo");
 +            loop {
 +                println!("{}", a);
 +                if a == 25 {
 +                    break &ref_a;
 +                }
 +            }
 +        },
 +        _ => panic!(),
 +    };
 +
 +    let _ = x(&&&a);
 +    let _ = x(&mut &&a);
 +    let _ = x(&&&mut b);
 +    let _ = x(&&ref_a);
 +    {
 +        let b = &mut b;
 +        x(&b);
 +    }
 +
 +    // Issue #8191
 +    let mut x = 5;
 +    let mut x = &mut x;
 +
 +    mut_ref(&mut x);
 +    mut_ref(&mut &mut x);
 +    let y: &mut i32 = &mut x;
 +    let y: &mut i32 = &mut &mut x;
 +
 +    let y = match 0 {
 +        // Don't lint. Removing the borrow would move 'x'
 +        0 => &mut x,
 +        _ => &mut *x,
 +    };
 +    let y: &mut i32 = match 0 {
 +        // Lint here. The type given above triggers auto-borrow.
 +        0 => &mut x,
 +        _ => &mut *x,
 +    };
 +    fn ref_mut_i32(_: &mut i32) {}
 +    ref_mut_i32(match 0 {
 +        // Lint here. The type given above triggers auto-borrow.
 +        0 => &mut x,
 +        _ => &mut *x,
 +    });
 +    // use 'x' after to make sure it's still usable in the fixed code.
 +    *x = 5;
 +
 +    let s = String::new();
 +    // let _ = (&s).len();
 +    // let _ = (&s).capacity();
 +    // let _ = (&&s).capacity();
 +
 +    let x = (1, 2);
 +    let _ = (&x).0;
 +    let x = &x as *const (i32, i32);
 +    let _ = unsafe { (&*x).0 };
 +
 +    // Issue #8367
 +    trait Foo {
 +        fn foo(self);
 +    }
 +    impl Foo for &'_ () {
 +        fn foo(self) {}
 +    }
 +    (&()).foo(); // Don't lint. `()` doesn't implement `Foo`
 +    (&&()).foo();
 +
 +    impl Foo for i32 {
 +        fn foo(self) {}
 +    }
 +    impl Foo for &'_ i32 {
 +        fn foo(self) {}
 +    }
 +    (&5).foo(); // Don't lint. `5` will call `<i32 as Foo>::foo`
 +    (&&5).foo();
 +
 +    trait FooRef {
 +        fn foo_ref(&self);
 +    }
 +    impl FooRef for () {
 +        fn foo_ref(&self) {}
 +    }
 +    impl FooRef for &'_ () {
 +        fn foo_ref(&self) {}
 +    }
 +    (&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
 +
 +    struct S;
 +    impl From<S> for u32 {
 +        fn from(s: S) -> Self {
 +            (&s).into()
 +        }
 +    }
 +    impl From<&S> for u32 {
 +        fn from(s: &S) -> Self {
 +            0
 +        }
 +    }
 +
 +    let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
 +    let _ = std::path::Path::new(".").join(&&".");
 +    deref_target_is_x(&X);
 +    multiple_constraints(&[[""]]);
 +    multiple_constraints_normalizes_to_same(&X, X);
 +    let _ = Some("").unwrap_or(&"");
++    let _ = std::fs::write("x", &"".to_string());
 +
 +    only_sized(&""); // Don't lint. `Sized` is only bound
 +    let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound
 +    let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
 +    ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
 +    refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
 +    multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
 +}
 +
 +#[allow(clippy::needless_borrowed_reference)]
 +fn x(y: &i32) -> i32 {
 +    *y
 +}
 +
 +fn mut_ref(y: &mut i32) {
 +    *y = 5;
 +}
 +
 +fn f<T: Copy>(y: &T) -> T {
 +    *y
 +}
 +
 +fn g(y: &[u8]) -> u8 {
 +    y[0]
 +}
 +
 +trait Trait {}
 +
 +impl<'a> Trait for &'a str {}
 +
 +fn h(_: &dyn Trait) {}
 +
 +#[allow(dead_code)]
 +fn check_expect_suppression() {
 +    let a = 5;
 +    #[expect(clippy::needless_borrow)]
 +    let _ = x(&&a);
 +}
 +
 +#[allow(dead_code)]
 +mod issue9160 {
 +    pub struct S<F> {
 +        f: F,
 +    }
 +
 +    impl<T, F> S<F>
 +    where
 +        F: Fn() -> T,
 +    {
 +        fn calls_field(&self) -> T {
 +            (&self.f)()
 +        }
 +    }
 +
 +    impl<T, F> S<F>
 +    where
 +        F: FnMut() -> T,
 +    {
 +        fn calls_mut_field(&mut self) -> T {
 +            (&mut self.f)()
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +struct X;
 +
 +impl std::ops::Deref for X {
 +    type Target = X;
 +    fn deref(&self) -> &Self::Target {
 +        self
 +    }
 +}
 +
 +fn deref_target_is_x<T>(_: T)
 +where
 +    T: std::ops::Deref<Target = X>,
 +{
 +}
 +
 +fn multiple_constraints<T, U, V, X, Y>(_: T)
 +where
 +    T: IntoIterator<Item = U> + IntoIterator<Item = X>,
 +    U: IntoIterator<Item = V>,
 +    V: AsRef<str>,
 +    X: IntoIterator<Item = Y>,
 +    Y: AsRef<std::ffi::OsStr>,
 +{
 +}
 +
 +fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
 +where
 +    T: std::ops::Deref<Target = U>,
 +    U: std::ops::Deref<Target = V>,
 +{
 +}
 +
 +fn only_sized<T>(_: T) {}
 +
 +fn ref_as_ref_path<T: 'static>(_: &'static T)
 +where
 +    &'static T: AsRef<std::path::Path>,
 +{
 +}
 +
 +trait RefsOnly {
 +    type Referent;
 +}
 +
 +impl<T> RefsOnly for &T {
 +    type Referent = T;
 +}
 +
 +fn refs_only<T, U>(_: T)
 +where
 +    T: RefsOnly<Referent = U>,
 +{
 +}
 +
 +fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
 +where
 +    T: IntoIterator<Item = U>,
 +    U: IntoIterator<Item = V>,
 +    V: AsRef<str>,
 +{
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
 +#[allow(dead_code)]
 +mod copyable_iterator {
 +    #[derive(Clone, Copy)]
 +    struct Iter;
 +    impl Iterator for Iter {
 +        type Item = ();
 +        fn next(&mut self) -> Option<Self::Item> {
 +            None
 +        }
 +    }
 +    fn takes_iter(_: impl Iterator) {}
 +    fn dont_warn(mut x: Iter) {
 +        takes_iter(&mut x);
 +    }
++    #[allow(unused_mut)]
 +    fn warn(mut x: &mut Iter) {
 +        takes_iter(&mut x)
 +    }
 +}
 +
 +mod under_msrv {
 +    #![allow(dead_code)]
 +    #![clippy::msrv = "1.52.0"]
 +
 +    fn foo() {
 +        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
 +    }
 +}
 +
 +mod meets_msrv {
 +    #![allow(dead_code)]
 +    #![clippy::msrv = "1.53.0"]
 +
 +    fn foo() {
 +        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
 +    }
 +}
 +
 +#[allow(unused)]
 +fn issue9383() {
 +    // Should not lint because unions need explicit deref when accessing field
 +    use std::mem::ManuallyDrop;
 +
 +    union Coral {
 +        crab: ManuallyDrop<Vec<i32>>,
 +    }
 +
 +    union Ocean {
 +        coral: ManuallyDrop<Coral>,
 +    }
 +
 +    let mut ocean = Ocean {
 +        coral: ManuallyDrop::new(Coral {
 +            crab: ManuallyDrop::new(vec![1, 2, 3]),
 +        }),
 +    };
 +
 +    unsafe {
 +        ManuallyDrop::drop(&mut (&mut ocean.coral).crab);
 +
 +        (*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]);
 +        ManuallyDrop::drop(&mut (*ocean.coral).crab);
 +
 +        ManuallyDrop::drop(&mut ocean.coral);
 +    }
 +}
++
++#[allow(dead_code)]
++fn closure_test() {
++    let env = "env".to_owned();
++    let arg = "arg".to_owned();
++    let f = |arg| {
++        let loc = "loc".to_owned();
++        let _ = std::fs::write("x", &env); // Don't lint. In environment
++        let _ = std::fs::write("x", &arg);
++        let _ = std::fs::write("x", &loc);
++    };
++    let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
++    f(arg);
++}
++
++#[allow(dead_code)]
++mod significant_drop {
++    #[derive(Debug)]
++    struct X;
++
++    #[derive(Debug)]
++    struct Y;
++
++    impl Drop for Y {
++        fn drop(&mut self) {}
++    }
++
++    fn foo(x: X, y: Y) {
++        debug(&x);
++        debug(&y); // Don't lint. Has significant drop
++    }
++
++    fn debug(_: impl std::fmt::Debug) {}
++}
++
++#[allow(dead_code)]
++mod used_exactly_once {
++    fn foo(x: String) {
++        use_x(&x);
++    }
++    fn use_x(_: impl AsRef<str>) {}
++}
++
++#[allow(dead_code)]
++mod used_more_than_once {
++    fn foo(x: String) {
++        use_x(&x);
++        use_x_again(&x);
++    }
++    fn use_x(_: impl AsRef<str>) {}
++    fn use_x_again(_: impl AsRef<str>) {}
++}
index 5af68706d4ba579311308199b560dbfcad016ad2,0000000000000000000000000000000000000000..8b593268bec2112b82b70c268d4c5d1ffafa9127
mode 100644,000000..100644
--- /dev/null
@@@ -1,178 -1,0 +1,214 @@@
-   --> $DIR/needless_borrow.rs:11:15
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:15:13
++  --> $DIR/needless_borrow.rs:15:15
 +   |
 +LL |     let _ = x(&&a); // warn
 +   |               ^^^ help: change this to: `&a`
 +   |
 +   = note: `-D clippy::needless-borrow` implied by `-D warnings`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:27:13
++  --> $DIR/needless_borrow.rs:19:13
 +   |
 +LL |     mut_ref(&mut &mut b); // warn
 +   |             ^^^^^^^^^^^ help: change this to: `&mut b`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:29:15
++  --> $DIR/needless_borrow.rs:31:13
 +   |
 +LL |             &&a
 +   |             ^^^ help: change this to: `&a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:35:27
++  --> $DIR/needless_borrow.rs:33:15
 +   |
 +LL |         46 => &&a,
 +   |               ^^^ help: change this to: `&a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:42:15
++  --> $DIR/needless_borrow.rs:39:27
 +   |
 +LL |                     break &ref_a;
 +   |                           ^^^^^^ help: change this to: `ref_a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:43:15
++  --> $DIR/needless_borrow.rs:46:15
 +   |
 +LL |     let _ = x(&&&a);
 +   |               ^^^^ help: change this to: `&a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:44:15
++  --> $DIR/needless_borrow.rs:47:15
 +   |
 +LL |     let _ = x(&mut &&a);
 +   |               ^^^^^^^^ help: change this to: `&a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:45:15
++  --> $DIR/needless_borrow.rs:48:15
 +   |
 +LL |     let _ = x(&&&mut b);
 +   |               ^^^^^^^^ help: change this to: `&mut b`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:48:11
++  --> $DIR/needless_borrow.rs:49:15
 +   |
 +LL |     let _ = x(&&ref_a);
 +   |               ^^^^^^^ help: change this to: `ref_a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:55:13
++  --> $DIR/needless_borrow.rs:52:11
 +   |
 +LL |         x(&b);
 +   |           ^^ help: change this to: `b`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:56:13
++  --> $DIR/needless_borrow.rs:59:13
 +   |
 +LL |     mut_ref(&mut x);
 +   |             ^^^^^^ help: change this to: `x`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:57:23
++  --> $DIR/needless_borrow.rs:60:13
 +   |
 +LL |     mut_ref(&mut &mut x);
 +   |             ^^^^^^^^^^^ help: change this to: `x`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:58:23
++  --> $DIR/needless_borrow.rs:61:23
 +   |
 +LL |     let y: &mut i32 = &mut x;
 +   |                       ^^^^^^ help: change this to: `x`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:67:14
++  --> $DIR/needless_borrow.rs:62:23
 +   |
 +LL |     let y: &mut i32 = &mut &mut x;
 +   |                       ^^^^^^^^^^^ help: change this to: `x`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:73:14
++  --> $DIR/needless_borrow.rs:71:14
 +   |
 +LL |         0 => &mut x,
 +   |              ^^^^^^ help: change this to: `x`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:85:13
++  --> $DIR/needless_borrow.rs:77:14
 +   |
 +LL |         0 => &mut x,
 +   |              ^^^^^^ help: change this to: `x`
 +
 +error: this expression borrows a value the compiler would automatically borrow
-   --> $DIR/needless_borrow.rs:87:22
++  --> $DIR/needless_borrow.rs:89:13
 +   |
 +LL |     let _ = (&x).0;
 +   |             ^^^^ help: change this to: `x`
 +
 +error: this expression borrows a value the compiler would automatically borrow
-   --> $DIR/needless_borrow.rs:97:5
++  --> $DIR/needless_borrow.rs:91:22
 +   |
 +LL |     let _ = unsafe { (&*x).0 };
 +   |                      ^^^^^ help: change this to: `(*x)`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:106:5
++  --> $DIR/needless_borrow.rs:101:5
 +   |
 +LL |     (&&()).foo();
 +   |     ^^^^^^ help: change this to: `(&())`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:131:51
++  --> $DIR/needless_borrow.rs:110:5
 +   |
 +LL |     (&&5).foo();
 +   |     ^^^^^ help: change this to: `(&5)`
 +
 +error: the borrowed expression implements the required traits
-   --> $DIR/needless_borrow.rs:132:44
++  --> $DIR/needless_borrow.rs:135:51
 +   |
 +LL |     let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
 +   |                                                   ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
 +
 +error: the borrowed expression implements the required traits
-   --> $DIR/needless_borrow.rs:133:23
++  --> $DIR/needless_borrow.rs:136:44
 +   |
 +LL |     let _ = std::path::Path::new(".").join(&&".");
 +   |                                            ^^^^^ help: change this to: `"."`
 +
 +error: the borrowed expression implements the required traits
-   --> $DIR/needless_borrow.rs:134:26
++  --> $DIR/needless_borrow.rs:137:23
 +   |
 +LL |     deref_target_is_x(&X);
 +   |                       ^^ help: change this to: `X`
 +
 +error: the borrowed expression implements the required traits
-   --> $DIR/needless_borrow.rs:135:45
++  --> $DIR/needless_borrow.rs:138:26
 +   |
 +LL |     multiple_constraints(&[[""]]);
 +   |                          ^^^^^^^ help: change this to: `[[""]]`
 +
 +error: the borrowed expression implements the required traits
-   --> $DIR/needless_borrow.rs:136:32
++  --> $DIR/needless_borrow.rs:139:45
 +   |
 +LL |     multiple_constraints_normalizes_to_same(&X, X);
 +   |                                             ^^ help: change this to: `X`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
-   --> $DIR/needless_borrow.rs:187:13
++  --> $DIR/needless_borrow.rs:140:32
 +   |
 +LL |     let _ = Some("").unwrap_or(&"");
 +   |                                ^^^ help: change this to: `""`
 +
++error: the borrowed expression implements the required traits
++  --> $DIR/needless_borrow.rs:141:33
++   |
++LL |     let _ = std::fs::write("x", &"".to_string());
++   |                                 ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()`
++
 +error: this expression borrows a value the compiler would automatically borrow
-   --> $DIR/needless_borrow.rs:196:13
++  --> $DIR/needless_borrow.rs:192:13
 +   |
 +LL |             (&self.f)()
 +   |             ^^^^^^^^^ help: change this to: `(self.f)`
 +
 +error: this expression borrows a value the compiler would automatically borrow
-   --> $DIR/needless_borrow.rs:298:55
++  --> $DIR/needless_borrow.rs:201:13
 +   |
 +LL |             (&mut self.f)()
 +   |             ^^^^^^^^^^^^^ help: change this to: `(self.f)`
 +
 +error: the borrowed expression implements the required traits
- error: aborting due to 29 previous errors
++  --> $DIR/needless_borrow.rs:286:20
++   |
++LL |         takes_iter(&mut x)
++   |                    ^^^^^^ help: change this to: `x`
++
++error: the borrowed expression implements the required traits
++  --> $DIR/needless_borrow.rs:304:55
 +   |
 +LL |         let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
 +   |                                                       ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
 +
++error: the borrowed expression implements the required traits
++  --> $DIR/needless_borrow.rs:344:37
++   |
++LL |         let _ = std::fs::write("x", &arg);
++   |                                     ^^^^ help: change this to: `arg`
++
++error: the borrowed expression implements the required traits
++  --> $DIR/needless_borrow.rs:345:37
++   |
++LL |         let _ = std::fs::write("x", &loc);
++   |                                     ^^^^ help: change this to: `loc`
++
++error: the borrowed expression implements the required traits
++  --> $DIR/needless_borrow.rs:364:15
++   |
++LL |         debug(&x);
++   |               ^^ help: change this to: `x`
++
++error: the borrowed expression implements the required traits
++  --> $DIR/needless_borrow.rs:374:15
++   |
++LL |         use_x(&x);
++   |               ^^ help: change this to: `x`
++
++error: aborting due to 35 previous errors
 +
index 07d7f0b45b0c2f2171cd29c29a4360deb7cff1ed,0000000000000000000000000000000000000000..bc376d0d7fb391eed0b6806779ed5bfec1063b47
mode 100644,000000..100644
--- /dev/null
@@@ -1,44 -1,0 +1,59 @@@
- #![allow(unused_imports, clippy::redundant_clone)]
 +// run-rustfix
 +
++#![feature(custom_inner_attributes)]
++#![allow(unused, clippy::redundant_clone)]
 +#![warn(clippy::option_as_ref_deref)]
 +
 +use std::ffi::{CString, OsString};
 +use std::ops::{Deref, DerefMut};
 +use std::path::PathBuf;
 +
 +fn main() {
 +    let mut opt = Some(String::from("123"));
 +
 +    let _ = opt.clone().as_deref().map(str::len);
 +
 +    #[rustfmt::skip]
 +    let _ = opt.clone().as_deref()
 +        .map(str::len);
 +
 +    let _ = opt.as_deref_mut();
 +
 +    let _ = opt.as_deref();
 +    let _ = opt.as_deref();
 +    let _ = opt.as_deref_mut();
 +    let _ = opt.as_deref_mut();
 +    let _ = Some(CString::new(vec![]).unwrap()).as_deref();
 +    let _ = Some(OsString::new()).as_deref();
 +    let _ = Some(PathBuf::new()).as_deref();
 +    let _ = Some(Vec::<()>::new()).as_deref();
 +    let _ = Some(Vec::<()>::new()).as_deref_mut();
 +
 +    let _ = opt.as_deref();
 +    let _ = opt.clone().as_deref_mut().map(|x| x.len());
 +
 +    let vc = vec![String::new()];
 +    let _ = Some(1_usize).as_ref().map(|x| vc[*x].as_str()); // should not be linted
 +
 +    let _: Option<&str> = Some(&String::new()).as_ref().map(|x| x.as_str()); // should not be linted
 +
 +    let _ = opt.as_deref();
 +    let _ = opt.as_deref_mut();
 +
 +    // Issue #5927
 +    let _ = opt.as_deref();
 +}
++
++fn msrv_1_39() {
++    #![clippy::msrv = "1.39"]
++
++    let opt = Some(String::from("123"));
++    let _ = opt.as_ref().map(String::as_str);
++}
++
++fn msrv_1_40() {
++    #![clippy::msrv = "1.40"]
++
++    let opt = Some(String::from("123"));
++    let _ = opt.as_deref();
++}
index 6ae059c9425d35480c7afcc00ab7e6b63b0db3cf,0000000000000000000000000000000000000000..ba3a2eedc225cf42b27803ce102c4a4713344fb6
mode 100644,000000..100644
--- /dev/null
@@@ -1,47 -1,0 +1,62 @@@
- #![allow(unused_imports, clippy::redundant_clone)]
 +// run-rustfix
 +
++#![feature(custom_inner_attributes)]
++#![allow(unused, clippy::redundant_clone)]
 +#![warn(clippy::option_as_ref_deref)]
 +
 +use std::ffi::{CString, OsString};
 +use std::ops::{Deref, DerefMut};
 +use std::path::PathBuf;
 +
 +fn main() {
 +    let mut opt = Some(String::from("123"));
 +
 +    let _ = opt.clone().as_ref().map(Deref::deref).map(str::len);
 +
 +    #[rustfmt::skip]
 +    let _ = opt.clone()
 +        .as_ref().map(
 +            Deref::deref
 +        )
 +        .map(str::len);
 +
 +    let _ = opt.as_mut().map(DerefMut::deref_mut);
 +
 +    let _ = opt.as_ref().map(String::as_str);
 +    let _ = opt.as_ref().map(|x| x.as_str());
 +    let _ = opt.as_mut().map(String::as_mut_str);
 +    let _ = opt.as_mut().map(|x| x.as_mut_str());
 +    let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str);
 +    let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str);
 +    let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path);
 +    let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice);
 +    let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice);
 +
 +    let _ = opt.as_ref().map(|x| x.deref());
 +    let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len());
 +
 +    let vc = vec![String::new()];
 +    let _ = Some(1_usize).as_ref().map(|x| vc[*x].as_str()); // should not be linted
 +
 +    let _: Option<&str> = Some(&String::new()).as_ref().map(|x| x.as_str()); // should not be linted
 +
 +    let _ = opt.as_ref().map(|x| &**x);
 +    let _ = opt.as_mut().map(|x| &mut **x);
 +
 +    // Issue #5927
 +    let _ = opt.as_ref().map(std::ops::Deref::deref);
 +}
++
++fn msrv_1_39() {
++    #![clippy::msrv = "1.39"]
++
++    let opt = Some(String::from("123"));
++    let _ = opt.as_ref().map(String::as_str);
++}
++
++fn msrv_1_40() {
++    #![clippy::msrv = "1.40"]
++
++    let opt = Some(String::from("123"));
++    let _ = opt.as_ref().map(String::as_str);
++}
index 62f28232475282a1a51ade866365560ec6031386,0000000000000000000000000000000000000000..7de8b3b6ba4355c55d3fb0a7712f7e43253e1596
mode 100644,000000..100644
--- /dev/null
@@@ -1,110 -1,0 +1,116 @@@
-   --> $DIR/option_as_ref_deref.rs:13:13
 +error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead
-   --> $DIR/option_as_ref_deref.rs:16:13
++  --> $DIR/option_as_ref_deref.rs:14:13
 +   |
 +LL |     let _ = opt.clone().as_ref().map(Deref::deref).map(str::len);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()`
 +   |
 +   = note: `-D clippy::option-as-ref-deref` implied by `-D warnings`
 +
 +error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead
-   --> $DIR/option_as_ref_deref.rs:22:13
++  --> $DIR/option_as_ref_deref.rs:17:13
 +   |
 +LL |       let _ = opt.clone()
 +   |  _____________^
 +LL | |         .as_ref().map(
 +LL | |             Deref::deref
 +LL | |         )
 +   | |_________^ help: try using as_deref instead: `opt.clone().as_deref()`
 +
 +error: called `.as_mut().map(DerefMut::deref_mut)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
-   --> $DIR/option_as_ref_deref.rs:24:13
++  --> $DIR/option_as_ref_deref.rs:23:13
 +   |
 +LL |     let _ = opt.as_mut().map(DerefMut::deref_mut);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
 +
 +error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
-   --> $DIR/option_as_ref_deref.rs:25:13
++  --> $DIR/option_as_ref_deref.rs:25:13
 +   |
 +LL |     let _ = opt.as_ref().map(String::as_str);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
 +
 +error: called `.as_ref().map(|x| x.as_str())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
-   --> $DIR/option_as_ref_deref.rs:26:13
++  --> $DIR/option_as_ref_deref.rs:26:13
 +   |
 +LL |     let _ = opt.as_ref().map(|x| x.as_str());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
 +
 +error: called `.as_mut().map(String::as_mut_str)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
-   --> $DIR/option_as_ref_deref.rs:27:13
++  --> $DIR/option_as_ref_deref.rs:27:13
 +   |
 +LL |     let _ = opt.as_mut().map(String::as_mut_str);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
 +
 +error: called `.as_mut().map(|x| x.as_mut_str())` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
-   --> $DIR/option_as_ref_deref.rs:28:13
++  --> $DIR/option_as_ref_deref.rs:28:13
 +   |
 +LL |     let _ = opt.as_mut().map(|x| x.as_mut_str());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
 +
 +error: called `.as_ref().map(CString::as_c_str)` on an Option value. This can be done more directly by calling `Some(CString::new(vec![]).unwrap()).as_deref()` instead
-   --> $DIR/option_as_ref_deref.rs:29:13
++  --> $DIR/option_as_ref_deref.rs:29:13
 +   |
 +LL |     let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()`
 +
 +error: called `.as_ref().map(OsString::as_os_str)` on an Option value. This can be done more directly by calling `Some(OsString::new()).as_deref()` instead
-   --> $DIR/option_as_ref_deref.rs:30:13
++  --> $DIR/option_as_ref_deref.rs:30:13
 +   |
 +LL |     let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()`
 +
 +error: called `.as_ref().map(PathBuf::as_path)` on an Option value. This can be done more directly by calling `Some(PathBuf::new()).as_deref()` instead
-   --> $DIR/option_as_ref_deref.rs:31:13
++  --> $DIR/option_as_ref_deref.rs:31:13
 +   |
 +LL |     let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()`
 +
 +error: called `.as_ref().map(Vec::as_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref()` instead
-   --> $DIR/option_as_ref_deref.rs:32:13
++  --> $DIR/option_as_ref_deref.rs:32:13
 +   |
 +LL |     let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()`
 +
 +error: called `.as_mut().map(Vec::as_mut_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref_mut()` instead
-   --> $DIR/option_as_ref_deref.rs:34:13
++  --> $DIR/option_as_ref_deref.rs:33:13
 +   |
 +LL |     let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()`
 +
 +error: called `.as_ref().map(|x| x.deref())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
-   --> $DIR/option_as_ref_deref.rs:35:13
++  --> $DIR/option_as_ref_deref.rs:35:13
 +   |
 +LL |     let _ = opt.as_ref().map(|x| x.deref());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
 +
 +error: called `.as_mut().map(|x| x.deref_mut())` on an Option value. This can be done more directly by calling `opt.clone().as_deref_mut()` instead
-   --> $DIR/option_as_ref_deref.rs:42:13
++  --> $DIR/option_as_ref_deref.rs:36:13
 +   |
 +LL |     let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()`
 +
 +error: called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
-   --> $DIR/option_as_ref_deref.rs:43:13
++  --> $DIR/option_as_ref_deref.rs:43:13
 +   |
 +LL |     let _ = opt.as_ref().map(|x| &**x);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
 +
 +error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
-   --> $DIR/option_as_ref_deref.rs:46:13
++  --> $DIR/option_as_ref_deref.rs:44:13
 +   |
 +LL |     let _ = opt.as_mut().map(|x| &mut **x);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
 +
 +error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
- error: aborting due to 17 previous errors
++  --> $DIR/option_as_ref_deref.rs:47:13
 +   |
 +LL |     let _ = opt.as_ref().map(std::ops::Deref::deref);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
 +
++error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
++  --> $DIR/option_as_ref_deref.rs:61:13
++   |
++LL |     let _ = opt.as_ref().map(String::as_str);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
++
++error: aborting due to 18 previous errors
 +
index 896430780ea80918dca8fb2e4ac8f0e2e3254508,0000000000000000000000000000000000000000..23b1aa8bebd53749cf05d23ce27ff7a4195b42d1
mode 100644,000000..100644
--- /dev/null
@@@ -1,228 -1,0 +1,239 @@@
 +// run-rustfix
 +#![warn(clippy::or_fun_call)]
 +#![allow(dead_code)]
 +#![allow(clippy::borrow_as_ptr, clippy::uninlined_format_args, clippy::unnecessary_wraps)]
 +
 +use std::collections::BTreeMap;
 +use std::collections::HashMap;
 +use std::time::Duration;
 +
 +/// Checks implementation of the `OR_FUN_CALL` lint.
 +fn or_fun_call() {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo
 +        }
 +    }
 +
 +    struct FakeDefault;
 +    impl FakeDefault {
 +        fn default() -> Self {
 +            FakeDefault
 +        }
 +    }
 +
 +    impl Default for FakeDefault {
 +        fn default() -> Self {
 +            FakeDefault
 +        }
 +    }
 +
 +    enum Enum {
 +        A(i32),
 +    }
 +
 +    fn make<T>() -> T {
 +        unimplemented!();
 +    }
 +
 +    let with_enum = Some(Enum::A(1));
 +    with_enum.unwrap_or(Enum::A(5));
 +
 +    let with_const_fn = Some(Duration::from_secs(1));
 +    with_const_fn.unwrap_or(Duration::from_secs(5));
 +
 +    let with_constructor = Some(vec![1]);
 +    with_constructor.unwrap_or_else(make);
 +
 +    let with_new = Some(vec![1]);
 +    with_new.unwrap_or_default();
 +
 +    let with_const_args = Some(vec![1]);
 +    with_const_args.unwrap_or_else(|| Vec::with_capacity(12));
 +
 +    let with_err: Result<_, ()> = Ok(vec![1]);
 +    with_err.unwrap_or_else(|_| make());
 +
 +    let with_err_args: Result<_, ()> = Ok(vec![1]);
 +    with_err_args.unwrap_or_else(|_| Vec::with_capacity(12));
 +
 +    let with_default_trait = Some(1);
 +    with_default_trait.unwrap_or_default();
 +
 +    let with_default_type = Some(1);
 +    with_default_type.unwrap_or_default();
 +
 +    let self_default = None::<FakeDefault>;
 +    self_default.unwrap_or_else(<FakeDefault>::default);
 +
 +    let real_default = None::<FakeDefault>;
 +    real_default.unwrap_or_default();
 +
 +    let with_vec = Some(vec![1]);
 +    with_vec.unwrap_or_default();
 +
 +    let without_default = Some(Foo);
 +    without_default.unwrap_or_else(Foo::new);
 +
 +    let mut map = HashMap::<u64, String>::new();
 +    map.entry(42).or_default();
 +
 +    let mut map_vec = HashMap::<u64, Vec<i32>>::new();
 +    map_vec.entry(42).or_default();
 +
 +    let mut btree = BTreeMap::<u64, String>::new();
 +    btree.entry(42).or_default();
 +
 +    let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
 +    btree_vec.entry(42).or_default();
 +
 +    let stringy = Some(String::new());
 +    let _ = stringy.unwrap_or_default();
 +
 +    let opt = Some(1);
 +    let hello = "Hello";
 +    let _ = opt.ok_or(format!("{} world.", hello));
 +
 +    // index
 +    let map = HashMap::<u64, u64>::new();
 +    let _ = Some(1).unwrap_or_else(|| map[&1]);
 +    let map = BTreeMap::<u64, u64>::new();
 +    let _ = Some(1).unwrap_or_else(|| map[&1]);
 +    // don't lint index vec
 +    let vec = vec![1];
 +    let _ = Some(1).unwrap_or(vec[1]);
 +}
 +
 +struct Foo(u8);
 +struct Bar(String, Duration);
 +#[rustfmt::skip]
 +fn test_or_with_ctors() {
 +    let opt = Some(1);
 +    let opt_opt = Some(Some(1));
 +    // we also test for const promotion, this makes sure we don't hit that
 +    let two = 2;
 +
 +    let _ = opt_opt.unwrap_or(Some(2));
 +    let _ = opt_opt.unwrap_or(Some(two));
 +    let _ = opt.ok_or(Some(2));
 +    let _ = opt.ok_or(Some(two));
 +    let _ = opt.ok_or(Foo(2));
 +    let _ = opt.ok_or(Foo(two));
 +    let _ = opt.or(Some(2));
 +    let _ = opt.or(Some(two));
 +
 +    let _ = Some("a".to_string()).or_else(|| Some("b".to_string()));
 +
 +    let b = "b".to_string();
 +    let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
 +        .or(Some(Bar(b, Duration::from_secs(2))));
 +
 +    let vec = vec!["foo"];
 +    let _ = opt.ok_or(vec.len());
 +
 +    let array = ["foo"];
 +    let _ = opt.ok_or(array.len());
 +
 +    let slice = &["foo"][..];
 +    let _ = opt.ok_or(slice.len());
 +
 +    let string = "foo";
 +    let _ = opt.ok_or(string.len());
 +}
 +
 +// Issue 4514 - early return
 +fn f() -> Option<()> {
 +    let a = Some(1);
 +    let b = 1i32;
 +
 +    let _ = a.unwrap_or(b.checked_mul(3)?.min(240));
 +
 +    Some(())
 +}
 +
 +mod issue6675 {
 +    unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T {
 +        #[allow(unused)]
 +        let x = vec![0; 1000]; // future-proofing, make this function expensive.
 +        &*p
 +    }
 +
 +    unsafe fn foo() {
 +        let s = "test".to_owned();
 +        let s = &s as *const _;
 +        None.unwrap_or_else(|| ptr_to_ref(s));
 +    }
 +
 +    fn bar() {
 +        let s = "test".to_owned();
 +        let s = &s as *const _;
 +        None.unwrap_or_else(|| unsafe { ptr_to_ref(s) });
 +        #[rustfmt::skip]
 +        None.unwrap_or_else(|| unsafe { ptr_to_ref(s) });
 +    }
 +}
 +
 +mod issue8239 {
 +    fn more_than_max_suggestion_highest_lines_0() {
 +        let frames = Vec::new();
 +        frames
 +            .iter()
 +            .map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or_default();
 +    }
 +
 +    fn more_to_max_suggestion_highest_lines_1() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        iter.map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                let _ = "";
 +                let _ = "";
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or_default();
 +    }
 +
 +    fn equal_to_max_suggestion_highest_lines() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        iter.map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                let _ = "";
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or_default();
 +    }
 +
 +    fn less_than_max_suggestion_highest_lines() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        let map = iter.map(|f: &String| f.to_lowercase());
 +        map.reduce(|mut acc, f| {
 +            acc.push_str(&f);
 +            acc
 +        })
 +        .unwrap_or_default();
 +    }
 +}
 +
++mod issue9608 {
++    fn sig_drop() {
++        enum X {
++            X(std::fs::File),
++            Y(u32),
++        }
++
++        let _ = None.unwrap_or(X::Y(0));
++    }
++}
++
 +fn main() {}
index 2473163d4fd2f4e74752d1f1178d079525ba8c78,0000000000000000000000000000000000000000..039998f22dd7147b9caf5134f1fcb5e139b11d1d
mode 100644,000000..100644
--- /dev/null
@@@ -1,228 -1,0 +1,239 @@@
 +// run-rustfix
 +#![warn(clippy::or_fun_call)]
 +#![allow(dead_code)]
 +#![allow(clippy::borrow_as_ptr, clippy::uninlined_format_args, clippy::unnecessary_wraps)]
 +
 +use std::collections::BTreeMap;
 +use std::collections::HashMap;
 +use std::time::Duration;
 +
 +/// Checks implementation of the `OR_FUN_CALL` lint.
 +fn or_fun_call() {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo
 +        }
 +    }
 +
 +    struct FakeDefault;
 +    impl FakeDefault {
 +        fn default() -> Self {
 +            FakeDefault
 +        }
 +    }
 +
 +    impl Default for FakeDefault {
 +        fn default() -> Self {
 +            FakeDefault
 +        }
 +    }
 +
 +    enum Enum {
 +        A(i32),
 +    }
 +
 +    fn make<T>() -> T {
 +        unimplemented!();
 +    }
 +
 +    let with_enum = Some(Enum::A(1));
 +    with_enum.unwrap_or(Enum::A(5));
 +
 +    let with_const_fn = Some(Duration::from_secs(1));
 +    with_const_fn.unwrap_or(Duration::from_secs(5));
 +
 +    let with_constructor = Some(vec![1]);
 +    with_constructor.unwrap_or(make());
 +
 +    let with_new = Some(vec![1]);
 +    with_new.unwrap_or(Vec::new());
 +
 +    let with_const_args = Some(vec![1]);
 +    with_const_args.unwrap_or(Vec::with_capacity(12));
 +
 +    let with_err: Result<_, ()> = Ok(vec![1]);
 +    with_err.unwrap_or(make());
 +
 +    let with_err_args: Result<_, ()> = Ok(vec![1]);
 +    with_err_args.unwrap_or(Vec::with_capacity(12));
 +
 +    let with_default_trait = Some(1);
 +    with_default_trait.unwrap_or(Default::default());
 +
 +    let with_default_type = Some(1);
 +    with_default_type.unwrap_or(u64::default());
 +
 +    let self_default = None::<FakeDefault>;
 +    self_default.unwrap_or(<FakeDefault>::default());
 +
 +    let real_default = None::<FakeDefault>;
 +    real_default.unwrap_or(<FakeDefault as Default>::default());
 +
 +    let with_vec = Some(vec![1]);
 +    with_vec.unwrap_or(vec![]);
 +
 +    let without_default = Some(Foo);
 +    without_default.unwrap_or(Foo::new());
 +
 +    let mut map = HashMap::<u64, String>::new();
 +    map.entry(42).or_insert(String::new());
 +
 +    let mut map_vec = HashMap::<u64, Vec<i32>>::new();
 +    map_vec.entry(42).or_insert(vec![]);
 +
 +    let mut btree = BTreeMap::<u64, String>::new();
 +    btree.entry(42).or_insert(String::new());
 +
 +    let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
 +    btree_vec.entry(42).or_insert(vec![]);
 +
 +    let stringy = Some(String::new());
 +    let _ = stringy.unwrap_or(String::new());
 +
 +    let opt = Some(1);
 +    let hello = "Hello";
 +    let _ = opt.ok_or(format!("{} world.", hello));
 +
 +    // index
 +    let map = HashMap::<u64, u64>::new();
 +    let _ = Some(1).unwrap_or(map[&1]);
 +    let map = BTreeMap::<u64, u64>::new();
 +    let _ = Some(1).unwrap_or(map[&1]);
 +    // don't lint index vec
 +    let vec = vec![1];
 +    let _ = Some(1).unwrap_or(vec[1]);
 +}
 +
 +struct Foo(u8);
 +struct Bar(String, Duration);
 +#[rustfmt::skip]
 +fn test_or_with_ctors() {
 +    let opt = Some(1);
 +    let opt_opt = Some(Some(1));
 +    // we also test for const promotion, this makes sure we don't hit that
 +    let two = 2;
 +
 +    let _ = opt_opt.unwrap_or(Some(2));
 +    let _ = opt_opt.unwrap_or(Some(two));
 +    let _ = opt.ok_or(Some(2));
 +    let _ = opt.ok_or(Some(two));
 +    let _ = opt.ok_or(Foo(2));
 +    let _ = opt.ok_or(Foo(two));
 +    let _ = opt.or(Some(2));
 +    let _ = opt.or(Some(two));
 +
 +    let _ = Some("a".to_string()).or(Some("b".to_string()));
 +
 +    let b = "b".to_string();
 +    let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
 +        .or(Some(Bar(b, Duration::from_secs(2))));
 +
 +    let vec = vec!["foo"];
 +    let _ = opt.ok_or(vec.len());
 +
 +    let array = ["foo"];
 +    let _ = opt.ok_or(array.len());
 +
 +    let slice = &["foo"][..];
 +    let _ = opt.ok_or(slice.len());
 +
 +    let string = "foo";
 +    let _ = opt.ok_or(string.len());
 +}
 +
 +// Issue 4514 - early return
 +fn f() -> Option<()> {
 +    let a = Some(1);
 +    let b = 1i32;
 +
 +    let _ = a.unwrap_or(b.checked_mul(3)?.min(240));
 +
 +    Some(())
 +}
 +
 +mod issue6675 {
 +    unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T {
 +        #[allow(unused)]
 +        let x = vec![0; 1000]; // future-proofing, make this function expensive.
 +        &*p
 +    }
 +
 +    unsafe fn foo() {
 +        let s = "test".to_owned();
 +        let s = &s as *const _;
 +        None.unwrap_or(ptr_to_ref(s));
 +    }
 +
 +    fn bar() {
 +        let s = "test".to_owned();
 +        let s = &s as *const _;
 +        None.unwrap_or(unsafe { ptr_to_ref(s) });
 +        #[rustfmt::skip]
 +        None.unwrap_or( unsafe { ptr_to_ref(s) }    );
 +    }
 +}
 +
 +mod issue8239 {
 +    fn more_than_max_suggestion_highest_lines_0() {
 +        let frames = Vec::new();
 +        frames
 +            .iter()
 +            .map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or(String::new());
 +    }
 +
 +    fn more_to_max_suggestion_highest_lines_1() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        iter.map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                let _ = "";
 +                let _ = "";
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or(String::new());
 +    }
 +
 +    fn equal_to_max_suggestion_highest_lines() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        iter.map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                let _ = "";
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or(String::new());
 +    }
 +
 +    fn less_than_max_suggestion_highest_lines() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        let map = iter.map(|f: &String| f.to_lowercase());
 +        map.reduce(|mut acc, f| {
 +            acc.push_str(&f);
 +            acc
 +        })
 +        .unwrap_or(String::new());
 +    }
 +}
 +
++mod issue9608 {
++    fn sig_drop() {
++        enum X {
++            X(std::fs::File),
++            Y(u32),
++        }
++
++        let _ = None.unwrap_or(X::Y(0));
++    }
++}
++
 +fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..668545da8441952687a8a510735f6cdbd43b855f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++#![allow(unused)]
++#![warn(clippy::partial_pub_fields)]
++
++fn main() {
++    use std::collections::HashMap;
++
++    #[derive(Default)]
++    pub struct FileSet {
++        files: HashMap<String, u32>,
++        pub paths: HashMap<u32, String>,
++    }
++
++    pub struct Color {
++        pub r: u8,
++        pub g: u8,
++        b: u8,
++    }
++
++    pub struct Point(i32, pub i32);
++
++    pub struct Visibility {
++        r#pub: bool,
++        pub pos: u32,
++    }
++
++    // Don't lint on empty structs;
++    pub struct Empty1;
++    pub struct Empty2();
++    pub struct Empty3 {};
++
++    // Don't lint on structs with one field.
++    pub struct Single1(i32);
++    pub struct Single2(pub i32);
++    pub struct Single3 {
++        v1: i32,
++    }
++    pub struct Single4 {
++        pub v1: i32,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..84cfc1a91940dcc9e69c0ac3945eb793867b5fd7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++error: mixed usage of pub and non-pub fields
++  --> $DIR/partial_pub_fields.rs:10:9
++   |
++LL |         pub paths: HashMap<u32, String>,
++   |         ^^^
++   |
++   = help: consider using private field here
++   = note: `-D clippy::partial-pub-fields` implied by `-D warnings`
++
++error: mixed usage of pub and non-pub fields
++  --> $DIR/partial_pub_fields.rs:16:9
++   |
++LL |         b: u8,
++   |         ^
++   |
++   = help: consider using public field here
++
++error: mixed usage of pub and non-pub fields
++  --> $DIR/partial_pub_fields.rs:19:27
++   |
++LL |     pub struct Point(i32, pub i32);
++   |                           ^^^
++   |
++   = help: consider using private field here
++
++error: mixed usage of pub and non-pub fields
++  --> $DIR/partial_pub_fields.rs:23:9
++   |
++LL |         pub pos: u32,
++   |         ^^^
++   |
++   = help: consider using private field here
++
++error: aborting due to 4 previous errors
++
index fd15001e540c6021d472bfd293fff12a53dec206,0000000000000000000000000000000000000000..5f54101ca15ad176b070974d1be290bc9aaf0374
mode 100644,000000..100644
--- /dev/null
@@@ -1,209 -1,0 +1,237 @@@
- use std::path::PathBuf;
 +#![feature(lint_reasons)]
 +#![allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
 +#![warn(clippy::ptr_arg)]
 +
 +use std::borrow::Cow;
++use std::path::{Path, PathBuf};
 +
 +fn do_vec(x: &Vec<i64>) {
 +    //Nothing here
 +}
 +
 +fn do_vec_mut(x: &mut Vec<i64>) {
 +    //Nothing here
 +}
 +
 +fn do_str(x: &String) {
 +    //Nothing here either
 +}
 +
 +fn do_str_mut(x: &mut String) {
 +    //Nothing here either
 +}
 +
 +fn do_path(x: &PathBuf) {
 +    //Nothing here either
 +}
 +
 +fn do_path_mut(x: &mut PathBuf) {
 +    //Nothing here either
 +}
 +
 +fn main() {}
 +
 +trait Foo {
 +    type Item;
 +    fn do_vec(x: &Vec<i64>);
 +    fn do_item(x: &Self::Item);
 +}
 +
 +struct Bar;
 +
 +// no error, in trait impl (#425)
 +impl Foo for Bar {
 +    type Item = Vec<u8>;
 +    fn do_vec(x: &Vec<i64>) {}
 +    fn do_item(x: &Vec<u8>) {}
 +}
 +
 +fn cloned(x: &Vec<u8>) -> Vec<u8> {
 +    let e = x.clone();
 +    let f = e.clone(); // OK
 +    let g = x;
 +    let h = g.clone();
 +    let i = (e).clone();
 +    x.clone()
 +}
 +
 +fn str_cloned(x: &String) -> String {
 +    let a = x.clone();
 +    let b = x.clone();
 +    let c = b.clone();
 +    let d = a.clone().clone().clone();
 +    x.clone()
 +}
 +
 +fn path_cloned(x: &PathBuf) -> PathBuf {
 +    let a = x.clone();
 +    let b = x.clone();
 +    let c = b.clone();
 +    let d = a.clone().clone().clone();
 +    x.clone()
 +}
 +
 +fn false_positive_capacity(x: &Vec<u8>, y: &String) {
 +    let a = x.capacity();
 +    let b = y.clone();
 +    let c = y.as_str();
 +}
 +
 +fn false_positive_capacity_too(x: &String) -> String {
 +    if x.capacity() > 1024 {
 +        panic!("Too large!");
 +    }
 +    x.clone()
 +}
 +
 +#[allow(dead_code)]
 +fn test_cow_with_ref(c: &Cow<[i32]>) {}
 +
 +fn test_cow(c: Cow<[i32]>) {
 +    let _c = c;
 +}
 +
 +trait Foo2 {
 +    fn do_string(&self);
 +}
 +
 +// no error for &self references where self is of type String (#2293)
 +impl Foo2 for String {
 +    fn do_string(&self) {}
 +}
 +
 +// Check that the allow attribute on parameters is honored
 +mod issue_5644 {
 +    use std::borrow::Cow;
 +    use std::path::PathBuf;
 +
 +    fn allowed(
 +        #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
 +        #[allow(clippy::ptr_arg)] _s: &String,
 +        #[allow(clippy::ptr_arg)] _p: &PathBuf,
 +        #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
 +        #[expect(clippy::ptr_arg)] _expect: &Cow<[i32]>,
 +    ) {
 +    }
 +
 +    fn some_allowed(#[allow(clippy::ptr_arg)] _v: &Vec<u32>, _s: &String) {}
 +
 +    struct S;
 +    impl S {
 +        fn allowed(
 +            #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
 +            #[allow(clippy::ptr_arg)] _s: &String,
 +            #[allow(clippy::ptr_arg)] _p: &PathBuf,
 +            #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
 +            #[expect(clippy::ptr_arg)] _expect: &Cow<[i32]>,
 +        ) {
 +        }
 +    }
 +
 +    trait T {
 +        fn allowed(
 +            #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
 +            #[allow(clippy::ptr_arg)] _s: &String,
 +            #[allow(clippy::ptr_arg)] _p: &PathBuf,
 +            #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
 +            #[expect(clippy::ptr_arg)] _expect: &Cow<[i32]>,
 +        ) {
 +        }
 +    }
 +}
 +
 +mod issue6509 {
 +    use std::path::PathBuf;
 +
 +    fn foo_vec(vec: &Vec<u8>) {
 +        let _ = vec.clone().pop();
 +        let _ = vec.clone().clone();
 +    }
 +
 +    fn foo_path(path: &PathBuf) {
 +        let _ = path.clone().pop();
 +        let _ = path.clone().clone();
 +    }
 +
 +    fn foo_str(str: &PathBuf) {
 +        let _ = str.clone().pop();
 +        let _ = str.clone().clone();
 +    }
 +}
 +
 +fn mut_vec_slice_methods(v: &mut Vec<u32>) {
 +    v.copy_within(1..5, 10);
 +}
 +
 +fn mut_vec_vec_methods(v: &mut Vec<u32>) {
 +    v.clear();
 +}
 +
 +fn vec_contains(v: &Vec<u32>) -> bool {
 +    [vec![], vec![0]].as_slice().contains(v)
 +}
 +
 +fn fn_requires_vec(v: &Vec<u32>) -> bool {
 +    vec_contains(v)
 +}
 +
 +fn impl_fn_requires_vec(v: &Vec<u32>, f: impl Fn(&Vec<u32>)) {
 +    f(v);
 +}
 +
 +fn dyn_fn_requires_vec(v: &Vec<u32>, f: &dyn Fn(&Vec<u32>)) {
 +    f(v);
 +}
 +
 +// No error for types behind an alias (#7699)
 +type A = Vec<u8>;
 +fn aliased(a: &A) {}
 +
 +// Issue #8366
 +pub trait Trait {
 +    fn f(v: &mut Vec<i32>);
 +    fn f2(v: &mut Vec<i32>) {}
 +}
 +
 +// Issue #8463
 +fn two_vecs(a: &mut Vec<u32>, b: &mut Vec<u32>) {
 +    a.push(0);
 +    a.push(0);
 +    a.push(0);
 +    b.push(1);
 +}
 +
 +// Issue #8495
 +fn cow_conditional_to_mut(a: &mut Cow<str>) {
 +    if a.is_empty() {
 +        a.to_mut().push_str("foo");
 +    }
 +}
++
++// Issue #9542
++fn dyn_trait_ok(a: &mut Vec<u32>, b: &mut String, c: &mut PathBuf) {
++    trait T {}
++    impl<U> T for Vec<U> {}
++    impl T for String {}
++    impl T for PathBuf {}
++    fn takes_dyn(_: &mut dyn T) {}
++
++    takes_dyn(a);
++    takes_dyn(b);
++    takes_dyn(c);
++}
++
++fn dyn_trait(a: &mut Vec<u32>, b: &mut String, c: &mut PathBuf) {
++    trait T {}
++    impl<U> T for Vec<U> {}
++    impl<U> T for [U] {}
++    impl T for String {}
++    impl T for str {}
++    impl T for PathBuf {}
++    impl T for Path {}
++    fn takes_dyn(_: &mut dyn T) {}
++
++    takes_dyn(a);
++    takes_dyn(b);
++    takes_dyn(c);
++}
index d64b5f454a5aa985f2d966e153befb4cb9d07e8d,0000000000000000000000000000000000000000..6b4de98ce88c6d567f0553562356a78599670590
mode 100644,000000..100644
--- /dev/null
@@@ -1,166 -1,0 +1,184 @@@
- error: aborting due to 17 previous errors
 +error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
 +  --> $DIR/ptr_arg.rs:8:14
 +   |
 +LL | fn do_vec(x: &Vec<i64>) {
 +   |              ^^^^^^^^^ help: change this to: `&[i64]`
 +   |
 +   = note: `-D clippy::ptr-arg` implied by `-D warnings`
 +
 +error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
 +  --> $DIR/ptr_arg.rs:12:18
 +   |
 +LL | fn do_vec_mut(x: &mut Vec<i64>) {
 +   |                  ^^^^^^^^^^^^^ help: change this to: `&mut [i64]`
 +
 +error: writing `&String` instead of `&str` involves a new object where a slice will do
 +  --> $DIR/ptr_arg.rs:16:14
 +   |
 +LL | fn do_str(x: &String) {
 +   |              ^^^^^^^ help: change this to: `&str`
 +
 +error: writing `&mut String` instead of `&mut str` involves a new object where a slice will do
 +  --> $DIR/ptr_arg.rs:20:18
 +   |
 +LL | fn do_str_mut(x: &mut String) {
 +   |                  ^^^^^^^^^^^ help: change this to: `&mut str`
 +
 +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
 +  --> $DIR/ptr_arg.rs:24:15
 +   |
 +LL | fn do_path(x: &PathBuf) {
 +   |               ^^^^^^^^ help: change this to: `&Path`
 +
 +error: writing `&mut PathBuf` instead of `&mut Path` involves a new object where a slice will do
 +  --> $DIR/ptr_arg.rs:28:19
 +   |
 +LL | fn do_path_mut(x: &mut PathBuf) {
 +   |                   ^^^^^^^^^^^^ help: change this to: `&mut Path`
 +
 +error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
 +  --> $DIR/ptr_arg.rs:36:18
 +   |
 +LL |     fn do_vec(x: &Vec<i64>);
 +   |                  ^^^^^^^^^ help: change this to: `&[i64]`
 +
 +error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
 +  --> $DIR/ptr_arg.rs:49:14
 +   |
 +LL | fn cloned(x: &Vec<u8>) -> Vec<u8> {
 +   |              ^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL ~ fn cloned(x: &[u8]) -> Vec<u8> {
 +LL ~     let e = x.to_owned();
 +LL |     let f = e.clone(); // OK
 +LL |     let g = x;
 +LL ~     let h = g.to_owned();
 +LL |     let i = (e).clone();
 +LL ~     x.to_owned()
 +   |
 +
 +error: writing `&String` instead of `&str` involves a new object where a slice will do
 +  --> $DIR/ptr_arg.rs:58:18
 +   |
 +LL | fn str_cloned(x: &String) -> String {
 +   |                  ^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL ~ fn str_cloned(x: &str) -> String {
 +LL ~     let a = x.to_owned();
 +LL ~     let b = x.to_owned();
 +LL |     let c = b.clone();
 +LL |     let d = a.clone().clone().clone();
 +LL ~     x.to_owned()
 +   |
 +
 +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
 +  --> $DIR/ptr_arg.rs:66:19
 +   |
 +LL | fn path_cloned(x: &PathBuf) -> PathBuf {
 +   |                   ^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL ~ fn path_cloned(x: &Path) -> PathBuf {
 +LL ~     let a = x.to_path_buf();
 +LL ~     let b = x.to_path_buf();
 +LL |     let c = b.clone();
 +LL |     let d = a.clone().clone().clone();
 +LL ~     x.to_path_buf()
 +   |
 +
 +error: writing `&String` instead of `&str` involves a new object where a slice will do
 +  --> $DIR/ptr_arg.rs:74:44
 +   |
 +LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) {
 +   |                                            ^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL ~ fn false_positive_capacity(x: &Vec<u8>, y: &str) {
 +LL |     let a = x.capacity();
 +LL ~     let b = y.to_owned();
 +LL ~     let c = y;
 +   |
 +
 +error: using a reference to `Cow` is not recommended
 +  --> $DIR/ptr_arg.rs:88:25
 +   |
 +LL | fn test_cow_with_ref(c: &Cow<[i32]>) {}
 +   |                         ^^^^^^^^^^^ help: change this to: `&[i32]`
 +
 +error: writing `&String` instead of `&str` involves a new object where a slice will do
 +  --> $DIR/ptr_arg.rs:117:66
 +   |
 +LL |     fn some_allowed(#[allow(clippy::ptr_arg)] _v: &Vec<u32>, _s: &String) {}
 +   |                                                                  ^^^^^^^ help: change this to: `&str`
 +
 +error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
 +  --> $DIR/ptr_arg.rs:146:21
 +   |
 +LL |     fn foo_vec(vec: &Vec<u8>) {
 +   |                     ^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL ~     fn foo_vec(vec: &[u8]) {
 +LL ~         let _ = vec.to_owned().pop();
 +LL ~         let _ = vec.to_owned().clone();
 +   |
 +
 +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
 +  --> $DIR/ptr_arg.rs:151:23
 +   |
 +LL |     fn foo_path(path: &PathBuf) {
 +   |                       ^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL ~     fn foo_path(path: &Path) {
 +LL ~         let _ = path.to_path_buf().pop();
 +LL ~         let _ = path.to_path_buf().clone();
 +   |
 +
 +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
 +  --> $DIR/ptr_arg.rs:156:21
 +   |
 +LL |     fn foo_str(str: &PathBuf) {
 +   |                     ^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL ~     fn foo_str(str: &Path) {
 +LL ~         let _ = str.to_path_buf().pop();
 +LL ~         let _ = str.to_path_buf().clone();
 +   |
 +
 +error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
 +  --> $DIR/ptr_arg.rs:162:29
 +   |
 +LL | fn mut_vec_slice_methods(v: &mut Vec<u32>) {
 +   |                             ^^^^^^^^^^^^^ help: change this to: `&mut [u32]`
 +
++error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
++  --> $DIR/ptr_arg.rs:224:17
++   |
++LL | fn dyn_trait(a: &mut Vec<u32>, b: &mut String, c: &mut PathBuf) {
++   |                 ^^^^^^^^^^^^^ help: change this to: `&mut [u32]`
++
++error: writing `&mut String` instead of `&mut str` involves a new object where a slice will do
++  --> $DIR/ptr_arg.rs:224:35
++   |
++LL | fn dyn_trait(a: &mut Vec<u32>, b: &mut String, c: &mut PathBuf) {
++   |                                   ^^^^^^^^^^^ help: change this to: `&mut str`
++
++error: writing `&mut PathBuf` instead of `&mut Path` involves a new object where a slice will do
++  --> $DIR/ptr_arg.rs:224:51
++   |
++LL | fn dyn_trait(a: &mut Vec<u32>, b: &mut String, c: &mut PathBuf) {
++   |                                                   ^^^^^^^^^^^^ help: change this to: `&mut Path`
++
++error: aborting due to 20 previous errors
 +
index 85d021b2f25e2094435df55fc9e40bcaa39caa34,0000000000000000000000000000000000000000..824f00cb99e85159987586450fc593ac93d97792
mode 100644,000000..100644
--- /dev/null
@@@ -1,64 -1,0 +1,80 @@@
- #[warn(clippy::manual_range_contains)]
- #[allow(unused)]
- #[allow(clippy::no_effect)]
- #[allow(clippy::short_circuit_statement)]
- #[allow(clippy::unnecessary_operation)]
 +// run-rustfix
 +
++#![feature(custom_inner_attributes)]
++#![warn(clippy::manual_range_contains)]
++#![allow(unused)]
++#![allow(clippy::no_effect)]
++#![allow(clippy::short_circuit_statement)]
++#![allow(clippy::unnecessary_operation)]
++
 +fn main() {
 +    let x = 9_i32;
 +
 +    // order shouldn't matter
 +    (8..12).contains(&x);
 +    (21..42).contains(&x);
 +    (1..100).contains(&x);
 +
 +    // also with inclusive ranges
 +    (9..=99).contains(&x);
 +    (1..=33).contains(&x);
 +    (1..=999).contains(&x);
 +
 +    // and the outside
 +    !(8..12).contains(&x);
 +    !(21..42).contains(&x);
 +    !(1..100).contains(&x);
 +
 +    // also with the outside of inclusive ranges
 +    !(9..=99).contains(&x);
 +    !(1..=33).contains(&x);
 +    !(1..=999).contains(&x);
 +
 +    // not a range.contains
 +    x > 8 && x < 12; // lower bound not inclusive
 +    x < 8 && x <= 12; // same direction
 +    x >= 12 && 12 >= x; // same bounds
 +    x < 8 && x > 12; // wrong direction
 +
 +    x <= 8 || x >= 12;
 +    x >= 8 || x >= 12;
 +    x < 12 || 12 < x;
 +    x >= 8 || x <= 12;
 +
 +    // Fix #6315
 +    let y = 3.;
 +    (0. ..1.).contains(&y);
 +    !(0. ..=1.).contains(&y);
 +
 +    // handle negatives #8721
 +    (-10..=10).contains(&x);
 +    x >= 10 && x <= -10;
 +    (-3. ..=3.).contains(&y);
 +    y >= 3. && y <= -3.;
 +
 +    // Fix #8745
 +    let z = 42;
 +    (0..=10).contains(&x) && (0..=10).contains(&z);
 +    !(0..10).contains(&x) || !(0..10).contains(&z);
 +    // Make sure operators in parens don't give a breaking suggestion
 +    ((x % 2 == 0) || (x < 0)) || (x >= 10);
 +}
 +
 +// Fix #6373
 +pub const fn in_range(a: i32) -> bool {
 +    3 <= a && a <= 20
 +}
++
++fn msrv_1_34() {
++    #![clippy::msrv = "1.34"]
++
++    let x = 5;
++    x >= 8 && x < 34;
++}
++
++fn msrv_1_35() {
++    #![clippy::msrv = "1.35"]
++
++    let x = 5;
++    (8..35).contains(&x);
++}
index 9a7a75dc13254ed2883943eabbd0c350fd991b6a,0000000000000000000000000000000000000000..df925eeadfe5e0762f712412b29cf938de30ca08
mode 100644,000000..100644
--- /dev/null
@@@ -1,64 -1,0 +1,80 @@@
- #[warn(clippy::manual_range_contains)]
- #[allow(unused)]
- #[allow(clippy::no_effect)]
- #[allow(clippy::short_circuit_statement)]
- #[allow(clippy::unnecessary_operation)]
 +// run-rustfix
 +
++#![feature(custom_inner_attributes)]
++#![warn(clippy::manual_range_contains)]
++#![allow(unused)]
++#![allow(clippy::no_effect)]
++#![allow(clippy::short_circuit_statement)]
++#![allow(clippy::unnecessary_operation)]
++
 +fn main() {
 +    let x = 9_i32;
 +
 +    // order shouldn't matter
 +    x >= 8 && x < 12;
 +    x < 42 && x >= 21;
 +    100 > x && 1 <= x;
 +
 +    // also with inclusive ranges
 +    x >= 9 && x <= 99;
 +    x <= 33 && x >= 1;
 +    999 >= x && 1 <= x;
 +
 +    // and the outside
 +    x < 8 || x >= 12;
 +    x >= 42 || x < 21;
 +    100 <= x || 1 > x;
 +
 +    // also with the outside of inclusive ranges
 +    x < 9 || x > 99;
 +    x > 33 || x < 1;
 +    999 < x || 1 > x;
 +
 +    // not a range.contains
 +    x > 8 && x < 12; // lower bound not inclusive
 +    x < 8 && x <= 12; // same direction
 +    x >= 12 && 12 >= x; // same bounds
 +    x < 8 && x > 12; // wrong direction
 +
 +    x <= 8 || x >= 12;
 +    x >= 8 || x >= 12;
 +    x < 12 || 12 < x;
 +    x >= 8 || x <= 12;
 +
 +    // Fix #6315
 +    let y = 3.;
 +    y >= 0. && y < 1.;
 +    y < 0. || y > 1.;
 +
 +    // handle negatives #8721
 +    x >= -10 && x <= 10;
 +    x >= 10 && x <= -10;
 +    y >= -3. && y <= 3.;
 +    y >= 3. && y <= -3.;
 +
 +    // Fix #8745
 +    let z = 42;
 +    (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
 +    (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
 +    // Make sure operators in parens don't give a breaking suggestion
 +    ((x % 2 == 0) || (x < 0)) || (x >= 10);
 +}
 +
 +// Fix #6373
 +pub const fn in_range(a: i32) -> bool {
 +    3 <= a && a <= 20
 +}
++
++fn msrv_1_34() {
++    #![clippy::msrv = "1.34"]
++
++    let x = 5;
++    x >= 8 && x < 34;
++}
++
++fn msrv_1_35() {
++    #![clippy::msrv = "1.35"]
++
++    let x = 5;
++    x >= 8 && x < 35;
++}
index 936859db5a1260ba217bcb91b31ef91e90fa9a2c,0000000000000000000000000000000000000000..9689e665b05c582ab061dc0734af45b461e0d059
mode 100644,000000..100644
--- /dev/null
@@@ -1,124 -1,0 +1,130 @@@
-   --> $DIR/range_contains.rs:12:5
 +error: manual `Range::contains` implementation
-   --> $DIR/range_contains.rs:13:5
++  --> $DIR/range_contains.rs:14:5
 +   |
 +LL |     x >= 8 && x < 12;
 +   |     ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)`
 +   |
 +   = note: `-D clippy::manual-range-contains` implied by `-D warnings`
 +
 +error: manual `Range::contains` implementation
-   --> $DIR/range_contains.rs:14:5
++  --> $DIR/range_contains.rs:15:5
 +   |
 +LL |     x < 42 && x >= 21;
 +   |     ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)`
 +
 +error: manual `Range::contains` implementation
-   --> $DIR/range_contains.rs:17:5
++  --> $DIR/range_contains.rs:16:5
 +   |
 +LL |     100 > x && 1 <= x;
 +   |     ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)`
 +
 +error: manual `RangeInclusive::contains` implementation
-   --> $DIR/range_contains.rs:18:5
++  --> $DIR/range_contains.rs:19:5
 +   |
 +LL |     x >= 9 && x <= 99;
 +   |     ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)`
 +
 +error: manual `RangeInclusive::contains` implementation
-   --> $DIR/range_contains.rs:19:5
++  --> $DIR/range_contains.rs:20:5
 +   |
 +LL |     x <= 33 && x >= 1;
 +   |     ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)`
 +
 +error: manual `RangeInclusive::contains` implementation
-   --> $DIR/range_contains.rs:22:5
++  --> $DIR/range_contains.rs:21:5
 +   |
 +LL |     999 >= x && 1 <= x;
 +   |     ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)`
 +
 +error: manual `!Range::contains` implementation
-   --> $DIR/range_contains.rs:23:5
++  --> $DIR/range_contains.rs:24:5
 +   |
 +LL |     x < 8 || x >= 12;
 +   |     ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)`
 +
 +error: manual `!Range::contains` implementation
-   --> $DIR/range_contains.rs:24:5
++  --> $DIR/range_contains.rs:25:5
 +   |
 +LL |     x >= 42 || x < 21;
 +   |     ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)`
 +
 +error: manual `!Range::contains` implementation
-   --> $DIR/range_contains.rs:27:5
++  --> $DIR/range_contains.rs:26:5
 +   |
 +LL |     100 <= x || 1 > x;
 +   |     ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)`
 +
 +error: manual `!RangeInclusive::contains` implementation
-   --> $DIR/range_contains.rs:28:5
++  --> $DIR/range_contains.rs:29:5
 +   |
 +LL |     x < 9 || x > 99;
 +   |     ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)`
 +
 +error: manual `!RangeInclusive::contains` implementation
-   --> $DIR/range_contains.rs:29:5
++  --> $DIR/range_contains.rs:30:5
 +   |
 +LL |     x > 33 || x < 1;
 +   |     ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)`
 +
 +error: manual `!RangeInclusive::contains` implementation
-   --> $DIR/range_contains.rs:44:5
++  --> $DIR/range_contains.rs:31:5
 +   |
 +LL |     999 < x || 1 > x;
 +   |     ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)`
 +
 +error: manual `Range::contains` implementation
-   --> $DIR/range_contains.rs:45:5
++  --> $DIR/range_contains.rs:46:5
 +   |
 +LL |     y >= 0. && y < 1.;
 +   |     ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)`
 +
 +error: manual `!RangeInclusive::contains` implementation
-   --> $DIR/range_contains.rs:48:5
++  --> $DIR/range_contains.rs:47:5
 +   |
 +LL |     y < 0. || y > 1.;
 +   |     ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)`
 +
 +error: manual `RangeInclusive::contains` implementation
-   --> $DIR/range_contains.rs:50:5
++  --> $DIR/range_contains.rs:50:5
 +   |
 +LL |     x >= -10 && x <= 10;
 +   |     ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)`
 +
 +error: manual `RangeInclusive::contains` implementation
-   --> $DIR/range_contains.rs:55:30
++  --> $DIR/range_contains.rs:52:5
 +   |
 +LL |     y >= -3. && y <= 3.;
 +   |     ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)`
 +
 +error: manual `RangeInclusive::contains` implementation
-   --> $DIR/range_contains.rs:55:5
++  --> $DIR/range_contains.rs:57:30
 +   |
 +LL |     (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
 +   |                              ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)`
 +
 +error: manual `RangeInclusive::contains` implementation
-   --> $DIR/range_contains.rs:56:29
++  --> $DIR/range_contains.rs:57:5
 +   |
 +LL |     (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
 +   |     ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)`
 +
 +error: manual `!Range::contains` implementation
-   --> $DIR/range_contains.rs:56:5
++  --> $DIR/range_contains.rs:58:29
 +   |
 +LL |     (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
 +   |                             ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)`
 +
 +error: manual `!Range::contains` implementation
- error: aborting due to 20 previous errors
++  --> $DIR/range_contains.rs:58:5
 +   |
 +LL |     (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
 +   |     ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)`
 +
++error: manual `Range::contains` implementation
++  --> $DIR/range_contains.rs:79:5
++   |
++LL |     x >= 8 && x < 35;
++   |     ^^^^^^^^^^^^^^^^ help: use: `(8..35).contains(&x)`
++
++error: aborting due to 21 previous errors
 +
index 5b4b8eeedd4696162ea63ca250f8cd1e5b61eb0d,0000000000000000000000000000000000000000..34ab552cb1d8d6aee22db40c07818a11eb42f8db
mode 100644,000000..100644
--- /dev/null
@@@ -1,71 -1,0 +1,87 @@@
 +// run-rustfix
++
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::redundant_field_names)]
 +#![allow(clippy::no_effect, dead_code, unused_variables)]
 +
 +#[macro_use]
 +extern crate derive_new;
 +
 +use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
 +
 +mod foo {
 +    pub const BAR: u8 = 0;
 +}
 +
 +struct Person {
 +    gender: u8,
 +    age: u8,
 +    name: u8,
 +    buzz: u64,
 +    foo: u8,
 +}
 +
 +#[derive(new)]
 +pub struct S {
 +    v: String,
 +}
 +
 +fn main() {
 +    let gender: u8 = 42;
 +    let age = 0;
 +    let fizz: u64 = 0;
 +    let name: u8 = 0;
 +
 +    let me = Person {
 +        gender,
 +        age,
 +
 +        name,          //should be ok
 +        buzz: fizz,    //should be ok
 +        foo: foo::BAR, //should be ok
 +    };
 +
 +    // Range expressions
 +    let (start, end) = (0, 0);
 +
 +    let _ = start..;
 +    let _ = ..end;
 +    let _ = start..end;
 +
 +    let _ = ..=end;
 +    let _ = start..=end;
 +
 +    // Issue #2799
 +    let _: Vec<_> = (start..end).collect();
 +
 +    // hand-written Range family structs are linted
 +    let _ = RangeFrom { start };
 +    let _ = RangeTo { end };
 +    let _ = Range { start, end };
 +    let _ = RangeInclusive::new(start, end);
 +    let _ = RangeToInclusive { end };
 +}
 +
 +fn issue_3476() {
 +    fn foo<T>() {}
 +
 +    struct S {
 +        foo: fn(),
 +    }
 +
 +    S { foo: foo::<i32> };
 +}
++
++fn msrv_1_16() {
++    #![clippy::msrv = "1.16"]
++
++    let start = 0;
++    let _ = RangeFrom { start: start };
++}
++
++fn msrv_1_17() {
++    #![clippy::msrv = "1.17"]
++
++    let start = 0;
++    let _ = RangeFrom { start };
++}
index 3f97b80c56828c3842c451bb9b616bb3103e1eec,0000000000000000000000000000000000000000..a051b1f96f0fda8dad22fe3fba5f867d0249263a
mode 100644,000000..100644
--- /dev/null
@@@ -1,71 -1,0 +1,87 @@@
 +// run-rustfix
++
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::redundant_field_names)]
 +#![allow(clippy::no_effect, dead_code, unused_variables)]
 +
 +#[macro_use]
 +extern crate derive_new;
 +
 +use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
 +
 +mod foo {
 +    pub const BAR: u8 = 0;
 +}
 +
 +struct Person {
 +    gender: u8,
 +    age: u8,
 +    name: u8,
 +    buzz: u64,
 +    foo: u8,
 +}
 +
 +#[derive(new)]
 +pub struct S {
 +    v: String,
 +}
 +
 +fn main() {
 +    let gender: u8 = 42;
 +    let age = 0;
 +    let fizz: u64 = 0;
 +    let name: u8 = 0;
 +
 +    let me = Person {
 +        gender: gender,
 +        age: age,
 +
 +        name,          //should be ok
 +        buzz: fizz,    //should be ok
 +        foo: foo::BAR, //should be ok
 +    };
 +
 +    // Range expressions
 +    let (start, end) = (0, 0);
 +
 +    let _ = start..;
 +    let _ = ..end;
 +    let _ = start..end;
 +
 +    let _ = ..=end;
 +    let _ = start..=end;
 +
 +    // Issue #2799
 +    let _: Vec<_> = (start..end).collect();
 +
 +    // hand-written Range family structs are linted
 +    let _ = RangeFrom { start: start };
 +    let _ = RangeTo { end: end };
 +    let _ = Range { start: start, end: end };
 +    let _ = RangeInclusive::new(start, end);
 +    let _ = RangeToInclusive { end: end };
 +}
 +
 +fn issue_3476() {
 +    fn foo<T>() {}
 +
 +    struct S {
 +        foo: fn(),
 +    }
 +
 +    S { foo: foo::<i32> };
 +}
++
++fn msrv_1_16() {
++    #![clippy::msrv = "1.16"]
++
++    let start = 0;
++    let _ = RangeFrom { start: start };
++}
++
++fn msrv_1_17() {
++    #![clippy::msrv = "1.17"]
++
++    let start = 0;
++    let _ = RangeFrom { start: start };
++}
index 7976292df224140b39a50d44c831c76072467708,0000000000000000000000000000000000000000..8b82e062b93a63325c94f5e4c79b5f9592f63f5c
mode 100644,000000..100644
--- /dev/null
@@@ -1,46 -1,0 +1,52 @@@
-   --> $DIR/redundant_field_names.rs:34:9
 +error: redundant field names in struct initialization
-   --> $DIR/redundant_field_names.rs:35:9
++  --> $DIR/redundant_field_names.rs:36:9
 +   |
 +LL |         gender: gender,
 +   |         ^^^^^^^^^^^^^^ help: replace it with: `gender`
 +   |
 +   = note: `-D clippy::redundant-field-names` implied by `-D warnings`
 +
 +error: redundant field names in struct initialization
-   --> $DIR/redundant_field_names.rs:56:25
++  --> $DIR/redundant_field_names.rs:37:9
 +   |
 +LL |         age: age,
 +   |         ^^^^^^^^ help: replace it with: `age`
 +
 +error: redundant field names in struct initialization
-   --> $DIR/redundant_field_names.rs:57:23
++  --> $DIR/redundant_field_names.rs:58:25
 +   |
 +LL |     let _ = RangeFrom { start: start };
 +   |                         ^^^^^^^^^^^^ help: replace it with: `start`
 +
 +error: redundant field names in struct initialization
-   --> $DIR/redundant_field_names.rs:58:21
++  --> $DIR/redundant_field_names.rs:59:23
 +   |
 +LL |     let _ = RangeTo { end: end };
 +   |                       ^^^^^^^^ help: replace it with: `end`
 +
 +error: redundant field names in struct initialization
-   --> $DIR/redundant_field_names.rs:58:35
++  --> $DIR/redundant_field_names.rs:60:21
 +   |
 +LL |     let _ = Range { start: start, end: end };
 +   |                     ^^^^^^^^^^^^ help: replace it with: `start`
 +
 +error: redundant field names in struct initialization
-   --> $DIR/redundant_field_names.rs:60:32
++  --> $DIR/redundant_field_names.rs:60:35
 +   |
 +LL |     let _ = Range { start: start, end: end };
 +   |                                   ^^^^^^^^ help: replace it with: `end`
 +
 +error: redundant field names in struct initialization
- error: aborting due to 7 previous errors
++  --> $DIR/redundant_field_names.rs:62:32
 +   |
 +LL |     let _ = RangeToInclusive { end: end };
 +   |                                ^^^^^^^^ help: replace it with: `end`
 +
++error: redundant field names in struct initialization
++  --> $DIR/redundant_field_names.rs:86:25
++   |
++LL |     let _ = RangeFrom { start: start };
++   |                         ^^^^^^^^^^^^ help: replace it with: `start`
++
++error: aborting due to 8 previous errors
 +
index acc8f1e25b6ed3a937186d5146275271ad46c0d8,0000000000000000000000000000000000000000..42110dbe81e84f8d35f678b7613be06f4b2f9a53
mode 100644,000000..100644
--- /dev/null
@@@ -1,56 -1,0 +1,69 @@@
 +// run-rustfix
 +
++#![feature(custom_inner_attributes)]
 +#![allow(unused)]
 +
 +#[derive(Debug)]
 +struct Foo;
 +
 +const VAR_ONE: &str = "Test constant #1"; // ERROR Consider removing 'static.
 +
 +const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning.
 +
 +const VAR_THREE: &[&str] = &["one", "two"]; // ERROR Consider removing 'static
 +
 +const VAR_FOUR: (&str, (&str, &str), &str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
 +
 +const VAR_SIX: &u8 = &5;
 +
 +const VAR_HEIGHT: &Foo = &Foo {};
 +
 +const VAR_SLICE: &[u8] = b"Test constant #1"; // ERROR Consider removing 'static.
 +
 +const VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static.
 +
 +const VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static.
 +
 +static STATIC_VAR_ONE: &str = "Test static #1"; // ERROR Consider removing 'static.
 +
 +static STATIC_VAR_TWO: &str = "Test static #2"; // This line should not raise a warning.
 +
 +static STATIC_VAR_THREE: &[&str] = &["one", "two"]; // ERROR Consider removing 'static
 +
 +static STATIC_VAR_SIX: &u8 = &5;
 +
 +static STATIC_VAR_HEIGHT: &Foo = &Foo {};
 +
 +static STATIC_VAR_SLICE: &[u8] = b"Test static #3"; // ERROR Consider removing 'static.
 +
 +static STATIC_VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static.
 +
 +static STATIC_VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static.
 +
 +fn main() {
 +    let false_positive: &'static str = "test";
 +}
 +
 +trait Bar {
 +    const TRAIT_VAR: &'static str;
 +}
 +
 +impl Foo {
 +    const IMPL_VAR: &'static str = "var";
 +}
 +
 +impl Bar for Foo {
 +    const TRAIT_VAR: &'static str = "foo";
 +}
++
++fn msrv_1_16() {
++    #![clippy::msrv = "1.16"]
++
++    static V: &'static u8 = &16;
++}
++
++fn msrv_1_17() {
++    #![clippy::msrv = "1.17"]
++
++    static V: &u8 = &17;
++}
index f2f0f78659c936516fab442fd624e1ba7f5164a0,0000000000000000000000000000000000000000..bc5200bc8625b0a200e05035f2582f3dd2eab21d
mode 100644,000000..100644
--- /dev/null
@@@ -1,56 -1,0 +1,69 @@@
 +// run-rustfix
 +
++#![feature(custom_inner_attributes)]
 +#![allow(unused)]
 +
 +#[derive(Debug)]
 +struct Foo;
 +
 +const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
 +
 +const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning.
 +
 +const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
 +
 +const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
 +
 +const VAR_SIX: &'static u8 = &5;
 +
 +const VAR_HEIGHT: &'static Foo = &Foo {};
 +
 +const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static.
 +
 +const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
 +
 +const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
 +
 +static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static.
 +
 +static STATIC_VAR_TWO: &str = "Test static #2"; // This line should not raise a warning.
 +
 +static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
 +
 +static STATIC_VAR_SIX: &'static u8 = &5;
 +
 +static STATIC_VAR_HEIGHT: &'static Foo = &Foo {};
 +
 +static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static.
 +
 +static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
 +
 +static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
 +
 +fn main() {
 +    let false_positive: &'static str = "test";
 +}
 +
 +trait Bar {
 +    const TRAIT_VAR: &'static str;
 +}
 +
 +impl Foo {
 +    const IMPL_VAR: &'static str = "var";
 +}
 +
 +impl Bar for Foo {
 +    const TRAIT_VAR: &'static str = "foo";
 +}
++
++fn msrv_1_16() {
++    #![clippy::msrv = "1.16"]
++
++    static V: &'static u8 = &16;
++}
++
++fn msrv_1_17() {
++    #![clippy::msrv = "1.17"]
++
++    static V: &'static u8 = &17;
++}
index 649831f9c069af3d30a60cbf1d06a14f3b83ab9b,0000000000000000000000000000000000000000..735113460d28caf9c67ea003d651fe7cbf9fbafc
mode 100644,000000..100644
--- /dev/null
@@@ -1,100 -1,0 +1,106 @@@
-   --> $DIR/redundant_static_lifetimes.rs:8:17
 +error: constants have by default a `'static` lifetime
-   --> $DIR/redundant_static_lifetimes.rs:12:21
++  --> $DIR/redundant_static_lifetimes.rs:9:17
 +   |
 +LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
 +   |                -^^^^^^^---- help: consider removing `'static`: `&str`
 +   |
 +   = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings`
 +
 +error: constants have by default a `'static` lifetime
-   --> $DIR/redundant_static_lifetimes.rs:14:32
++  --> $DIR/redundant_static_lifetimes.rs:13:21
 +   |
 +LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
 +   |                    -^^^^^^^---- help: consider removing `'static`: `&str`
 +
 +error: constants have by default a `'static` lifetime
-   --> $DIR/redundant_static_lifetimes.rs:14:47
++  --> $DIR/redundant_static_lifetimes.rs:15:32
 +   |
 +LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
 +   |                               -^^^^^^^---- help: consider removing `'static`: `&str`
 +
 +error: constants have by default a `'static` lifetime
-   --> $DIR/redundant_static_lifetimes.rs:16:17
++  --> $DIR/redundant_static_lifetimes.rs:15:47
 +   |
 +LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
 +   |                                              -^^^^^^^---- help: consider removing `'static`: `&str`
 +
 +error: constants have by default a `'static` lifetime
-   --> $DIR/redundant_static_lifetimes.rs:18:20
++  --> $DIR/redundant_static_lifetimes.rs:17:17
 +   |
 +LL | const VAR_SIX: &'static u8 = &5;
 +   |                -^^^^^^^--- help: consider removing `'static`: `&u8`
 +
 +error: constants have by default a `'static` lifetime
-   --> $DIR/redundant_static_lifetimes.rs:20:19
++  --> $DIR/redundant_static_lifetimes.rs:19:20
 +   |
 +LL | const VAR_HEIGHT: &'static Foo = &Foo {};
 +   |                   -^^^^^^^---- help: consider removing `'static`: `&Foo`
 +
 +error: constants have by default a `'static` lifetime
-   --> $DIR/redundant_static_lifetimes.rs:22:19
++  --> $DIR/redundant_static_lifetimes.rs:21:19
 +   |
 +LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static.
 +   |                  -^^^^^^^----- help: consider removing `'static`: `&[u8]`
 +
 +error: constants have by default a `'static` lifetime
-   --> $DIR/redundant_static_lifetimes.rs:24:19
++  --> $DIR/redundant_static_lifetimes.rs:23:19
 +   |
 +LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
 +   |                  -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
 +
 +error: constants have by default a `'static` lifetime
-   --> $DIR/redundant_static_lifetimes.rs:26:25
++  --> $DIR/redundant_static_lifetimes.rs:25:19
 +   |
 +LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
 +   |                  -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
 +
 +error: statics have by default a `'static` lifetime
-   --> $DIR/redundant_static_lifetimes.rs:30:29
++  --> $DIR/redundant_static_lifetimes.rs:27:25
 +   |
 +LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static.
 +   |                        -^^^^^^^---- help: consider removing `'static`: `&str`
 +
 +error: statics have by default a `'static` lifetime
-   --> $DIR/redundant_static_lifetimes.rs:32:25
++  --> $DIR/redundant_static_lifetimes.rs:31:29
 +   |
 +LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
 +   |                            -^^^^^^^---- help: consider removing `'static`: `&str`
 +
 +error: statics have by default a `'static` lifetime
-   --> $DIR/redundant_static_lifetimes.rs:34:28
++  --> $DIR/redundant_static_lifetimes.rs:33:25
 +   |
 +LL | static STATIC_VAR_SIX: &'static u8 = &5;
 +   |                        -^^^^^^^--- help: consider removing `'static`: `&u8`
 +
 +error: statics have by default a `'static` lifetime
-   --> $DIR/redundant_static_lifetimes.rs:36:27
++  --> $DIR/redundant_static_lifetimes.rs:35:28
 +   |
 +LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {};
 +   |                           -^^^^^^^---- help: consider removing `'static`: `&Foo`
 +
 +error: statics have by default a `'static` lifetime
-   --> $DIR/redundant_static_lifetimes.rs:38:27
++  --> $DIR/redundant_static_lifetimes.rs:37:27
 +   |
 +LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static.
 +   |                          -^^^^^^^----- help: consider removing `'static`: `&[u8]`
 +
 +error: statics have by default a `'static` lifetime
-   --> $DIR/redundant_static_lifetimes.rs:40:27
++  --> $DIR/redundant_static_lifetimes.rs:39:27
 +   |
 +LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
 +   |                          -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
 +
 +error: statics have by default a `'static` lifetime
- error: aborting due to 16 previous errors
++  --> $DIR/redundant_static_lifetimes.rs:41:27
 +   |
 +LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
 +   |                          -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
 +
++error: statics have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes.rs:68:16
++   |
++LL |     static V: &'static u8 = &17;
++   |               -^^^^^^^--- help: consider removing `'static`: `&u8`
++
++error: aborting due to 17 previous errors
 +
index 2df45c927d7169bdc987082bcc3e281c4c4f84b2,0000000000000000000000000000000000000000..e487799e1522099e1fe2c529b2008ae9a63c54bb
mode 100644,000000..100644
--- /dev/null
@@@ -1,47 -1,0 +1,52 @@@
 +#![allow(unused)]
 +#![warn(clippy::ref_option_ref)]
 +
 +// This lint is not tagged as run-rustfix because automatically
 +// changing the type of a variable would also means changing
 +// all usages of this variable to match and This is not handled
 +// by this lint.
 +
 +static THRESHOLD: i32 = 10;
 +static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD);
 +const CONST_THRESHOLD: &i32 = &10;
 +const REF_CONST: &Option<&i32> = &Some(CONST_THRESHOLD);
 +
 +type RefOptRefU32<'a> = &'a Option<&'a u32>;
 +type RefOptRef<'a, T> = &'a Option<&'a T>;
 +
 +fn foo(data: &Option<&u32>) {}
 +
 +fn bar(data: &u32) -> &Option<&u32> {
 +    &None
 +}
 +
 +struct StructRef<'a> {
 +    data: &'a Option<&'a u32>,
 +}
 +
 +struct StructTupleRef<'a>(u32, &'a Option<&'a u32>);
 +
 +enum EnumRef<'a> {
 +    Variant1(u32),
 +    Variant2(&'a Option<&'a u32>),
 +}
 +
 +trait RefOptTrait {
 +    type A;
 +    fn foo(&self, _: Self::A);
 +}
 +
 +impl RefOptTrait for u32 {
 +    type A = &'static Option<&'static Self>;
 +
 +    fn foo(&self, _: Self::A) {}
 +}
 +
 +fn main() {
 +    let x: &Option<&u32> = &None;
 +}
++
++fn issue9682(arg: &Option<&mut String>) {
++    // Should not lint, as the inner ref is mutable making it non `Copy`
++    println!("{arg:?}");
++}
index 3ca7a401902530446a86f271af9946a2f8bebb57,0000000000000000000000000000000000000000..106274479751d7439a50bc2411b9106317447f3b
mode 100644,000000..100644
--- /dev/null
@@@ -1,169 -1,0 +1,182 @@@
 +// aux-build:proc_macro_with_span.rs
 +// run-rustfix
 +#![feature(custom_inner_attributes)]
 +#![warn(clippy::uninlined_format_args)]
 +#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)]
 +#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)]
 +
 +extern crate proc_macro_with_span;
 +use proc_macro_with_span::with_span;
 +
 +macro_rules! no_param_str {
 +    () => {
 +        "{}"
 +    };
 +}
 +
 +macro_rules! my_println {
 +   ($($args:tt),*) => {{
 +        println!($($args),*)
 +    }};
 +}
 +
 +macro_rules! my_println_args {
 +    ($($args:tt),*) => {{
 +        println!("foo: {}", format_args!($($args),*))
 +    }};
 +}
 +
 +fn tester(fn_arg: i32) {
 +    let local_i32 = 1;
 +    let local_f64 = 2.0;
 +    let local_opt: Option<i32> = Some(3);
 +    let width = 4;
 +    let prec = 5;
 +    let val = 6;
 +
 +    // make sure this file hasn't been corrupted with tabs converted to spaces
 +    // let _ = '      ';  // <- this is a single tab character
 +    let _: &[u8; 3] = b"              "; // <- <tab><space><tab>
 +
 +    println!("val='{local_i32}'");
 +    println!("val='{local_i32}'"); // 3 spaces
 +    println!("val='{local_i32}'"); // tab
 +    println!("val='{local_i32}'"); // space+tab
 +    println!("val='{local_i32}'"); // tab+space
 +    println!(
 +        "val='{
 +    }'",
 +        local_i32
 +    );
 +    println!("{local_i32}");
 +    println!("{fn_arg}");
 +    println!("{local_i32:?}");
 +    println!("{local_i32:#?}");
 +    println!("{local_i32:4}");
 +    println!("{local_i32:04}");
 +    println!("{local_i32:<3}");
 +    println!("{local_i32:#010x}");
 +    println!("{local_f64:.1}");
 +    println!("Hello {} is {local_f64:.local_i32$}", "x");
 +    println!("Hello {local_i32} is {local_f64:.*}", 5);
 +    println!("Hello {local_i32} is {local_f64:.*}", 5);
 +    println!("{local_i32} {local_f64}");
 +    println!("{local_i32}, {}", local_opt.unwrap());
 +    println!("{val}");
 +    println!("{val}");
 +    println!("{} {1}", local_i32, 42);
 +    println!("val='{local_i32}'");
 +    println!("val='{local_i32}'");
 +    println!("val='{local_i32}'");
 +    println!("val='{fn_arg}'");
 +    println!("{local_i32}");
 +    println!("{local_i32:?}");
 +    println!("{local_i32:#?}");
 +    println!("{local_i32:04}");
 +    println!("{local_i32:<3}");
 +    println!("{local_i32:#010x}");
 +    println!("{local_f64:.1}");
 +    println!("{local_i32} {local_i32}");
 +    println!("{local_f64} {local_i32} {local_i32} {local_f64}");
 +    println!("{local_i32} {local_f64}");
 +    println!("{local_f64} {local_i32}");
 +    println!("{local_f64} {local_i32} {local_f64} {local_i32}");
 +    println!("{1} {0}", "str", local_i32);
 +    println!("{local_i32}");
 +    println!("{local_i32:width$}");
 +    println!("{local_i32:width$}");
 +    println!("{local_i32:.prec$}");
 +    println!("{local_i32:.prec$}");
 +    println!("{val:val$}");
 +    println!("{val:val$}");
 +    println!("{val:val$.val$}");
 +    println!("{val:val$.val$}");
 +    println!("{val:val$.val$}");
 +    println!("{val:val$.val$}");
 +    println!("{val:val$.val$}");
 +    println!("{val:val$.val$}");
 +    println!("{val:val$.val$}");
 +    println!("{val:val$.val$}");
 +    println!("{width:width$}");
 +    println!("{local_i32:width$}");
 +    println!("{width:width$}");
 +    println!("{local_i32:width$}");
 +    println!("{prec:.prec$}");
 +    println!("{local_i32:.prec$}");
 +    println!("{prec:.prec$}");
 +    println!("{local_i32:.prec$}");
 +    println!("{width:width$.prec$}");
 +    println!("{width:width$.prec$}");
 +    println!("{local_f64:width$.prec$}");
 +    println!("{local_f64:width$.prec$} {local_f64} {width} {prec}");
 +    println!(
 +        "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}",
 +        local_i32, width, prec,
 +    );
 +    println!(
 +        "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}",
 +        local_i32,
 +        width,
 +        prec,
 +        1 + 2
 +    );
 +    println!("Width = {local_i32}, value with width = {local_f64:local_i32$}");
 +    println!("{local_i32:width$.prec$}");
 +    println!("{width:width$.prec$}");
 +    println!("{}", format!("{local_i32}"));
 +    my_println!("{}", local_i32);
 +    my_println_args!("{}", local_i32);
 +
 +    // these should NOT be modified by the lint
 +    println!(concat!("nope ", "{}"), local_i32);
 +    println!("val='{local_i32}'");
 +    println!("val='{local_i32 }'");
 +    println!("val='{local_i32 }'"); // with tab
 +    println!("val='{local_i32\n}'");
 +    println!("{}", usize::MAX);
 +    println!("{}", local_opt.unwrap());
 +    println!(
 +        "val='{local_i32
 +    }'"
 +    );
 +    println!(no_param_str!(), local_i32);
 +
 +    println!(
 +        "{}",
 +        // comment with a comma , in it
 +        val,
 +    );
 +    println!("{val}");
 +
 +    println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64);
 +    println!("{}", with_span!(span val));
++
++    if local_i32 > 0 {
++        panic!("p1 {local_i32}");
++    }
++    if local_i32 > 0 {
++        panic!("p2 {local_i32}");
++    }
++    if local_i32 > 0 {
++        panic!("p3 {local_i32}");
++    }
++    if local_i32 > 0 {
++        panic!("p4 {local_i32}");
++    }
 +}
 +
 +fn main() {
 +    tester(42);
 +}
 +
 +fn _under_msrv() {
 +    #![clippy::msrv = "1.57"]
 +    let local_i32 = 1;
 +    println!("don't expand='{}'", local_i32);
 +}
 +
 +fn _meets_msrv() {
 +    #![clippy::msrv = "1.58"]
 +    let local_i32 = 1;
 +    println!("expand='{local_i32}'");
 +}
index 924191f4324cfc7ef7aaf405ab84885aab98ff1e,0000000000000000000000000000000000000000..8e495ebd083a55e6eacae4ba91dbc7859c5d2ec6
mode 100644,000000..100644
--- /dev/null
@@@ -1,169 -1,0 +1,182 @@@
 +// aux-build:proc_macro_with_span.rs
 +// run-rustfix
 +#![feature(custom_inner_attributes)]
 +#![warn(clippy::uninlined_format_args)]
 +#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)]
 +#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)]
 +
 +extern crate proc_macro_with_span;
 +use proc_macro_with_span::with_span;
 +
 +macro_rules! no_param_str {
 +    () => {
 +        "{}"
 +    };
 +}
 +
 +macro_rules! my_println {
 +   ($($args:tt),*) => {{
 +        println!($($args),*)
 +    }};
 +}
 +
 +macro_rules! my_println_args {
 +    ($($args:tt),*) => {{
 +        println!("foo: {}", format_args!($($args),*))
 +    }};
 +}
 +
 +fn tester(fn_arg: i32) {
 +    let local_i32 = 1;
 +    let local_f64 = 2.0;
 +    let local_opt: Option<i32> = Some(3);
 +    let width = 4;
 +    let prec = 5;
 +    let val = 6;
 +
 +    // make sure this file hasn't been corrupted with tabs converted to spaces
 +    // let _ = '      ';  // <- this is a single tab character
 +    let _: &[u8; 3] = b"              "; // <- <tab><space><tab>
 +
 +    println!("val='{}'", local_i32);
 +    println!("val='{   }'", local_i32); // 3 spaces
 +    println!("val='{  }'", local_i32); // tab
 +    println!("val='{  }'", local_i32); // space+tab
 +    println!("val='{   }'", local_i32); // tab+space
 +    println!(
 +        "val='{
 +    }'",
 +        local_i32
 +    );
 +    println!("{}", local_i32);
 +    println!("{}", fn_arg);
 +    println!("{:?}", local_i32);
 +    println!("{:#?}", local_i32);
 +    println!("{:4}", local_i32);
 +    println!("{:04}", local_i32);
 +    println!("{:<3}", local_i32);
 +    println!("{:#010x}", local_i32);
 +    println!("{:.1}", local_f64);
 +    println!("Hello {} is {:.*}", "x", local_i32, local_f64);
 +    println!("Hello {} is {:.*}", local_i32, 5, local_f64);
 +    println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
 +    println!("{} {}", local_i32, local_f64);
 +    println!("{}, {}", local_i32, local_opt.unwrap());
 +    println!("{}", val);
 +    println!("{}", v = val);
 +    println!("{} {1}", local_i32, 42);
 +    println!("val='{\t }'", local_i32);
 +    println!("val='{\n }'", local_i32);
 +    println!("val='{local_i32}'", local_i32 = local_i32);
 +    println!("val='{local_i32}'", local_i32 = fn_arg);
 +    println!("{0}", local_i32);
 +    println!("{0:?}", local_i32);
 +    println!("{0:#?}", local_i32);
 +    println!("{0:04}", local_i32);
 +    println!("{0:<3}", local_i32);
 +    println!("{0:#010x}", local_i32);
 +    println!("{0:.1}", local_f64);
 +    println!("{0} {0}", local_i32);
 +    println!("{1} {} {0} {}", local_i32, local_f64);
 +    println!("{0} {1}", local_i32, local_f64);
 +    println!("{1} {0}", local_i32, local_f64);
 +    println!("{1} {0} {1} {0}", local_i32, local_f64);
 +    println!("{1} {0}", "str", local_i32);
 +    println!("{v}", v = local_i32);
 +    println!("{local_i32:0$}", width);
 +    println!("{local_i32:w$}", w = width);
 +    println!("{local_i32:.0$}", prec);
 +    println!("{local_i32:.p$}", p = prec);
 +    println!("{:0$}", v = val);
 +    println!("{0:0$}", v = val);
 +    println!("{:0$.0$}", v = val);
 +    println!("{0:0$.0$}", v = val);
 +    println!("{0:0$.v$}", v = val);
 +    println!("{0:v$.0$}", v = val);
 +    println!("{v:0$.0$}", v = val);
 +    println!("{v:v$.0$}", v = val);
 +    println!("{v:0$.v$}", v = val);
 +    println!("{v:v$.v$}", v = val);
 +    println!("{:0$}", width);
 +    println!("{:1$}", local_i32, width);
 +    println!("{:w$}", w = width);
 +    println!("{:w$}", local_i32, w = width);
 +    println!("{:.0$}", prec);
 +    println!("{:.1$}", local_i32, prec);
 +    println!("{:.p$}", p = prec);
 +    println!("{:.p$}", local_i32, p = prec);
 +    println!("{:0$.1$}", width, prec);
 +    println!("{:0$.w$}", width, w = prec);
 +    println!("{:1$.2$}", local_f64, width, prec);
 +    println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec);
 +    println!(
 +        "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}",
 +        local_i32, width, prec,
 +    );
 +    println!(
 +        "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}",
 +        local_i32,
 +        width,
 +        prec,
 +        1 + 2
 +    );
 +    println!("Width = {}, value with width = {:0$}", local_i32, local_f64);
 +    println!("{:w$.p$}", local_i32, w = width, p = prec);
 +    println!("{:w$.p$}", w = width, p = prec);
 +    println!("{}", format!("{}", local_i32));
 +    my_println!("{}", local_i32);
 +    my_println_args!("{}", local_i32);
 +
 +    // these should NOT be modified by the lint
 +    println!(concat!("nope ", "{}"), local_i32);
 +    println!("val='{local_i32}'");
 +    println!("val='{local_i32 }'");
 +    println!("val='{local_i32 }'"); // with tab
 +    println!("val='{local_i32\n}'");
 +    println!("{}", usize::MAX);
 +    println!("{}", local_opt.unwrap());
 +    println!(
 +        "val='{local_i32
 +    }'"
 +    );
 +    println!(no_param_str!(), local_i32);
 +
 +    println!(
 +        "{}",
 +        // comment with a comma , in it
 +        val,
 +    );
 +    println!("{}", /* comment with a comma , in it */ val);
 +
 +    println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64);
 +    println!("{}", with_span!(span val));
++
++    if local_i32 > 0 {
++        panic!("p1 {}", local_i32);
++    }
++    if local_i32 > 0 {
++        panic!("p2 {0}", local_i32);
++    }
++    if local_i32 > 0 {
++        panic!("p3 {local_i32}", local_i32 = local_i32);
++    }
++    if local_i32 > 0 {
++        panic!("p4 {local_i32}");
++    }
 +}
 +
 +fn main() {
 +    tester(42);
 +}
 +
 +fn _under_msrv() {
 +    #![clippy::msrv = "1.57"]
 +    let local_i32 = 1;
 +    println!("don't expand='{}'", local_i32);
 +}
 +
 +fn _meets_msrv() {
 +    #![clippy::msrv = "1.58"]
 +    let local_i32 = 1;
 +    println!("expand='{}'", local_i32);
 +}
index d1a7749263429d5a7a5c9978e8087a05f5732224,0000000000000000000000000000000000000000..2ce3b7fa960c6159e3c3a237d43220aa195f1516
mode 100644,000000..100644
--- /dev/null
@@@ -1,843 -1,0 +1,879 @@@
-   --> $DIR/uninlined_format_args.rs:168:5
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:41:5
 +   |
 +LL |     println!("val='{}'", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::uninlined-format-args` implied by `-D warnings`
 +help: change this to
 +   |
 +LL -     println!("val='{}'", local_i32);
 +LL +     println!("val='{local_i32}'");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:42:5
 +   |
 +LL |     println!("val='{   }'", local_i32); // 3 spaces
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("val='{   }'", local_i32); // 3 spaces
 +LL +     println!("val='{local_i32}'"); // 3 spaces
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:43:5
 +   |
 +LL |     println!("val='{    }'", local_i32); // tab
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("val='{    }'", local_i32); // tab
 +LL +     println!("val='{local_i32}'"); // tab
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:44:5
 +   |
 +LL |     println!("val='{     }'", local_i32); // space+tab
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("val='{     }'", local_i32); // space+tab
 +LL +     println!("val='{local_i32}'"); // space+tab
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:45:5
 +   |
 +LL |     println!("val='{     }'", local_i32); // tab+space
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("val='{     }'", local_i32); // tab+space
 +LL +     println!("val='{local_i32}'"); // tab+space
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:51:5
 +   |
 +LL |     println!("{}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{}", local_i32);
 +LL +     println!("{local_i32}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:52:5
 +   |
 +LL |     println!("{}", fn_arg);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{}", fn_arg);
 +LL +     println!("{fn_arg}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:53:5
 +   |
 +LL |     println!("{:?}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:?}", local_i32);
 +LL +     println!("{local_i32:?}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:54:5
 +   |
 +LL |     println!("{:#?}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:#?}", local_i32);
 +LL +     println!("{local_i32:#?}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:55:5
 +   |
 +LL |     println!("{:4}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:4}", local_i32);
 +LL +     println!("{local_i32:4}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:56:5
 +   |
 +LL |     println!("{:04}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:04}", local_i32);
 +LL +     println!("{local_i32:04}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:57:5
 +   |
 +LL |     println!("{:<3}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:<3}", local_i32);
 +LL +     println!("{local_i32:<3}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:58:5
 +   |
 +LL |     println!("{:#010x}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:#010x}", local_i32);
 +LL +     println!("{local_i32:#010x}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:59:5
 +   |
 +LL |     println!("{:.1}", local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:.1}", local_f64);
 +LL +     println!("{local_f64:.1}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:60:5
 +   |
 +LL |     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
 +LL +     println!("Hello {} is {local_f64:.local_i32$}", "x");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:61:5
 +   |
 +LL |     println!("Hello {} is {:.*}", local_i32, 5, local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("Hello {} is {:.*}", local_i32, 5, local_f64);
 +LL +     println!("Hello {local_i32} is {local_f64:.*}", 5);
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:62:5
 +   |
 +LL |     println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
 +LL +     println!("Hello {local_i32} is {local_f64:.*}", 5);
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:63:5
 +   |
 +LL |     println!("{} {}", local_i32, local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{} {}", local_i32, local_f64);
 +LL +     println!("{local_i32} {local_f64}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:64:5
 +   |
 +LL |     println!("{}, {}", local_i32, local_opt.unwrap());
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{}, {}", local_i32, local_opt.unwrap());
 +LL +     println!("{local_i32}, {}", local_opt.unwrap());
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:65:5
 +   |
 +LL |     println!("{}", val);
 +   |     ^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{}", val);
 +LL +     println!("{val}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:66:5
 +   |
 +LL |     println!("{}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{}", v = val);
 +LL +     println!("{val}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:68:5
 +   |
 +LL |     println!("val='{/t }'", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("val='{/t }'", local_i32);
 +LL +     println!("val='{local_i32}'");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:69:5
 +   |
 +LL |     println!("val='{/n }'", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("val='{/n }'", local_i32);
 +LL +     println!("val='{local_i32}'");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:70:5
 +   |
 +LL |     println!("val='{local_i32}'", local_i32 = local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("val='{local_i32}'", local_i32 = local_i32);
 +LL +     println!("val='{local_i32}'");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:71:5
 +   |
 +LL |     println!("val='{local_i32}'", local_i32 = fn_arg);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("val='{local_i32}'", local_i32 = fn_arg);
 +LL +     println!("val='{fn_arg}'");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:72:5
 +   |
 +LL |     println!("{0}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0}", local_i32);
 +LL +     println!("{local_i32}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:73:5
 +   |
 +LL |     println!("{0:?}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:?}", local_i32);
 +LL +     println!("{local_i32:?}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:74:5
 +   |
 +LL |     println!("{0:#?}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:#?}", local_i32);
 +LL +     println!("{local_i32:#?}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:75:5
 +   |
 +LL |     println!("{0:04}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:04}", local_i32);
 +LL +     println!("{local_i32:04}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:76:5
 +   |
 +LL |     println!("{0:<3}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:<3}", local_i32);
 +LL +     println!("{local_i32:<3}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:77:5
 +   |
 +LL |     println!("{0:#010x}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:#010x}", local_i32);
 +LL +     println!("{local_i32:#010x}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:78:5
 +   |
 +LL |     println!("{0:.1}", local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:.1}", local_f64);
 +LL +     println!("{local_f64:.1}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:79:5
 +   |
 +LL |     println!("{0} {0}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0} {0}", local_i32);
 +LL +     println!("{local_i32} {local_i32}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:80:5
 +   |
 +LL |     println!("{1} {} {0} {}", local_i32, local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{1} {} {0} {}", local_i32, local_f64);
 +LL +     println!("{local_f64} {local_i32} {local_i32} {local_f64}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:81:5
 +   |
 +LL |     println!("{0} {1}", local_i32, local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0} {1}", local_i32, local_f64);
 +LL +     println!("{local_i32} {local_f64}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:82:5
 +   |
 +LL |     println!("{1} {0}", local_i32, local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{1} {0}", local_i32, local_f64);
 +LL +     println!("{local_f64} {local_i32}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:83:5
 +   |
 +LL |     println!("{1} {0} {1} {0}", local_i32, local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{1} {0} {1} {0}", local_i32, local_f64);
 +LL +     println!("{local_f64} {local_i32} {local_f64} {local_i32}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:85:5
 +   |
 +LL |     println!("{v}", v = local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{v}", v = local_i32);
 +LL +     println!("{local_i32}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:86:5
 +   |
 +LL |     println!("{local_i32:0$}", width);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{local_i32:0$}", width);
 +LL +     println!("{local_i32:width$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:87:5
 +   |
 +LL |     println!("{local_i32:w$}", w = width);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{local_i32:w$}", w = width);
 +LL +     println!("{local_i32:width$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:88:5
 +   |
 +LL |     println!("{local_i32:.0$}", prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{local_i32:.0$}", prec);
 +LL +     println!("{local_i32:.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:89:5
 +   |
 +LL |     println!("{local_i32:.p$}", p = prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{local_i32:.p$}", p = prec);
 +LL +     println!("{local_i32:.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:90:5
 +   |
 +LL |     println!("{:0$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:0$}", v = val);
 +LL +     println!("{val:val$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:91:5
 +   |
 +LL |     println!("{0:0$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:0$}", v = val);
 +LL +     println!("{val:val$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:92:5
 +   |
 +LL |     println!("{:0$.0$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:0$.0$}", v = val);
 +LL +     println!("{val:val$.val$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:93:5
 +   |
 +LL |     println!("{0:0$.0$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:0$.0$}", v = val);
 +LL +     println!("{val:val$.val$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:94:5
 +   |
 +LL |     println!("{0:0$.v$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:0$.v$}", v = val);
 +LL +     println!("{val:val$.val$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:95:5
 +   |
 +LL |     println!("{0:v$.0$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:v$.0$}", v = val);
 +LL +     println!("{val:val$.val$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:96:5
 +   |
 +LL |     println!("{v:0$.0$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{v:0$.0$}", v = val);
 +LL +     println!("{val:val$.val$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:97:5
 +   |
 +LL |     println!("{v:v$.0$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{v:v$.0$}", v = val);
 +LL +     println!("{val:val$.val$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:98:5
 +   |
 +LL |     println!("{v:0$.v$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{v:0$.v$}", v = val);
 +LL +     println!("{val:val$.val$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:99:5
 +   |
 +LL |     println!("{v:v$.v$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{v:v$.v$}", v = val);
 +LL +     println!("{val:val$.val$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:100:5
 +   |
 +LL |     println!("{:0$}", width);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:0$}", width);
 +LL +     println!("{width:width$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:101:5
 +   |
 +LL |     println!("{:1$}", local_i32, width);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:1$}", local_i32, width);
 +LL +     println!("{local_i32:width$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:102:5
 +   |
 +LL |     println!("{:w$}", w = width);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:w$}", w = width);
 +LL +     println!("{width:width$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:103:5
 +   |
 +LL |     println!("{:w$}", local_i32, w = width);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:w$}", local_i32, w = width);
 +LL +     println!("{local_i32:width$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:104:5
 +   |
 +LL |     println!("{:.0$}", prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:.0$}", prec);
 +LL +     println!("{prec:.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:105:5
 +   |
 +LL |     println!("{:.1$}", local_i32, prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:.1$}", local_i32, prec);
 +LL +     println!("{local_i32:.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:106:5
 +   |
 +LL |     println!("{:.p$}", p = prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:.p$}", p = prec);
 +LL +     println!("{prec:.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:107:5
 +   |
 +LL |     println!("{:.p$}", local_i32, p = prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:.p$}", local_i32, p = prec);
 +LL +     println!("{local_i32:.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:108:5
 +   |
 +LL |     println!("{:0$.1$}", width, prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:0$.1$}", width, prec);
 +LL +     println!("{width:width$.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:109:5
 +   |
 +LL |     println!("{:0$.w$}", width, w = prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:0$.w$}", width, w = prec);
 +LL +     println!("{width:width$.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:110:5
 +   |
 +LL |     println!("{:1$.2$}", local_f64, width, prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:1$.2$}", local_f64, width, prec);
 +LL +     println!("{local_f64:width$.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:111:5
 +   |
 +LL |     println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec);
 +LL +     println!("{local_f64:width$.prec$} {local_f64} {width} {prec}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:123:5
 +   |
 +LL |     println!("Width = {}, value with width = {:0$}", local_i32, local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("Width = {}, value with width = {:0$}", local_i32, local_f64);
 +LL +     println!("Width = {local_i32}, value with width = {local_f64:local_i32$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:124:5
 +   |
 +LL |     println!("{:w$.p$}", local_i32, w = width, p = prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:w$.p$}", local_i32, w = width, p = prec);
 +LL +     println!("{local_i32:width$.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:125:5
 +   |
 +LL |     println!("{:w$.p$}", w = width, p = prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:w$.p$}", w = width, p = prec);
 +LL +     println!("{width:width$.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:126:20
 +   |
 +LL |     println!("{}", format!("{}", local_i32));
 +   |                    ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{}", format!("{}", local_i32));
 +LL +     println!("{}", format!("{local_i32}"));
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:149:5
 +   |
 +LL |     println!("{}", /* comment with a comma , in it */ val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{}", /* comment with a comma , in it */ val);
 +LL +     println!("{val}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
- error: aborting due to 70 previous errors
++  --> $DIR/uninlined_format_args.rs:155:9
++   |
++LL |         panic!("p1 {}", local_i32);
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: change this to
++   |
++LL -         panic!("p1 {}", local_i32);
++LL +         panic!("p1 {local_i32}");
++   |
++
++error: variables can be used directly in the `format!` string
++  --> $DIR/uninlined_format_args.rs:158:9
++   |
++LL |         panic!("p2 {0}", local_i32);
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: change this to
++   |
++LL -         panic!("p2 {0}", local_i32);
++LL +         panic!("p2 {local_i32}");
++   |
++
++error: variables can be used directly in the `format!` string
++  --> $DIR/uninlined_format_args.rs:161:9
++   |
++LL |         panic!("p3 {local_i32}", local_i32 = local_i32);
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: change this to
++   |
++LL -         panic!("p3 {local_i32}", local_i32 = local_i32);
++LL +         panic!("p3 {local_i32}");
++   |
++
++error: variables can be used directly in the `format!` string
++  --> $DIR/uninlined_format_args.rs:181:5
 +   |
 +LL |     println!("expand='{}'", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("expand='{}'", local_i32);
 +LL +     println!("expand='{local_i32}'");
 +   |
 +
++error: aborting due to 73 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..96cc0877960ec00c627a896f5eb54cafc633f2c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++// revisions: edition2018 edition2021
++//[edition2018] edition:2018
++//[edition2021] edition:2021
++// run-rustfix
++
++#![warn(clippy::uninlined_format_args)]
++
++fn main() {
++    let var = 1;
++
++    println!("val='{var}'");
++
++    if var > 0 {
++        panic!("p1 {}", var);
++    }
++    if var > 0 {
++        panic!("p2 {0}", var);
++    }
++    if var > 0 {
++        panic!("p3 {var}", var = var);
++    }
++
++    #[allow(non_fmt_panics)]
++    {
++        if var > 0 {
++            panic!("p4 {var}");
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2c8061259229a83f8f82609b7800cb12c226fabe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++error: variables can be used directly in the `format!` string
++  --> $DIR/uninlined_format_args_panic.rs:11:5
++   |
++LL |     println!("val='{}'", var);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::uninlined-format-args` implied by `-D warnings`
++help: change this to
++   |
++LL -     println!("val='{}'", var);
++LL +     println!("val='{var}'");
++   |
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..faf8ca4d3a797aa2e989571786cd8c317a2e6652
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++// revisions: edition2018 edition2021
++//[edition2018] edition:2018
++//[edition2021] edition:2021
++// run-rustfix
++
++#![warn(clippy::uninlined_format_args)]
++
++fn main() {
++    let var = 1;
++
++    println!("val='{var}'");
++
++    if var > 0 {
++        panic!("p1 {var}");
++    }
++    if var > 0 {
++        panic!("p2 {var}");
++    }
++    if var > 0 {
++        panic!("p3 {var}");
++    }
++
++    #[allow(non_fmt_panics)]
++    {
++        if var > 0 {
++            panic!("p4 {var}");
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0f09c45f41324b94da38117a4d885deabe904c0f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++error: variables can be used directly in the `format!` string
++  --> $DIR/uninlined_format_args_panic.rs:11:5
++   |
++LL |     println!("val='{}'", var);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::uninlined-format-args` implied by `-D warnings`
++help: change this to
++   |
++LL -     println!("val='{}'", var);
++LL +     println!("val='{var}'");
++   |
++
++error: variables can be used directly in the `format!` string
++  --> $DIR/uninlined_format_args_panic.rs:14:9
++   |
++LL |         panic!("p1 {}", var);
++   |         ^^^^^^^^^^^^^^^^^^^^
++   |
++help: change this to
++   |
++LL -         panic!("p1 {}", var);
++LL +         panic!("p1 {var}");
++   |
++
++error: variables can be used directly in the `format!` string
++  --> $DIR/uninlined_format_args_panic.rs:17:9
++   |
++LL |         panic!("p2 {0}", var);
++   |         ^^^^^^^^^^^^^^^^^^^^^
++   |
++help: change this to
++   |
++LL -         panic!("p2 {0}", var);
++LL +         panic!("p2 {var}");
++   |
++
++error: variables can be used directly in the `format!` string
++  --> $DIR/uninlined_format_args_panic.rs:20:9
++   |
++LL |         panic!("p3 {var}", var = var);
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: change this to
++   |
++LL -         panic!("p3 {var}", var = var);
++LL +         panic!("p3 {var}");
++   |
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6421c5bbed2f5a05a6b5523d8560f36a374ca6c3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++// revisions: edition2018 edition2021
++//[edition2018] edition:2018
++//[edition2021] edition:2021
++// run-rustfix
++
++#![warn(clippy::uninlined_format_args)]
++
++fn main() {
++    let var = 1;
++
++    println!("val='{}'", var);
++
++    if var > 0 {
++        panic!("p1 {}", var);
++    }
++    if var > 0 {
++        panic!("p2 {0}", var);
++    }
++    if var > 0 {
++        panic!("p3 {var}", var = var);
++    }
++
++    #[allow(non_fmt_panics)]
++    {
++        if var > 0 {
++            panic!("p4 {var}");
++        }
++    }
++}
index 94dc96427263cf36102800d5e04b25c608830eee,0000000000000000000000000000000000000000..ec8c6abfab91e725891f268967c59dabcb101e13
mode 100644,000000..100644
--- /dev/null
@@@ -1,114 -1,0 +1,118 @@@
 +// run-rustfix
 +#![warn(clippy::unnecessary_cast)]
 +#![allow(
 +    unused_must_use,
 +    clippy::borrow_as_ptr,
 +    clippy::no_effect,
 +    clippy::nonstandard_macro_braces,
 +    clippy::unnecessary_operation
 +)]
 +
 +#[rustfmt::skip]
 +fn main() {
 +    // Test cast_unnecessary
 +    1_i32;
 +    1_f32;
 +    false;
 +    &1i32 as &i32;
 +
 +    -1_i32;
 +    - 1_i32;
 +    -1_f32;
 +    1_i32;
 +    1_f32;
 +
 +    // macro version
 +    macro_rules! foo {
 +        ($a:ident, $b:ident) => {
 +            #[allow(unused)]
 +            pub fn $a() -> $b {
 +                1 as $b
 +            }
 +        };
 +    }
 +    foo!(a, i32);
 +    foo!(b, f32);
 +    foo!(c, f64);
 +
 +    // do not lint cast to cfg-dependant type
 +    1 as std::os::raw::c_char;
 +
 +    // do not lint cast to alias type
 +    1 as I32Alias;
 +    &1 as &I32Alias;
 +}
 +
 +type I32Alias = i32;
 +
 +mod fixable {
 +    #![allow(dead_code)]
 +
 +    fn main() {
 +        // casting integer literal to float is unnecessary
 +        100_f32;
 +        100_f64;
 +        100_f64;
 +        let _ = -100_f32;
 +        let _ = -100_f64;
 +        let _ = -100_f64;
 +        100_f32;
 +        100_f64;
 +        // Should not trigger
 +        #[rustfmt::skip]
 +        let v = vec!(1);
 +        &v as &[i32];
 +        0x10 as f32;
 +        0o10 as f32;
 +        0b10 as f32;
 +        0x11 as f64;
 +        0o11 as f64;
 +        0b11 as f64;
 +
 +        1_u32;
 +        0x10_i32;
 +        0b10_usize;
 +        0o73_u16;
 +        1_000_000_000_u32;
 +
 +        1.0_f64;
 +        0.5_f32;
 +
 +        1.0 as u16;
 +
 +        let _ = -1_i32;
 +        let _ = -1.0_f32;
 +
 +        let _ = 1 as I32Alias;
 +        let _ = &1 as &I32Alias;
 +    }
 +
 +    type I32Alias = i32;
 +
 +    fn issue_9380() {
 +        let _: i32 = -1_i32;
 +        let _: f32 = -(1) as f32;
 +        let _: i64 = -1_i64;
 +        let _: i64 = -(1.0) as i64;
 +
 +        let _ = -(1 + 1) as i64;
 +    }
 +
 +    fn issue_9563() {
 +        let _: f64 = (-8.0_f64).exp();
 +        #[allow(clippy::precedence)]
 +        let _: f64 = -8.0_f64.exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior
 +    }
 +
 +    fn issue_9562_non_literal() {
 +        fn foo() -> f32 {
 +            0.
 +        }
 +
 +        let _num = foo();
 +    }
++
++    fn issue_9603() {
++        let _: f32 = -0x400 as f32;
++    }
 +}
index e5150256f69ac49e5dbd465c616484352e5b5297,0000000000000000000000000000000000000000..5213cdc269bd4c5a9aec63cb2c5ff31a87e4ccf9
mode 100644,000000..100644
--- /dev/null
@@@ -1,114 -1,0 +1,118 @@@
 +// run-rustfix
 +#![warn(clippy::unnecessary_cast)]
 +#![allow(
 +    unused_must_use,
 +    clippy::borrow_as_ptr,
 +    clippy::no_effect,
 +    clippy::nonstandard_macro_braces,
 +    clippy::unnecessary_operation
 +)]
 +
 +#[rustfmt::skip]
 +fn main() {
 +    // Test cast_unnecessary
 +    1i32 as i32;
 +    1f32 as f32;
 +    false as bool;
 +    &1i32 as &i32;
 +
 +    -1_i32 as i32;
 +    - 1_i32 as i32;
 +    -1f32 as f32;
 +    1_i32 as i32;
 +    1_f32 as f32;
 +
 +    // macro version
 +    macro_rules! foo {
 +        ($a:ident, $b:ident) => {
 +            #[allow(unused)]
 +            pub fn $a() -> $b {
 +                1 as $b
 +            }
 +        };
 +    }
 +    foo!(a, i32);
 +    foo!(b, f32);
 +    foo!(c, f64);
 +
 +    // do not lint cast to cfg-dependant type
 +    1 as std::os::raw::c_char;
 +
 +    // do not lint cast to alias type
 +    1 as I32Alias;
 +    &1 as &I32Alias;
 +}
 +
 +type I32Alias = i32;
 +
 +mod fixable {
 +    #![allow(dead_code)]
 +
 +    fn main() {
 +        // casting integer literal to float is unnecessary
 +        100 as f32;
 +        100 as f64;
 +        100_i32 as f64;
 +        let _ = -100 as f32;
 +        let _ = -100 as f64;
 +        let _ = -100_i32 as f64;
 +        100. as f32;
 +        100. as f64;
 +        // Should not trigger
 +        #[rustfmt::skip]
 +        let v = vec!(1);
 +        &v as &[i32];
 +        0x10 as f32;
 +        0o10 as f32;
 +        0b10 as f32;
 +        0x11 as f64;
 +        0o11 as f64;
 +        0b11 as f64;
 +
 +        1 as u32;
 +        0x10 as i32;
 +        0b10 as usize;
 +        0o73 as u16;
 +        1_000_000_000 as u32;
 +
 +        1.0 as f64;
 +        0.5 as f32;
 +
 +        1.0 as u16;
 +
 +        let _ = -1 as i32;
 +        let _ = -1.0 as f32;
 +
 +        let _ = 1 as I32Alias;
 +        let _ = &1 as &I32Alias;
 +    }
 +
 +    type I32Alias = i32;
 +
 +    fn issue_9380() {
 +        let _: i32 = -(1) as i32;
 +        let _: f32 = -(1) as f32;
 +        let _: i64 = -(1) as i64;
 +        let _: i64 = -(1.0) as i64;
 +
 +        let _ = -(1 + 1) as i64;
 +    }
 +
 +    fn issue_9563() {
 +        let _: f64 = (-8.0 as f64).exp();
 +        #[allow(clippy::precedence)]
 +        let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior
 +    }
 +
 +    fn issue_9562_non_literal() {
 +        fn foo() -> f32 {
 +            0.
 +        }
 +
 +        let _num = foo() as f32;
 +    }
++
++    fn issue_9603() {
++        let _: f32 = -0x400 as f32;
++    }
 +}
index f97583aa22f9a77247990c48aab8a68b9ae73715,0000000000000000000000000000000000000000..fe09aad06bc84d792c10d99a04e8fdd37f05bdbd
mode 100644,000000..100644
--- /dev/null
@@@ -1,428 -1,0 +1,428 @@@
- #![allow(clippy::ptr_arg)]
 +// run-rustfix
 +
++#![allow(clippy::needless_borrow, clippy::ptr_arg)]
 +#![warn(clippy::unnecessary_to_owned)]
 +#![feature(custom_inner_attributes)]
 +
 +use std::borrow::Cow;
 +use std::ffi::{CStr, CString, OsStr, OsString};
 +use std::ops::Deref;
 +
 +#[derive(Clone)]
 +struct X(String);
 +
 +impl Deref for X {
 +    type Target = [u8];
 +    fn deref(&self) -> &[u8] {
 +        self.0.as_bytes()
 +    }
 +}
 +
 +impl AsRef<str> for X {
 +    fn as_ref(&self) -> &str {
 +        self.0.as_str()
 +    }
 +}
 +
 +impl ToString for X {
 +    fn to_string(&self) -> String {
 +        self.0.to_string()
 +    }
 +}
 +
 +impl X {
 +    fn join(&self, other: impl AsRef<str>) -> Self {
 +        let mut s = self.0.clone();
 +        s.push_str(other.as_ref());
 +        Self(s)
 +    }
 +}
 +
 +#[allow(dead_code)]
 +#[derive(Clone)]
 +enum FileType {
 +    Account,
 +    PrivateKey,
 +    Certificate,
 +}
 +
 +fn main() {
 +    let c_str = CStr::from_bytes_with_nul(&[0]).unwrap();
 +    let os_str = OsStr::new("x");
 +    let path = std::path::Path::new("x");
 +    let s = "x";
 +    let array = ["x"];
 +    let array_ref = &["x"];
 +    let slice = &["x"][..];
 +    let x = X(String::from("x"));
 +    let x_ref = &x;
 +
 +    require_c_str(&Cow::from(c_str));
 +    require_c_str(c_str);
 +
 +    require_os_str(os_str);
 +    require_os_str(&Cow::from(os_str));
 +    require_os_str(os_str);
 +
 +    require_path(path);
 +    require_path(&Cow::from(path));
 +    require_path(path);
 +
 +    require_str(s);
 +    require_str(&Cow::from(s));
 +    require_str(s);
 +    require_str(x_ref.as_ref());
 +
 +    require_slice(slice);
 +    require_slice(&Cow::from(slice));
 +    require_slice(array.as_ref());
 +    require_slice(array_ref.as_ref());
 +    require_slice(slice);
 +    require_slice(&x_ref.to_owned()); // No longer flagged because of #8759.
 +
 +    require_x(&Cow::<X>::Owned(x.clone()));
 +    require_x(&x_ref.to_owned()); // No longer flagged because of #8759.
 +
 +    require_deref_c_str(c_str);
 +    require_deref_os_str(os_str);
 +    require_deref_path(path);
 +    require_deref_str(s);
 +    require_deref_slice(slice);
 +
 +    require_impl_deref_c_str(c_str);
 +    require_impl_deref_os_str(os_str);
 +    require_impl_deref_path(path);
 +    require_impl_deref_str(s);
 +    require_impl_deref_slice(slice);
 +
 +    require_deref_str_slice(s, slice);
 +    require_deref_slice_str(slice, s);
 +
 +    require_as_ref_c_str(c_str);
 +    require_as_ref_os_str(os_str);
 +    require_as_ref_path(path);
 +    require_as_ref_str(s);
 +    require_as_ref_str(&x);
 +    require_as_ref_slice(array);
 +    require_as_ref_slice(array_ref);
 +    require_as_ref_slice(slice);
 +
 +    require_impl_as_ref_c_str(c_str);
 +    require_impl_as_ref_os_str(os_str);
 +    require_impl_as_ref_path(path);
 +    require_impl_as_ref_str(s);
 +    require_impl_as_ref_str(&x);
 +    require_impl_as_ref_slice(array);
 +    require_impl_as_ref_slice(array_ref);
 +    require_impl_as_ref_slice(slice);
 +
 +    require_as_ref_str_slice(s, array);
 +    require_as_ref_str_slice(s, array_ref);
 +    require_as_ref_str_slice(s, slice);
 +    require_as_ref_slice_str(array, s);
 +    require_as_ref_slice_str(array_ref, s);
 +    require_as_ref_slice_str(slice, s);
 +
 +    let _ = x.join(x_ref);
 +
 +    let _ = slice.iter().copied();
 +    let _ = slice.iter().copied();
 +    let _ = [std::path::PathBuf::new()][..].iter().cloned();
 +    let _ = [std::path::PathBuf::new()][..].iter().cloned();
 +
 +    let _ = slice.iter().copied();
 +    let _ = slice.iter().copied();
 +    let _ = [std::path::PathBuf::new()][..].iter().cloned();
 +    let _ = [std::path::PathBuf::new()][..].iter().cloned();
 +
 +    let _ = check_files(&[FileType::Account]);
 +
 +    // negative tests
 +    require_string(&s.to_string());
 +    require_string(&Cow::from(s).into_owned());
 +    require_string(&s.to_owned());
 +    require_string(&x_ref.to_string());
 +
 +    // `X` isn't copy.
 +    require_slice(&x.to_owned());
 +    require_deref_slice(x.to_owned());
 +
 +    // The following should be flagged by `redundant_clone`, but not by this lint.
 +    require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap());
 +    require_os_str(&OsString::from("x"));
 +    require_path(&std::path::PathBuf::from("x"));
 +    require_str(&String::from("x"));
 +    require_slice(&[String::from("x")]);
 +}
 +
 +fn require_c_str(_: &CStr) {}
 +fn require_os_str(_: &OsStr) {}
 +fn require_path(_: &std::path::Path) {}
 +fn require_str(_: &str) {}
 +fn require_slice<T>(_: &[T]) {}
 +fn require_x(_: &X) {}
 +
 +fn require_deref_c_str<T: Deref<Target = CStr>>(_: T) {}
 +fn require_deref_os_str<T: Deref<Target = OsStr>>(_: T) {}
 +fn require_deref_path<T: Deref<Target = std::path::Path>>(_: T) {}
 +fn require_deref_str<T: Deref<Target = str>>(_: T) {}
 +fn require_deref_slice<T, U: Deref<Target = [T]>>(_: U) {}
 +
 +fn require_impl_deref_c_str(_: impl Deref<Target = CStr>) {}
 +fn require_impl_deref_os_str(_: impl Deref<Target = OsStr>) {}
 +fn require_impl_deref_path(_: impl Deref<Target = std::path::Path>) {}
 +fn require_impl_deref_str(_: impl Deref<Target = str>) {}
 +fn require_impl_deref_slice<T>(_: impl Deref<Target = [T]>) {}
 +
 +fn require_deref_str_slice<T: Deref<Target = str>, U, V: Deref<Target = [U]>>(_: T, _: V) {}
 +fn require_deref_slice_str<T, U: Deref<Target = [T]>, V: Deref<Target = str>>(_: U, _: V) {}
 +
 +fn require_as_ref_c_str<T: AsRef<CStr>>(_: T) {}
 +fn require_as_ref_os_str<T: AsRef<OsStr>>(_: T) {}
 +fn require_as_ref_path<T: AsRef<std::path::Path>>(_: T) {}
 +fn require_as_ref_str<T: AsRef<str>>(_: T) {}
 +fn require_as_ref_slice<T, U: AsRef<[T]>>(_: U) {}
 +
 +fn require_impl_as_ref_c_str(_: impl AsRef<CStr>) {}
 +fn require_impl_as_ref_os_str(_: impl AsRef<OsStr>) {}
 +fn require_impl_as_ref_path(_: impl AsRef<std::path::Path>) {}
 +fn require_impl_as_ref_str(_: impl AsRef<str>) {}
 +fn require_impl_as_ref_slice<T>(_: impl AsRef<[T]>) {}
 +
 +fn require_as_ref_str_slice<T: AsRef<str>, U, V: AsRef<[U]>>(_: T, _: V) {}
 +fn require_as_ref_slice_str<T, U: AsRef<[T]>, V: AsRef<str>>(_: U, _: V) {}
 +
 +// `check_files` is based on:
 +// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262
 +fn check_files(file_types: &[FileType]) -> bool {
 +    for t in file_types {
 +        let path = match get_file_path(t) {
 +            Ok(p) => p,
 +            Err(_) => {
 +                return false;
 +            },
 +        };
 +        if !path.is_file() {
 +            return false;
 +        }
 +    }
 +    true
 +}
 +
 +fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
 +    Ok(std::path::PathBuf::new())
 +}
 +
 +fn require_string(_: &String) {}
 +
 +fn _msrv_1_35() {
 +    #![clippy::msrv = "1.35"]
 +    // `copied` was stabilized in 1.36, so clippy should use `cloned`.
 +    let _ = &["x"][..].iter().cloned();
 +}
 +
 +fn _msrv_1_36() {
 +    #![clippy::msrv = "1.36"]
 +    let _ = &["x"][..].iter().copied();
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8507
 +mod issue_8507 {
 +    #![allow(dead_code)]
 +
 +    struct Opaque<P>(P);
 +
 +    pub trait Abstracted {}
 +
 +    impl<P> Abstracted for Opaque<P> {}
 +
 +    fn build<P>(p: P) -> Opaque<P>
 +    where
 +        P: AsRef<str>,
 +    {
 +        Opaque(p)
 +    }
 +
 +    // Should not lint.
 +    fn test_str(s: &str) -> Box<dyn Abstracted> {
 +        Box::new(build(s.to_string()))
 +    }
 +
 +    // Should not lint.
 +    fn test_x(x: super::X) -> Box<dyn Abstracted> {
 +        Box::new(build(x))
 +    }
 +
 +    #[derive(Clone, Copy)]
 +    struct Y(&'static str);
 +
 +    impl AsRef<str> for Y {
 +        fn as_ref(&self) -> &str {
 +            self.0
 +        }
 +    }
 +
 +    impl ToString for Y {
 +        fn to_string(&self) -> String {
 +            self.0.to_string()
 +        }
 +    }
 +
 +    // Should lint because Y is copy.
 +    fn test_y(y: Y) -> Box<dyn Abstracted> {
 +        Box::new(build(y))
 +    }
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8759
 +mod issue_8759 {
 +    #![allow(dead_code)]
 +
 +    #[derive(Default)]
 +    struct View {}
 +
 +    impl std::borrow::ToOwned for View {
 +        type Owned = View;
 +        fn to_owned(&self) -> Self::Owned {
 +            View {}
 +        }
 +    }
 +
 +    #[derive(Default)]
 +    struct RenderWindow {
 +        default_view: View,
 +    }
 +
 +    impl RenderWindow {
 +        fn default_view(&self) -> &View {
 +            &self.default_view
 +        }
 +        fn set_view(&mut self, _view: &View) {}
 +    }
 +
 +    fn main() {
 +        let mut rw = RenderWindow::default();
 +        rw.set_view(&rw.default_view().to_owned());
 +    }
 +}
 +
 +mod issue_8759_variant {
 +    #![allow(dead_code)]
 +
 +    #[derive(Clone, Default)]
 +    struct View {}
 +
 +    #[derive(Default)]
 +    struct RenderWindow {
 +        default_view: View,
 +    }
 +
 +    impl RenderWindow {
 +        fn default_view(&self) -> &View {
 +            &self.default_view
 +        }
 +        fn set_view(&mut self, _view: &View) {}
 +    }
 +
 +    fn main() {
 +        let mut rw = RenderWindow::default();
 +        rw.set_view(&rw.default_view().to_owned());
 +    }
 +}
 +
 +mod issue_9317 {
 +    #![allow(dead_code)]
 +
 +    struct Bytes {}
 +
 +    impl ToString for Bytes {
 +        fn to_string(&self) -> String {
 +            "123".to_string()
 +        }
 +    }
 +
 +    impl AsRef<[u8]> for Bytes {
 +        fn as_ref(&self) -> &[u8] {
 +            &[1, 2, 3]
 +        }
 +    }
 +
 +    fn consume<C: AsRef<[u8]>>(c: C) {
 +        let _ = c;
 +    }
 +
 +    pub fn main() {
 +        let b = Bytes {};
 +        // Should not lint.
 +        consume(b.to_string());
 +    }
 +}
 +
 +mod issue_9351 {
 +    #![allow(dead_code)]
 +
 +    use std::ops::Deref;
 +    use std::path::{Path, PathBuf};
 +
 +    fn require_deref_path<T: Deref<Target = std::path::Path>>(x: T) -> T {
 +        x
 +    }
 +
 +    fn generic_arg_used_elsewhere<T: AsRef<Path>>(_x: T, _y: T) {}
 +
 +    fn id<T: AsRef<str>>(x: T) -> T {
 +        x
 +    }
 +
 +    fn predicates_are_satisfied(_x: impl std::fmt::Write) {}
 +
 +    // Should lint
 +    fn single_return() -> impl AsRef<str> {
 +        id("abc")
 +    }
 +
 +    // Should not lint
 +    fn multiple_returns(b: bool) -> impl AsRef<str> {
 +        if b {
 +            return String::new();
 +        }
 +
 +        id("abc".to_string())
 +    }
 +
 +    struct S1(String);
 +
 +    // Should not lint
 +    fn fields1() -> S1 {
 +        S1(id("abc".to_string()))
 +    }
 +
 +    struct S2 {
 +        s: String,
 +    }
 +
 +    // Should not lint
 +    fn fields2() {
 +        let mut s = S2 { s: "abc".into() };
 +        s.s = id("abc".to_string());
 +    }
 +
 +    pub fn main() {
 +        let path = std::path::Path::new("x");
 +        let path_buf = path.to_owned();
 +
 +        // Should not lint.
 +        let _x: PathBuf = require_deref_path(path.to_owned());
 +        generic_arg_used_elsewhere(path.to_owned(), path_buf);
 +        predicates_are_satisfied(id("abc".to_string()));
 +    }
 +}
 +
 +mod issue_9504 {
 +    #![allow(dead_code)]
 +
 +    async fn foo<S: AsRef<str>>(_: S) {}
 +    async fn bar() {
 +        foo(std::path::PathBuf::new().to_string_lossy().to_string()).await;
 +    }
 +}
index aa5394a565790c3f1478f12a56421745b4b18391,0000000000000000000000000000000000000000..3de6d0903c0f1d0e083fa5de7fc89cfc331fe1a1
mode 100644,000000..100644
--- /dev/null
@@@ -1,428 -1,0 +1,428 @@@
- #![allow(clippy::ptr_arg)]
 +// run-rustfix
 +
++#![allow(clippy::needless_borrow, clippy::ptr_arg)]
 +#![warn(clippy::unnecessary_to_owned)]
 +#![feature(custom_inner_attributes)]
 +
 +use std::borrow::Cow;
 +use std::ffi::{CStr, CString, OsStr, OsString};
 +use std::ops::Deref;
 +
 +#[derive(Clone)]
 +struct X(String);
 +
 +impl Deref for X {
 +    type Target = [u8];
 +    fn deref(&self) -> &[u8] {
 +        self.0.as_bytes()
 +    }
 +}
 +
 +impl AsRef<str> for X {
 +    fn as_ref(&self) -> &str {
 +        self.0.as_str()
 +    }
 +}
 +
 +impl ToString for X {
 +    fn to_string(&self) -> String {
 +        self.0.to_string()
 +    }
 +}
 +
 +impl X {
 +    fn join(&self, other: impl AsRef<str>) -> Self {
 +        let mut s = self.0.clone();
 +        s.push_str(other.as_ref());
 +        Self(s)
 +    }
 +}
 +
 +#[allow(dead_code)]
 +#[derive(Clone)]
 +enum FileType {
 +    Account,
 +    PrivateKey,
 +    Certificate,
 +}
 +
 +fn main() {
 +    let c_str = CStr::from_bytes_with_nul(&[0]).unwrap();
 +    let os_str = OsStr::new("x");
 +    let path = std::path::Path::new("x");
 +    let s = "x";
 +    let array = ["x"];
 +    let array_ref = &["x"];
 +    let slice = &["x"][..];
 +    let x = X(String::from("x"));
 +    let x_ref = &x;
 +
 +    require_c_str(&Cow::from(c_str).into_owned());
 +    require_c_str(&c_str.to_owned());
 +
 +    require_os_str(&os_str.to_os_string());
 +    require_os_str(&Cow::from(os_str).into_owned());
 +    require_os_str(&os_str.to_owned());
 +
 +    require_path(&path.to_path_buf());
 +    require_path(&Cow::from(path).into_owned());
 +    require_path(&path.to_owned());
 +
 +    require_str(&s.to_string());
 +    require_str(&Cow::from(s).into_owned());
 +    require_str(&s.to_owned());
 +    require_str(&x_ref.to_string());
 +
 +    require_slice(&slice.to_vec());
 +    require_slice(&Cow::from(slice).into_owned());
 +    require_slice(&array.to_owned());
 +    require_slice(&array_ref.to_owned());
 +    require_slice(&slice.to_owned());
 +    require_slice(&x_ref.to_owned()); // No longer flagged because of #8759.
 +
 +    require_x(&Cow::<X>::Owned(x.clone()).into_owned());
 +    require_x(&x_ref.to_owned()); // No longer flagged because of #8759.
 +
 +    require_deref_c_str(c_str.to_owned());
 +    require_deref_os_str(os_str.to_owned());
 +    require_deref_path(path.to_owned());
 +    require_deref_str(s.to_owned());
 +    require_deref_slice(slice.to_owned());
 +
 +    require_impl_deref_c_str(c_str.to_owned());
 +    require_impl_deref_os_str(os_str.to_owned());
 +    require_impl_deref_path(path.to_owned());
 +    require_impl_deref_str(s.to_owned());
 +    require_impl_deref_slice(slice.to_owned());
 +
 +    require_deref_str_slice(s.to_owned(), slice.to_owned());
 +    require_deref_slice_str(slice.to_owned(), s.to_owned());
 +
 +    require_as_ref_c_str(c_str.to_owned());
 +    require_as_ref_os_str(os_str.to_owned());
 +    require_as_ref_path(path.to_owned());
 +    require_as_ref_str(s.to_owned());
 +    require_as_ref_str(x.to_owned());
 +    require_as_ref_slice(array.to_owned());
 +    require_as_ref_slice(array_ref.to_owned());
 +    require_as_ref_slice(slice.to_owned());
 +
 +    require_impl_as_ref_c_str(c_str.to_owned());
 +    require_impl_as_ref_os_str(os_str.to_owned());
 +    require_impl_as_ref_path(path.to_owned());
 +    require_impl_as_ref_str(s.to_owned());
 +    require_impl_as_ref_str(x.to_owned());
 +    require_impl_as_ref_slice(array.to_owned());
 +    require_impl_as_ref_slice(array_ref.to_owned());
 +    require_impl_as_ref_slice(slice.to_owned());
 +
 +    require_as_ref_str_slice(s.to_owned(), array.to_owned());
 +    require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
 +    require_as_ref_str_slice(s.to_owned(), slice.to_owned());
 +    require_as_ref_slice_str(array.to_owned(), s.to_owned());
 +    require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
 +    require_as_ref_slice_str(slice.to_owned(), s.to_owned());
 +
 +    let _ = x.join(&x_ref.to_string());
 +
 +    let _ = slice.to_vec().into_iter();
 +    let _ = slice.to_owned().into_iter();
 +    let _ = [std::path::PathBuf::new()][..].to_vec().into_iter();
 +    let _ = [std::path::PathBuf::new()][..].to_owned().into_iter();
 +
 +    let _ = IntoIterator::into_iter(slice.to_vec());
 +    let _ = IntoIterator::into_iter(slice.to_owned());
 +    let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec());
 +    let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
 +
 +    let _ = check_files(&[FileType::Account]);
 +
 +    // negative tests
 +    require_string(&s.to_string());
 +    require_string(&Cow::from(s).into_owned());
 +    require_string(&s.to_owned());
 +    require_string(&x_ref.to_string());
 +
 +    // `X` isn't copy.
 +    require_slice(&x.to_owned());
 +    require_deref_slice(x.to_owned());
 +
 +    // The following should be flagged by `redundant_clone`, but not by this lint.
 +    require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
 +    require_os_str(&OsString::from("x").to_os_string());
 +    require_path(&std::path::PathBuf::from("x").to_path_buf());
 +    require_str(&String::from("x").to_string());
 +    require_slice(&[String::from("x")].to_owned());
 +}
 +
 +fn require_c_str(_: &CStr) {}
 +fn require_os_str(_: &OsStr) {}
 +fn require_path(_: &std::path::Path) {}
 +fn require_str(_: &str) {}
 +fn require_slice<T>(_: &[T]) {}
 +fn require_x(_: &X) {}
 +
 +fn require_deref_c_str<T: Deref<Target = CStr>>(_: T) {}
 +fn require_deref_os_str<T: Deref<Target = OsStr>>(_: T) {}
 +fn require_deref_path<T: Deref<Target = std::path::Path>>(_: T) {}
 +fn require_deref_str<T: Deref<Target = str>>(_: T) {}
 +fn require_deref_slice<T, U: Deref<Target = [T]>>(_: U) {}
 +
 +fn require_impl_deref_c_str(_: impl Deref<Target = CStr>) {}
 +fn require_impl_deref_os_str(_: impl Deref<Target = OsStr>) {}
 +fn require_impl_deref_path(_: impl Deref<Target = std::path::Path>) {}
 +fn require_impl_deref_str(_: impl Deref<Target = str>) {}
 +fn require_impl_deref_slice<T>(_: impl Deref<Target = [T]>) {}
 +
 +fn require_deref_str_slice<T: Deref<Target = str>, U, V: Deref<Target = [U]>>(_: T, _: V) {}
 +fn require_deref_slice_str<T, U: Deref<Target = [T]>, V: Deref<Target = str>>(_: U, _: V) {}
 +
 +fn require_as_ref_c_str<T: AsRef<CStr>>(_: T) {}
 +fn require_as_ref_os_str<T: AsRef<OsStr>>(_: T) {}
 +fn require_as_ref_path<T: AsRef<std::path::Path>>(_: T) {}
 +fn require_as_ref_str<T: AsRef<str>>(_: T) {}
 +fn require_as_ref_slice<T, U: AsRef<[T]>>(_: U) {}
 +
 +fn require_impl_as_ref_c_str(_: impl AsRef<CStr>) {}
 +fn require_impl_as_ref_os_str(_: impl AsRef<OsStr>) {}
 +fn require_impl_as_ref_path(_: impl AsRef<std::path::Path>) {}
 +fn require_impl_as_ref_str(_: impl AsRef<str>) {}
 +fn require_impl_as_ref_slice<T>(_: impl AsRef<[T]>) {}
 +
 +fn require_as_ref_str_slice<T: AsRef<str>, U, V: AsRef<[U]>>(_: T, _: V) {}
 +fn require_as_ref_slice_str<T, U: AsRef<[T]>, V: AsRef<str>>(_: U, _: V) {}
 +
 +// `check_files` is based on:
 +// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262
 +fn check_files(file_types: &[FileType]) -> bool {
 +    for t in file_types.to_vec() {
 +        let path = match get_file_path(&t) {
 +            Ok(p) => p,
 +            Err(_) => {
 +                return false;
 +            },
 +        };
 +        if !path.is_file() {
 +            return false;
 +        }
 +    }
 +    true
 +}
 +
 +fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
 +    Ok(std::path::PathBuf::new())
 +}
 +
 +fn require_string(_: &String) {}
 +
 +fn _msrv_1_35() {
 +    #![clippy::msrv = "1.35"]
 +    // `copied` was stabilized in 1.36, so clippy should use `cloned`.
 +    let _ = &["x"][..].to_vec().into_iter();
 +}
 +
 +fn _msrv_1_36() {
 +    #![clippy::msrv = "1.36"]
 +    let _ = &["x"][..].to_vec().into_iter();
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8507
 +mod issue_8507 {
 +    #![allow(dead_code)]
 +
 +    struct Opaque<P>(P);
 +
 +    pub trait Abstracted {}
 +
 +    impl<P> Abstracted for Opaque<P> {}
 +
 +    fn build<P>(p: P) -> Opaque<P>
 +    where
 +        P: AsRef<str>,
 +    {
 +        Opaque(p)
 +    }
 +
 +    // Should not lint.
 +    fn test_str(s: &str) -> Box<dyn Abstracted> {
 +        Box::new(build(s.to_string()))
 +    }
 +
 +    // Should not lint.
 +    fn test_x(x: super::X) -> Box<dyn Abstracted> {
 +        Box::new(build(x))
 +    }
 +
 +    #[derive(Clone, Copy)]
 +    struct Y(&'static str);
 +
 +    impl AsRef<str> for Y {
 +        fn as_ref(&self) -> &str {
 +            self.0
 +        }
 +    }
 +
 +    impl ToString for Y {
 +        fn to_string(&self) -> String {
 +            self.0.to_string()
 +        }
 +    }
 +
 +    // Should lint because Y is copy.
 +    fn test_y(y: Y) -> Box<dyn Abstracted> {
 +        Box::new(build(y.to_string()))
 +    }
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8759
 +mod issue_8759 {
 +    #![allow(dead_code)]
 +
 +    #[derive(Default)]
 +    struct View {}
 +
 +    impl std::borrow::ToOwned for View {
 +        type Owned = View;
 +        fn to_owned(&self) -> Self::Owned {
 +            View {}
 +        }
 +    }
 +
 +    #[derive(Default)]
 +    struct RenderWindow {
 +        default_view: View,
 +    }
 +
 +    impl RenderWindow {
 +        fn default_view(&self) -> &View {
 +            &self.default_view
 +        }
 +        fn set_view(&mut self, _view: &View) {}
 +    }
 +
 +    fn main() {
 +        let mut rw = RenderWindow::default();
 +        rw.set_view(&rw.default_view().to_owned());
 +    }
 +}
 +
 +mod issue_8759_variant {
 +    #![allow(dead_code)]
 +
 +    #[derive(Clone, Default)]
 +    struct View {}
 +
 +    #[derive(Default)]
 +    struct RenderWindow {
 +        default_view: View,
 +    }
 +
 +    impl RenderWindow {
 +        fn default_view(&self) -> &View {
 +            &self.default_view
 +        }
 +        fn set_view(&mut self, _view: &View) {}
 +    }
 +
 +    fn main() {
 +        let mut rw = RenderWindow::default();
 +        rw.set_view(&rw.default_view().to_owned());
 +    }
 +}
 +
 +mod issue_9317 {
 +    #![allow(dead_code)]
 +
 +    struct Bytes {}
 +
 +    impl ToString for Bytes {
 +        fn to_string(&self) -> String {
 +            "123".to_string()
 +        }
 +    }
 +
 +    impl AsRef<[u8]> for Bytes {
 +        fn as_ref(&self) -> &[u8] {
 +            &[1, 2, 3]
 +        }
 +    }
 +
 +    fn consume<C: AsRef<[u8]>>(c: C) {
 +        let _ = c;
 +    }
 +
 +    pub fn main() {
 +        let b = Bytes {};
 +        // Should not lint.
 +        consume(b.to_string());
 +    }
 +}
 +
 +mod issue_9351 {
 +    #![allow(dead_code)]
 +
 +    use std::ops::Deref;
 +    use std::path::{Path, PathBuf};
 +
 +    fn require_deref_path<T: Deref<Target = std::path::Path>>(x: T) -> T {
 +        x
 +    }
 +
 +    fn generic_arg_used_elsewhere<T: AsRef<Path>>(_x: T, _y: T) {}
 +
 +    fn id<T: AsRef<str>>(x: T) -> T {
 +        x
 +    }
 +
 +    fn predicates_are_satisfied(_x: impl std::fmt::Write) {}
 +
 +    // Should lint
 +    fn single_return() -> impl AsRef<str> {
 +        id("abc".to_string())
 +    }
 +
 +    // Should not lint
 +    fn multiple_returns(b: bool) -> impl AsRef<str> {
 +        if b {
 +            return String::new();
 +        }
 +
 +        id("abc".to_string())
 +    }
 +
 +    struct S1(String);
 +
 +    // Should not lint
 +    fn fields1() -> S1 {
 +        S1(id("abc".to_string()))
 +    }
 +
 +    struct S2 {
 +        s: String,
 +    }
 +
 +    // Should not lint
 +    fn fields2() {
 +        let mut s = S2 { s: "abc".into() };
 +        s.s = id("abc".to_string());
 +    }
 +
 +    pub fn main() {
 +        let path = std::path::Path::new("x");
 +        let path_buf = path.to_owned();
 +
 +        // Should not lint.
 +        let _x: PathBuf = require_deref_path(path.to_owned());
 +        generic_arg_used_elsewhere(path.to_owned(), path_buf);
 +        predicates_are_satisfied(id("abc".to_string()));
 +    }
 +}
 +
 +mod issue_9504 {
 +    #![allow(dead_code)]
 +
 +    async fn foo<S: AsRef<str>>(_: S) {}
 +    async fn bar() {
 +        foo(std::path::PathBuf::new().to_string_lossy().to_string()).await;
 +    }
 +}
index c223b5bc711b2a83956542e652457132f8136fd6,0000000000000000000000000000000000000000..9786c7b12128ba811fb0298222d3e48b821eed9f
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,47 @@@
- #![feature(box_patterns)]
 +// run-rustfix
 +
- #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
++#![feature(box_patterns, custom_inner_attributes)]
 +#![warn(clippy::unnested_or_patterns)]
 +#![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)]
++#![allow(unreachable_patterns, irrefutable_let_patterns, unused)]
 +
 +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 }) {}
 +}
++
++fn msrv_1_52() {
++    #![clippy::msrv = "1.52"]
++
++    if let [1] | [52] = [0] {}
++}
++
++fn msrv_1_53() {
++    #![clippy::msrv = "1.53"]
++
++    if let [1 | 53] = [0] {}
++}
index 04cd11036e4e00e954a27faf2c35cfb650db530e,0000000000000000000000000000000000000000..f57322396d4ac242c09efab9a7c9bdde7e8779cb
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,47 @@@
- #![feature(box_patterns)]
 +// run-rustfix
 +
- #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
++#![feature(box_patterns, custom_inner_attributes)]
 +#![warn(clippy::unnested_or_patterns)]
 +#![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)]
++#![allow(unreachable_patterns, irrefutable_let_patterns, unused)]
 +
 +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 }) {}
 +}
++
++fn msrv_1_52() {
++    #![clippy::msrv = "1.52"]
++
++    if let [1] | [52] = [0] {}
++}
++
++fn msrv_1_53() {
++    #![clippy::msrv = "1.53"]
++
++    if let [1] | [53] = [0] {}
++}
index 453c66cbba8fc2838311408c637b65e8b20c9e59,0000000000000000000000000000000000000000..fbc12fff0b0e7dccc8d4c89aab51a408b499ea83
mode 100644,000000..100644
--- /dev/null
@@@ -1,179 -1,0 +1,190 @@@
- error: aborting due to 16 previous errors
 +error: unnested or-patterns
 +  --> $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: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
 +  --> $DIR/unnested_or_patterns.rs:15:12
 +   |
 +LL |     if let Some(1) | C0 | Some(2) = None {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let Some(1 | 2) | C0 = None {}
 +   |            ~~~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
 +  --> $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: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: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: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: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: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: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: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: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: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: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: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: unnested or-patterns
++  --> $DIR/unnested_or_patterns.rs:46:12
++   |
++LL |     if let [1] | [53] = [0] {}
++   |            ^^^^^^^^^^
++   |
++help: nest the patterns
++   |
++LL |     if let [1 | 53] = [0] {}
++   |            ~~~~~~~~
++
++error: aborting due to 17 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2930722b42d9d4f39d33deb8119a3659b11df891
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++// run-rustfix
++
++#![warn(clippy::unused_format_specs)]
++#![allow(unused)]
++
++fn main() {
++    let f = 1.0f64;
++    println!("{}", 1.0);
++    println!("{f} {f:?}");
++
++    println!("{}", 1);
++}
++
++fn should_not_lint() {
++    let f = 1.0f64;
++    println!("{:.1}", 1.0);
++    println!("{f:.w$} {f:.*?}", 3, w = 2);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ee192a000d4b55e6fd4d1a8c8ba72238cc626a4f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++// run-rustfix
++
++#![warn(clippy::unused_format_specs)]
++#![allow(unused)]
++
++fn main() {
++    let f = 1.0f64;
++    println!("{:.}", 1.0);
++    println!("{f:.} {f:.?}");
++
++    println!("{:.}", 1);
++}
++
++fn should_not_lint() {
++    let f = 1.0f64;
++    println!("{:.1}", 1.0);
++    println!("{f:.w$} {f:.*?}", 3, w = 2);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7231c17e74c19cd9de5c4c36d6c227594917f147
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,54 @@@
++error: empty precision specifier has no effect
++  --> $DIR/unused_format_specs.rs:8:17
++   |
++LL |     println!("{:.}", 1.0);
++   |                 ^
++   |
++   = note: a precision specifier is not required to format floats
++   = note: `-D clippy::unused-format-specs` implied by `-D warnings`
++help: remove the `.`
++   |
++LL -     println!("{:.}", 1.0);
++LL +     println!("{}", 1.0);
++   |
++
++error: empty precision specifier has no effect
++  --> $DIR/unused_format_specs.rs:9:18
++   |
++LL |     println!("{f:.} {f:.?}");
++   |                  ^
++   |
++   = note: a precision specifier is not required to format floats
++help: remove the `.`
++   |
++LL -     println!("{f:.} {f:.?}");
++LL +     println!("{f} {f:.?}");
++   |
++
++error: empty precision specifier has no effect
++  --> $DIR/unused_format_specs.rs:9:24
++   |
++LL |     println!("{f:.} {f:.?}");
++   |                        ^
++   |
++   = note: a precision specifier is not required to format floats
++help: remove the `.`
++   |
++LL -     println!("{f:.} {f:.?}");
++LL +     println!("{f:.} {f:?}");
++   |
++
++error: empty precision specifier has no effect
++  --> $DIR/unused_format_specs.rs:11:17
++   |
++LL |     println!("{:.}", 1);
++   |                 ^
++   |
++help: remove the `.`
++   |
++LL -     println!("{:.}", 1);
++LL +     println!("{}", 1);
++   |
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..78601a3483d347428d5f2fededb5b42cb6a5d5ef
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++#![warn(clippy::unused_format_specs)]
++#![allow(unused)]
++
++macro_rules! format_args_from_macro {
++    () => {
++        format_args!("from macro")
++    };
++}
++
++fn main() {
++    // prints `.`, not `     .`
++    println!("{:5}.", format_args!(""));
++    //prints `abcde`, not `abc`
++    println!("{:.3}", format_args!("abcde"));
++
++    println!("{:5}.", format_args_from_macro!());
++
++    let args = format_args!("");
++    println!("{args:5}");
++}
++
++fn should_not_lint() {
++    println!("{}", format_args!(""));
++    // Technically the same as `{}`, but the `format_args` docs specifically mention that you can use
++    // debug formatting so allow it
++    println!("{:?}", format_args!(""));
++
++    let args = format_args!("");
++    println!("{args}");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9f1890282e6ac91f4e140e413de3e9ced145958f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,69 @@@
++error: format specifiers have no effect on `format_args!()`
++  --> $DIR/unused_format_specs_unfixable.rs:12:15
++   |
++LL |     println!("{:5}.", format_args!(""));
++   |               ^^^^
++   |
++   = note: `-D clippy::unused-format-specs` implied by `-D warnings`
++help: for the width to apply consider using `format!()`
++   |
++LL |     println!("{:5}.", format!(""));
++   |                       ~~~~~~
++help: if the current behavior is intentional, remove the format specifiers
++   |
++LL -     println!("{:5}.", format_args!(""));
++LL +     println!("{}.", format_args!(""));
++   |
++
++error: format specifiers have no effect on `format_args!()`
++  --> $DIR/unused_format_specs_unfixable.rs:14:15
++   |
++LL |     println!("{:.3}", format_args!("abcde"));
++   |               ^^^^^
++   |
++help: for the precision to apply consider using `format!()`
++   |
++LL |     println!("{:.3}", format!("abcde"));
++   |                       ~~~~~~
++help: if the current behavior is intentional, remove the format specifiers
++   |
++LL -     println!("{:.3}", format_args!("abcde"));
++LL +     println!("{}", format_args!("abcde"));
++   |
++
++error: format specifiers have no effect on `format_args!()`
++  --> $DIR/unused_format_specs_unfixable.rs:16:15
++   |
++LL |     println!("{:5}.", format_args_from_macro!());
++   |               ^^^^
++   |
++help: for the width to apply consider using `format!()`
++  --> $DIR/unused_format_specs_unfixable.rs:16:17
++   |
++LL |     println!("{:5}.", format_args_from_macro!());
++   |                 ^
++help: if the current behavior is intentional, remove the format specifiers
++   |
++LL -     println!("{:5}.", format_args_from_macro!());
++LL +     println!("{}.", format_args_from_macro!());
++   |
++
++error: format specifiers have no effect on `format_args!()`
++  --> $DIR/unused_format_specs_unfixable.rs:19:15
++   |
++LL |     println!("{args:5}");
++   |               ^^^^^^^^
++   |
++help: for the width to apply consider using `format!()`
++  --> $DIR/unused_format_specs_unfixable.rs:19:21
++   |
++LL |     println!("{args:5}");
++   |                     ^
++help: if the current behavior is intentional, remove the format specifiers
++   |
++LL -     println!("{args:5}");
++LL +     println!("{args}");
++   |
++
++error: aborting due to 4 previous errors
++
index 37986187da179b6acd40a6a006596a48343ae28a,0000000000000000000000000000000000000000..3b54fe9d5ff3d66e8d7074b7884a9367537e340d
mode 100644,000000..100644
--- /dev/null
@@@ -1,619 -1,0 +1,652 @@@
 +// run-rustfix
 +// aux-build:proc_macro_derive.rs
 +
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::use_self)]
 +#![allow(dead_code, unreachable_code)]
 +#![allow(
 +    clippy::should_implement_trait,
 +    clippy::upper_case_acronyms,
 +    clippy::from_over_into,
 +    clippy::self_named_constructors
 +)]
 +
 +#[macro_use]
 +extern crate proc_macro_derive;
 +
 +fn main() {}
 +
 +mod use_self {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Self {
 +            Self {}
 +        }
 +        fn test() -> Self {
 +            Self::new()
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        fn default() -> Self {
 +            Self::new()
 +        }
 +    }
 +}
 +
 +mod better {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Self {
 +            Self {}
 +        }
 +        fn test() -> Self {
 +            Self::new()
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        fn default() -> Self {
 +            Self::new()
 +        }
 +    }
 +}
 +
 +mod lifetimes {
 +    struct Foo<'a> {
 +        foo_str: &'a str,
 +    }
 +
 +    impl<'a> Foo<'a> {
 +        // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
 +        // Foo<'b>`
 +        fn foo(s: &str) -> Foo {
 +            Foo { foo_str: s }
 +        }
 +        // cannot replace with `Self`, because that's `Foo<'a>`
 +        fn bar() -> Foo<'static> {
 +            Foo { foo_str: "foo" }
 +        }
 +
 +        // FIXME: the lint does not handle lifetimed struct
 +        // `Self` should be applicable here
 +        fn clone(&self) -> Foo<'a> {
 +            Foo { foo_str: self.foo_str }
 +        }
 +    }
 +}
 +
 +mod issue2894 {
 +    trait IntoBytes {
 +        fn to_bytes(self) -> Vec<u8>;
 +    }
 +
 +    // This should not be linted
 +    impl IntoBytes for u8 {
 +        fn to_bytes(self) -> Vec<u8> {
 +            vec![self]
 +        }
 +    }
 +}
 +
 +mod existential {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn bad(foos: &[Self]) -> impl Iterator<Item = &Self> {
 +            foos.iter()
 +        }
 +
 +        fn good(foos: &[Self]) -> impl Iterator<Item = &Self> {
 +            foos.iter()
 +        }
 +    }
 +}
 +
 +mod tuple_structs {
 +    pub struct TS(i32);
 +
 +    impl TS {
 +        pub fn ts() -> Self {
 +            Self(0)
 +        }
 +    }
 +}
 +
 +mod macros {
 +    macro_rules! use_self_expand {
 +        () => {
 +            fn new() -> Foo {
 +                Foo {}
 +            }
 +        };
 +    }
 +
 +    struct Foo;
 +
 +    impl Foo {
 +        use_self_expand!(); // Should not lint in local macros
 +    }
 +
 +    #[derive(StructAUseSelf)] // Should not lint in derives
 +    struct A;
 +}
 +
 +mod nesting {
 +    struct Foo;
 +    impl Foo {
 +        fn foo() {
 +            #[allow(unused_imports)]
 +            use self::Foo; // Can't use Self here
 +            struct Bar {
 +                foo: Foo, // Foo != Self
 +            }
 +
 +            impl Bar {
 +                fn bar() -> Self {
 +                    Self { foo: Foo {} }
 +                }
 +            }
 +
 +            // Can't use Self here
 +            fn baz() -> Foo {
 +                Foo {}
 +            }
 +        }
 +
 +        // Should lint here
 +        fn baz() -> Self {
 +            Self {}
 +        }
 +    }
 +
 +    enum Enum {
 +        A,
 +        B(u64),
 +        C { field: bool },
 +    }
 +    impl Enum {
 +        fn method() {
 +            #[allow(unused_imports)]
 +            use self::Enum::*; // Issue 3425
 +            static STATIC: Enum = Enum::A; // Can't use Self as type
 +        }
 +
 +        fn method2() {
 +            let _ = Self::B(42);
 +            let _ = Self::C { field: true };
 +            let _ = Self::A;
 +        }
 +    }
 +}
 +
 +mod issue3410 {
 +
 +    struct A;
 +    struct B;
 +
 +    trait Trait<T> {
 +        fn a(v: T) -> Self;
 +    }
 +
 +    impl Trait<Vec<A>> for Vec<B> {
 +        fn a(_: Vec<A>) -> Self {
 +            unimplemented!()
 +        }
 +    }
 +
 +    impl<T> Trait<Vec<A>> for Vec<T>
 +    where
 +        T: Trait<B>,
 +    {
 +        fn a(v: Vec<A>) -> Self {
 +            <Vec<B>>::a(v).into_iter().map(Trait::a).collect()
 +        }
 +    }
 +}
 +
 +#[allow(clippy::no_effect, path_statements)]
 +mod rustfix {
 +    mod nested {
 +        pub struct A;
 +    }
 +
 +    impl nested::A {
 +        const A: bool = true;
 +
 +        fn fun_1() {}
 +
 +        fn fun_2() {
 +            Self::fun_1();
 +            Self::A;
 +
 +            Self {};
 +        }
 +    }
 +}
 +
 +mod issue3567 {
 +    struct TestStruct;
 +    impl TestStruct {
 +        fn from_something() -> Self {
 +            Self {}
 +        }
 +    }
 +
 +    trait Test {
 +        fn test() -> TestStruct;
 +    }
 +
 +    impl Test for TestStruct {
 +        fn test() -> TestStruct {
 +            Self::from_something()
 +        }
 +    }
 +}
 +
 +mod paths_created_by_lowering {
 +    use std::ops::Range;
 +
 +    struct S;
 +
 +    impl S {
 +        const A: usize = 0;
 +        const B: usize = 1;
 +
 +        async fn g() -> Self {
 +            Self {}
 +        }
 +
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
 +            &p[Self::A..Self::B]
 +        }
 +    }
 +
 +    trait T {
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8];
 +    }
 +
 +    impl T for Range<u8> {
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
 +            &p[0..1]
 +        }
 +    }
 +}
 +
 +// reused from #1997
 +mod generics {
 +    struct Foo<T> {
 +        value: T,
 +    }
 +
 +    impl<T> Foo<T> {
 +        // `Self` is applicable here
 +        fn foo(value: T) -> Self {
 +            Self { value }
 +        }
 +
 +        // `Cannot` use `Self` as a return type as the generic types are different
 +        fn bar(value: i32) -> Foo<i32> {
 +            Foo { value }
 +        }
 +    }
 +}
 +
 +mod issue4140 {
 +    pub struct Error<From, To> {
 +        _from: From,
 +        _too: To,
 +    }
 +
 +    pub trait From<T> {
 +        type From;
 +        type To;
 +
 +        fn from(value: T) -> Self;
 +    }
 +
 +    pub trait TryFrom<T>
 +    where
 +        Self: Sized,
 +    {
 +        type From;
 +        type To;
 +
 +        fn try_from(value: T) -> Result<Self, Error<Self::From, Self::To>>;
 +    }
 +
 +    // FIXME: Suggested fix results in infinite recursion.
 +    // impl<F, T> TryFrom<F> for T
 +    // where
 +    //     T: From<F>,
 +    // {
 +    //     type From = Self::From;
 +    //     type To = Self::To;
 +
 +    //     fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> {
 +    //         Ok(From::from(value))
 +    //     }
 +    // }
 +
 +    impl From<bool> for i64 {
 +        type From = bool;
 +        type To = Self;
 +
 +        fn from(value: bool) -> Self {
 +            if value { 100 } else { 0 }
 +        }
 +    }
 +}
 +
 +mod issue2843 {
 +    trait Foo {
 +        type Bar;
 +    }
 +
 +    impl Foo for usize {
 +        type Bar = u8;
 +    }
 +
 +    impl<T: Foo> Foo for Option<T> {
 +        type Bar = Option<T::Bar>;
 +    }
 +}
 +
 +mod issue3859 {
 +    pub struct Foo;
 +    pub struct Bar([usize; 3]);
 +
 +    impl Foo {
 +        pub const BAR: usize = 3;
 +
 +        pub fn foo() {
 +            const _X: usize = Foo::BAR;
 +            // const _Y: usize = Self::BAR;
 +        }
 +    }
 +}
 +
 +mod issue4305 {
 +    trait Foo: 'static {}
 +
 +    struct Bar;
 +
 +    impl Foo for Bar {}
 +
 +    impl<T: Foo> From<T> for Box<dyn Foo> {
 +        fn from(t: T) -> Self {
 +            Box::new(t)
 +        }
 +    }
 +}
 +
 +mod lint_at_item_level {
 +    struct Foo;
 +
 +    #[allow(clippy::use_self)]
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    #[allow(clippy::use_self)]
 +    impl Default for Foo {
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod lint_at_impl_item_level {
 +    struct Foo;
 +
 +    impl Foo {
 +        #[allow(clippy::use_self)]
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        #[allow(clippy::use_self)]
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod issue4734 {
 +    #[repr(C, packed)]
 +    pub struct X {
 +        pub x: u32,
 +    }
 +
 +    impl From<X> for u32 {
 +        fn from(c: X) -> Self {
 +            unsafe { core::mem::transmute(c) }
 +        }
 +    }
 +}
 +
 +mod nested_paths {
 +    use std::convert::Into;
 +    mod submod {
 +        pub struct B;
 +        pub struct C;
 +
 +        impl Into<C> for B {
 +            fn into(self) -> C {
 +                C {}
 +            }
 +        }
 +    }
 +
 +    struct A<T> {
 +        t: T,
 +    }
 +
 +    impl<T> A<T> {
 +        fn new<V: Into<T>>(v: V) -> Self {
 +            Self { t: Into::into(v) }
 +        }
 +    }
 +
 +    impl A<submod::C> {
 +        fn test() -> Self {
 +            Self::new::<submod::B>(submod::B {})
 +        }
 +    }
 +}
 +
 +mod issue6818 {
 +    #[derive(serde::Deserialize)]
 +    struct A {
 +        a: i32,
 +    }
 +}
 +
 +mod issue7206 {
 +    struct MyStruct<const C: char>;
 +    impl From<MyStruct<'a'>> for MyStruct<'b'> {
 +        fn from(_s: MyStruct<'a'>) -> Self {
 +            Self
 +        }
 +    }
 +
 +    // keep linting non-`Const` generic args
 +    struct S<'a> {
 +        inner: &'a str,
 +    }
 +
 +    struct S2<T> {
 +        inner: T,
 +    }
 +
 +    impl<T> S2<T> {
 +        fn new() -> Self {
 +            unimplemented!();
 +        }
 +    }
 +
 +    impl<'a> S2<S<'a>> {
 +        fn new_again() -> Self {
 +            Self::new()
 +        }
 +    }
 +}
 +
 +mod self_is_ty_param {
 +    trait Trait {
 +        type Type;
 +        type Hi;
 +
 +        fn test();
 +    }
 +
 +    impl<I> Trait for I
 +    where
 +        I: Iterator,
 +        I::Item: Trait, // changing this to Self would require <Self as Iterator>
 +    {
 +        type Type = I;
 +        type Hi = I::Item;
 +
 +        fn test() {
 +            let _: I::Item;
 +            let _: I; // this could lint, but is questionable
 +        }
 +    }
 +}
 +
 +mod use_self_in_pat {
 +    enum Foo {
 +        Bar,
 +        Baz,
 +    }
 +
 +    impl Foo {
 +        fn do_stuff(self) {
 +            match self {
 +                Self::Bar => unimplemented!(),
 +                Self::Baz => unimplemented!(),
 +            }
 +            match Some(1) {
 +                Some(_) => unimplemented!(),
 +                None => unimplemented!(),
 +            }
 +            if let Self::Bar = self {
 +                unimplemented!()
 +            }
 +        }
 +    }
 +}
 +
 +mod issue8845 {
 +    pub enum Something {
 +        Num(u8),
 +        TupleNums(u8, u8),
 +        StructNums { one: u8, two: u8 },
 +    }
 +
 +    struct Foo(u8);
 +
 +    struct Bar {
 +        x: u8,
 +        y: usize,
 +    }
 +
 +    impl Something {
 +        fn get_value(&self) -> u8 {
 +            match self {
 +                Self::Num(n) => *n,
 +                Self::TupleNums(n, _m) => *n,
 +                Self::StructNums { one, two: _ } => *one,
 +            }
 +        }
 +
 +        fn use_crate(&self) -> u8 {
 +            match self {
 +                Self::Num(n) => *n,
 +                Self::TupleNums(n, _m) => *n,
 +                Self::StructNums { one, two: _ } => *one,
 +            }
 +        }
 +
 +        fn imported_values(&self) -> u8 {
 +            use Something::*;
 +            match self {
 +                Num(n) => *n,
 +                TupleNums(n, _m) => *n,
 +                StructNums { one, two: _ } => *one,
 +            }
 +        }
 +    }
 +
 +    impl Foo {
 +        fn get_value(&self) -> u8 {
 +            let Self(x) = self;
 +            *x
 +        }
 +
 +        fn use_crate(&self) -> u8 {
 +            let Self(x) = self;
 +            *x
 +        }
 +    }
 +
 +    impl Bar {
 +        fn get_value(&self) -> u8 {
 +            let Self { x, .. } = self;
 +            *x
 +        }
 +
 +        fn use_crate(&self) -> u8 {
 +            let Self { x, .. } = self;
 +            *x
 +        }
 +    }
 +}
 +
 +mod issue6902 {
 +    use serde::Serialize;
 +
 +    #[derive(Serialize)]
 +    pub enum Foo {
 +        Bar = 1,
 +    }
 +}
++
++fn msrv_1_36() {
++    #![clippy::msrv = "1.36"]
++
++    enum E {
++        A,
++    }
++
++    impl E {
++        fn foo(self) {
++            match self {
++                E::A => {},
++            }
++        }
++    }
++}
++
++fn msrv_1_37() {
++    #![clippy::msrv = "1.37"]
++
++    enum E {
++        A,
++    }
++
++    impl E {
++        fn foo(self) {
++            match self {
++                Self::A => {},
++            }
++        }
++    }
++}
index 1b2b3337c92ed00fc30a08d00bb405c9dd4abe8d,0000000000000000000000000000000000000000..bf87633cd2d8f1b675552f2af3b5fae37ad5f191
mode 100644,000000..100644
--- /dev/null
@@@ -1,619 -1,0 +1,652 @@@
 +// run-rustfix
 +// aux-build:proc_macro_derive.rs
 +
++#![feature(custom_inner_attributes)]
 +#![warn(clippy::use_self)]
 +#![allow(dead_code, unreachable_code)]
 +#![allow(
 +    clippy::should_implement_trait,
 +    clippy::upper_case_acronyms,
 +    clippy::from_over_into,
 +    clippy::self_named_constructors
 +)]
 +
 +#[macro_use]
 +extern crate proc_macro_derive;
 +
 +fn main() {}
 +
 +mod use_self {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +        fn test() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod better {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Self {
 +            Self {}
 +        }
 +        fn test() -> Self {
 +            Self::new()
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        fn default() -> Self {
 +            Self::new()
 +        }
 +    }
 +}
 +
 +mod lifetimes {
 +    struct Foo<'a> {
 +        foo_str: &'a str,
 +    }
 +
 +    impl<'a> Foo<'a> {
 +        // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
 +        // Foo<'b>`
 +        fn foo(s: &str) -> Foo {
 +            Foo { foo_str: s }
 +        }
 +        // cannot replace with `Self`, because that's `Foo<'a>`
 +        fn bar() -> Foo<'static> {
 +            Foo { foo_str: "foo" }
 +        }
 +
 +        // FIXME: the lint does not handle lifetimed struct
 +        // `Self` should be applicable here
 +        fn clone(&self) -> Foo<'a> {
 +            Foo { foo_str: self.foo_str }
 +        }
 +    }
 +}
 +
 +mod issue2894 {
 +    trait IntoBytes {
 +        fn to_bytes(self) -> Vec<u8>;
 +    }
 +
 +    // This should not be linted
 +    impl IntoBytes for u8 {
 +        fn to_bytes(self) -> Vec<u8> {
 +            vec![self]
 +        }
 +    }
 +}
 +
 +mod existential {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
 +            foos.iter()
 +        }
 +
 +        fn good(foos: &[Self]) -> impl Iterator<Item = &Self> {
 +            foos.iter()
 +        }
 +    }
 +}
 +
 +mod tuple_structs {
 +    pub struct TS(i32);
 +
 +    impl TS {
 +        pub fn ts() -> Self {
 +            TS(0)
 +        }
 +    }
 +}
 +
 +mod macros {
 +    macro_rules! use_self_expand {
 +        () => {
 +            fn new() -> Foo {
 +                Foo {}
 +            }
 +        };
 +    }
 +
 +    struct Foo;
 +
 +    impl Foo {
 +        use_self_expand!(); // Should not lint in local macros
 +    }
 +
 +    #[derive(StructAUseSelf)] // Should not lint in derives
 +    struct A;
 +}
 +
 +mod nesting {
 +    struct Foo;
 +    impl Foo {
 +        fn foo() {
 +            #[allow(unused_imports)]
 +            use self::Foo; // Can't use Self here
 +            struct Bar {
 +                foo: Foo, // Foo != Self
 +            }
 +
 +            impl Bar {
 +                fn bar() -> Bar {
 +                    Bar { foo: Foo {} }
 +                }
 +            }
 +
 +            // Can't use Self here
 +            fn baz() -> Foo {
 +                Foo {}
 +            }
 +        }
 +
 +        // Should lint here
 +        fn baz() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    enum Enum {
 +        A,
 +        B(u64),
 +        C { field: bool },
 +    }
 +    impl Enum {
 +        fn method() {
 +            #[allow(unused_imports)]
 +            use self::Enum::*; // Issue 3425
 +            static STATIC: Enum = Enum::A; // Can't use Self as type
 +        }
 +
 +        fn method2() {
 +            let _ = Enum::B(42);
 +            let _ = Enum::C { field: true };
 +            let _ = Enum::A;
 +        }
 +    }
 +}
 +
 +mod issue3410 {
 +
 +    struct A;
 +    struct B;
 +
 +    trait Trait<T> {
 +        fn a(v: T) -> Self;
 +    }
 +
 +    impl Trait<Vec<A>> for Vec<B> {
 +        fn a(_: Vec<A>) -> Self {
 +            unimplemented!()
 +        }
 +    }
 +
 +    impl<T> Trait<Vec<A>> for Vec<T>
 +    where
 +        T: Trait<B>,
 +    {
 +        fn a(v: Vec<A>) -> Self {
 +            <Vec<B>>::a(v).into_iter().map(Trait::a).collect()
 +        }
 +    }
 +}
 +
 +#[allow(clippy::no_effect, path_statements)]
 +mod rustfix {
 +    mod nested {
 +        pub struct A;
 +    }
 +
 +    impl nested::A {
 +        const A: bool = true;
 +
 +        fn fun_1() {}
 +
 +        fn fun_2() {
 +            nested::A::fun_1();
 +            nested::A::A;
 +
 +            nested::A {};
 +        }
 +    }
 +}
 +
 +mod issue3567 {
 +    struct TestStruct;
 +    impl TestStruct {
 +        fn from_something() -> Self {
 +            Self {}
 +        }
 +    }
 +
 +    trait Test {
 +        fn test() -> TestStruct;
 +    }
 +
 +    impl Test for TestStruct {
 +        fn test() -> TestStruct {
 +            TestStruct::from_something()
 +        }
 +    }
 +}
 +
 +mod paths_created_by_lowering {
 +    use std::ops::Range;
 +
 +    struct S;
 +
 +    impl S {
 +        const A: usize = 0;
 +        const B: usize = 1;
 +
 +        async fn g() -> S {
 +            S {}
 +        }
 +
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
 +            &p[S::A..S::B]
 +        }
 +    }
 +
 +    trait T {
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8];
 +    }
 +
 +    impl T for Range<u8> {
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
 +            &p[0..1]
 +        }
 +    }
 +}
 +
 +// reused from #1997
 +mod generics {
 +    struct Foo<T> {
 +        value: T,
 +    }
 +
 +    impl<T> Foo<T> {
 +        // `Self` is applicable here
 +        fn foo(value: T) -> Foo<T> {
 +            Foo::<T> { value }
 +        }
 +
 +        // `Cannot` use `Self` as a return type as the generic types are different
 +        fn bar(value: i32) -> Foo<i32> {
 +            Foo { value }
 +        }
 +    }
 +}
 +
 +mod issue4140 {
 +    pub struct Error<From, To> {
 +        _from: From,
 +        _too: To,
 +    }
 +
 +    pub trait From<T> {
 +        type From;
 +        type To;
 +
 +        fn from(value: T) -> Self;
 +    }
 +
 +    pub trait TryFrom<T>
 +    where
 +        Self: Sized,
 +    {
 +        type From;
 +        type To;
 +
 +        fn try_from(value: T) -> Result<Self, Error<Self::From, Self::To>>;
 +    }
 +
 +    // FIXME: Suggested fix results in infinite recursion.
 +    // impl<F, T> TryFrom<F> for T
 +    // where
 +    //     T: From<F>,
 +    // {
 +    //     type From = Self::From;
 +    //     type To = Self::To;
 +
 +    //     fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> {
 +    //         Ok(From::from(value))
 +    //     }
 +    // }
 +
 +    impl From<bool> for i64 {
 +        type From = bool;
 +        type To = Self;
 +
 +        fn from(value: bool) -> Self {
 +            if value { 100 } else { 0 }
 +        }
 +    }
 +}
 +
 +mod issue2843 {
 +    trait Foo {
 +        type Bar;
 +    }
 +
 +    impl Foo for usize {
 +        type Bar = u8;
 +    }
 +
 +    impl<T: Foo> Foo for Option<T> {
 +        type Bar = Option<T::Bar>;
 +    }
 +}
 +
 +mod issue3859 {
 +    pub struct Foo;
 +    pub struct Bar([usize; 3]);
 +
 +    impl Foo {
 +        pub const BAR: usize = 3;
 +
 +        pub fn foo() {
 +            const _X: usize = Foo::BAR;
 +            // const _Y: usize = Self::BAR;
 +        }
 +    }
 +}
 +
 +mod issue4305 {
 +    trait Foo: 'static {}
 +
 +    struct Bar;
 +
 +    impl Foo for Bar {}
 +
 +    impl<T: Foo> From<T> for Box<dyn Foo> {
 +        fn from(t: T) -> Self {
 +            Box::new(t)
 +        }
 +    }
 +}
 +
 +mod lint_at_item_level {
 +    struct Foo;
 +
 +    #[allow(clippy::use_self)]
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    #[allow(clippy::use_self)]
 +    impl Default for Foo {
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod lint_at_impl_item_level {
 +    struct Foo;
 +
 +    impl Foo {
 +        #[allow(clippy::use_self)]
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        #[allow(clippy::use_self)]
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod issue4734 {
 +    #[repr(C, packed)]
 +    pub struct X {
 +        pub x: u32,
 +    }
 +
 +    impl From<X> for u32 {
 +        fn from(c: X) -> Self {
 +            unsafe { core::mem::transmute(c) }
 +        }
 +    }
 +}
 +
 +mod nested_paths {
 +    use std::convert::Into;
 +    mod submod {
 +        pub struct B;
 +        pub struct C;
 +
 +        impl Into<C> for B {
 +            fn into(self) -> C {
 +                C {}
 +            }
 +        }
 +    }
 +
 +    struct A<T> {
 +        t: T,
 +    }
 +
 +    impl<T> A<T> {
 +        fn new<V: Into<T>>(v: V) -> Self {
 +            Self { t: Into::into(v) }
 +        }
 +    }
 +
 +    impl A<submod::C> {
 +        fn test() -> Self {
 +            A::new::<submod::B>(submod::B {})
 +        }
 +    }
 +}
 +
 +mod issue6818 {
 +    #[derive(serde::Deserialize)]
 +    struct A {
 +        a: i32,
 +    }
 +}
 +
 +mod issue7206 {
 +    struct MyStruct<const C: char>;
 +    impl From<MyStruct<'a'>> for MyStruct<'b'> {
 +        fn from(_s: MyStruct<'a'>) -> Self {
 +            Self
 +        }
 +    }
 +
 +    // keep linting non-`Const` generic args
 +    struct S<'a> {
 +        inner: &'a str,
 +    }
 +
 +    struct S2<T> {
 +        inner: T,
 +    }
 +
 +    impl<T> S2<T> {
 +        fn new() -> Self {
 +            unimplemented!();
 +        }
 +    }
 +
 +    impl<'a> S2<S<'a>> {
 +        fn new_again() -> Self {
 +            S2::new()
 +        }
 +    }
 +}
 +
 +mod self_is_ty_param {
 +    trait Trait {
 +        type Type;
 +        type Hi;
 +
 +        fn test();
 +    }
 +
 +    impl<I> Trait for I
 +    where
 +        I: Iterator,
 +        I::Item: Trait, // changing this to Self would require <Self as Iterator>
 +    {
 +        type Type = I;
 +        type Hi = I::Item;
 +
 +        fn test() {
 +            let _: I::Item;
 +            let _: I; // this could lint, but is questionable
 +        }
 +    }
 +}
 +
 +mod use_self_in_pat {
 +    enum Foo {
 +        Bar,
 +        Baz,
 +    }
 +
 +    impl Foo {
 +        fn do_stuff(self) {
 +            match self {
 +                Foo::Bar => unimplemented!(),
 +                Foo::Baz => unimplemented!(),
 +            }
 +            match Some(1) {
 +                Some(_) => unimplemented!(),
 +                None => unimplemented!(),
 +            }
 +            if let Foo::Bar = self {
 +                unimplemented!()
 +            }
 +        }
 +    }
 +}
 +
 +mod issue8845 {
 +    pub enum Something {
 +        Num(u8),
 +        TupleNums(u8, u8),
 +        StructNums { one: u8, two: u8 },
 +    }
 +
 +    struct Foo(u8);
 +
 +    struct Bar {
 +        x: u8,
 +        y: usize,
 +    }
 +
 +    impl Something {
 +        fn get_value(&self) -> u8 {
 +            match self {
 +                Something::Num(n) => *n,
 +                Something::TupleNums(n, _m) => *n,
 +                Something::StructNums { one, two: _ } => *one,
 +            }
 +        }
 +
 +        fn use_crate(&self) -> u8 {
 +            match self {
 +                crate::issue8845::Something::Num(n) => *n,
 +                crate::issue8845::Something::TupleNums(n, _m) => *n,
 +                crate::issue8845::Something::StructNums { one, two: _ } => *one,
 +            }
 +        }
 +
 +        fn imported_values(&self) -> u8 {
 +            use Something::*;
 +            match self {
 +                Num(n) => *n,
 +                TupleNums(n, _m) => *n,
 +                StructNums { one, two: _ } => *one,
 +            }
 +        }
 +    }
 +
 +    impl Foo {
 +        fn get_value(&self) -> u8 {
 +            let Foo(x) = self;
 +            *x
 +        }
 +
 +        fn use_crate(&self) -> u8 {
 +            let crate::issue8845::Foo(x) = self;
 +            *x
 +        }
 +    }
 +
 +    impl Bar {
 +        fn get_value(&self) -> u8 {
 +            let Bar { x, .. } = self;
 +            *x
 +        }
 +
 +        fn use_crate(&self) -> u8 {
 +            let crate::issue8845::Bar { x, .. } = self;
 +            *x
 +        }
 +    }
 +}
 +
 +mod issue6902 {
 +    use serde::Serialize;
 +
 +    #[derive(Serialize)]
 +    pub enum Foo {
 +        Bar = 1,
 +    }
 +}
++
++fn msrv_1_36() {
++    #![clippy::msrv = "1.36"]
++
++    enum E {
++        A,
++    }
++
++    impl E {
++        fn foo(self) {
++            match self {
++                E::A => {},
++            }
++        }
++    }
++}
++
++fn msrv_1_37() {
++    #![clippy::msrv = "1.37"]
++
++    enum E {
++        A,
++    }
++
++    impl E {
++        fn foo(self) {
++            match self {
++                E::A => {},
++            }
++        }
++    }
++}
index f06bb959b3bdefe38cd776bf016403187e09b1e4,0000000000000000000000000000000000000000..16fb0609242cbfea2724b8b548ef41f720898739
mode 100644,000000..100644
--- /dev/null
@@@ -1,250 -1,0 +1,256 @@@
-   --> $DIR/use_self.rs:22:21
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:23:13
++  --> $DIR/use_self.rs:23:21
 +   |
 +LL |         fn new() -> Foo {
 +   |                     ^^^ help: use the applicable keyword: `Self`
 +   |
 +   = note: `-D clippy::use-self` implied by `-D warnings`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:25:22
++  --> $DIR/use_self.rs:24:13
 +   |
 +LL |             Foo {}
 +   |             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:26:13
++  --> $DIR/use_self.rs:26:22
 +   |
 +LL |         fn test() -> Foo {
 +   |                      ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:31:25
++  --> $DIR/use_self.rs:27:13
 +   |
 +LL |             Foo::new()
 +   |             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:32:13
++  --> $DIR/use_self.rs:32:25
 +   |
 +LL |         fn default() -> Foo {
 +   |                         ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:97:24
++  --> $DIR/use_self.rs:33:13
 +   |
 +LL |             Foo::new()
 +   |             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:97:55
++  --> $DIR/use_self.rs:98:24
 +   |
 +LL |         fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
 +   |                        ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:112:13
++  --> $DIR/use_self.rs:98:55
 +   |
 +LL |         fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
 +   |                                                       ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:147:29
++  --> $DIR/use_self.rs:113:13
 +   |
 +LL |             TS(0)
 +   |             ^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:148:21
++  --> $DIR/use_self.rs:148:29
 +   |
 +LL |                 fn bar() -> Bar {
 +   |                             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:159:21
++  --> $DIR/use_self.rs:149:21
 +   |
 +LL |                     Bar { foo: Foo {} }
 +   |                     ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:160:13
++  --> $DIR/use_self.rs:160:21
 +   |
 +LL |         fn baz() -> Foo {
 +   |                     ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:177:21
++  --> $DIR/use_self.rs:161:13
 +   |
 +LL |             Foo {}
 +   |             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:178:21
++  --> $DIR/use_self.rs:178:21
 +   |
 +LL |             let _ = Enum::B(42);
 +   |                     ^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:179:21
++  --> $DIR/use_self.rs:179:21
 +   |
 +LL |             let _ = Enum::C { field: true };
 +   |                     ^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:221:13
++  --> $DIR/use_self.rs:180:21
 +   |
 +LL |             let _ = Enum::A;
 +   |                     ^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:222:13
++  --> $DIR/use_self.rs:222:13
 +   |
 +LL |             nested::A::fun_1();
 +   |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:224:13
++  --> $DIR/use_self.rs:223:13
 +   |
 +LL |             nested::A::A;
 +   |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:243:13
++  --> $DIR/use_self.rs:225:13
 +   |
 +LL |             nested::A {};
 +   |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:257:25
++  --> $DIR/use_self.rs:244:13
 +   |
 +LL |             TestStruct::from_something()
 +   |             ^^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:258:13
++  --> $DIR/use_self.rs:258:25
 +   |
 +LL |         async fn g() -> S {
 +   |                         ^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:262:16
++  --> $DIR/use_self.rs:259:13
 +   |
 +LL |             S {}
 +   |             ^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:262:22
++  --> $DIR/use_self.rs:263:16
 +   |
 +LL |             &p[S::A..S::B]
 +   |                ^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:285:29
++  --> $DIR/use_self.rs:263:22
 +   |
 +LL |             &p[S::A..S::B]
 +   |                      ^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:286:13
++  --> $DIR/use_self.rs:286:29
 +   |
 +LL |         fn foo(value: T) -> Foo<T> {
 +   |                             ^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:458:13
++  --> $DIR/use_self.rs:287:13
 +   |
 +LL |             Foo::<T> { value }
 +   |             ^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:495:13
++  --> $DIR/use_self.rs:459:13
 +   |
 +LL |             A::new::<submod::B>(submod::B {})
 +   |             ^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:532:17
++  --> $DIR/use_self.rs:496:13
 +   |
 +LL |             S2::new()
 +   |             ^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:533:17
++  --> $DIR/use_self.rs:533:17
 +   |
 +LL |                 Foo::Bar => unimplemented!(),
 +   |                 ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:539:20
++  --> $DIR/use_self.rs:534:17
 +   |
 +LL |                 Foo::Baz => unimplemented!(),
 +   |                 ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:563:17
++  --> $DIR/use_self.rs:540:20
 +   |
 +LL |             if let Foo::Bar = self {
 +   |                    ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:564:17
++  --> $DIR/use_self.rs:564:17
 +   |
 +LL |                 Something::Num(n) => *n,
 +   |                 ^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:565:17
++  --> $DIR/use_self.rs:565:17
 +   |
 +LL |                 Something::TupleNums(n, _m) => *n,
 +   |                 ^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:571:17
++  --> $DIR/use_self.rs:566:17
 +   |
 +LL |                 Something::StructNums { one, two: _ } => *one,
 +   |                 ^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:572:17
++  --> $DIR/use_self.rs:572:17
 +   |
 +LL |                 crate::issue8845::Something::Num(n) => *n,
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:573:17
++  --> $DIR/use_self.rs:573:17
 +   |
 +LL |                 crate::issue8845::Something::TupleNums(n, _m) => *n,
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:589:17
++  --> $DIR/use_self.rs:574:17
 +   |
 +LL |                 crate::issue8845::Something::StructNums { one, two: _ } => *one,
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:594:17
++  --> $DIR/use_self.rs:590:17
 +   |
 +LL |             let Foo(x) = self;
 +   |                 ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:601:17
++  --> $DIR/use_self.rs:595:17
 +   |
 +LL |             let crate::issue8845::Foo(x) = self;
 +   |                 ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:606:17
++  --> $DIR/use_self.rs:602:17
 +   |
 +LL |             let Bar { x, .. } = self;
 +   |                 ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
- error: aborting due to 41 previous errors
++  --> $DIR/use_self.rs:607:17
 +   |
 +LL |             let crate::issue8845::Bar { x, .. } = self;
 +   |                 ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
 +
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:648:17
++   |
++LL |                 E::A => {},
++   |                 ^ help: use the applicable keyword: `Self`
++
++error: aborting due to 42 previous errors
 +
index 9e07769a8e4fd8883d4bb98021397dfd76cfea4b,0000000000000000000000000000000000000000..a6d8d0307ce536900c51a6986b034afc2690fa74
mode 100644,000000..100644
--- /dev/null
@@@ -1,89 -1,0 +1,89 @@@
-         std::process::Command::new(&rustc)
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +#![allow(clippy::single_match_else)]
 +
 +use rustc_tools_util::VersionInfo;
 +use std::fs;
 +
 +#[test]
 +fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() {
 +    fn read_version(path: &str) -> String {
 +        let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("error reading `{path}`: {e:?}"));
 +        contents
 +            .lines()
 +            .filter_map(|l| l.split_once('='))
 +            .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))
 +            .unwrap_or_else(|| panic!("error finding version in `{path}`"))
 +            .to_string()
 +    }
 +
 +    // do not run this test inside the upstream rustc repo:
 +    // https://github.com/rust-lang/rust-clippy/issues/6683
 +    if option_env!("RUSTC_TEST_SUITE").is_some() {
 +        return;
 +    }
 +
 +    let clippy_version = read_version("Cargo.toml");
 +    let clippy_lints_version = read_version("clippy_lints/Cargo.toml");
 +    let clippy_utils_version = read_version("clippy_utils/Cargo.toml");
 +
 +    assert_eq!(clippy_version, clippy_lints_version);
 +    assert_eq!(clippy_version, clippy_utils_version);
 +}
 +
 +#[test]
 +fn check_that_clippy_has_the_same_major_version_as_rustc() {
 +    // do not run this test inside the upstream rustc repo:
 +    // https://github.com/rust-lang/rust-clippy/issues/6683
 +    if option_env!("RUSTC_TEST_SUITE").is_some() {
 +        return;
 +    }
 +
 +    let clippy_version = rustc_tools_util::get_version_info!();
 +    let clippy_major = clippy_version.major;
 +    let clippy_minor = clippy_version.minor;
 +    let clippy_patch = clippy_version.patch;
 +
 +    // get the rustc version either from the rustc installed with the toolchain file or from
 +    // `RUSTC_REAL` if Clippy is build in the Rust repo with `./x.py`.
 +    let rustc = std::env::var("RUSTC_REAL").unwrap_or_else(|_| "rustc".to_string());
 +    let rustc_version = String::from_utf8(
++        std::process::Command::new(rustc)
 +            .arg("--version")
 +            .output()
 +            .expect("failed to run `rustc --version`")
 +            .stdout,
 +    )
 +    .unwrap();
 +    // extract "1 XX 0" from "rustc 1.XX.0-nightly (<commit> <date>)"
 +    let vsplit: Vec<&str> = rustc_version
 +        .split(' ')
 +        .nth(1)
 +        .unwrap()
 +        .split('-')
 +        .next()
 +        .unwrap()
 +        .split('.')
 +        .collect();
 +    match vsplit.as_slice() {
 +        [rustc_major, rustc_minor, _rustc_patch] => {
 +            // clippy 0.1.XX should correspond to rustc 1.XX.0
 +            assert_eq!(clippy_major, 0); // this will probably stay the same for a long time
 +            assert_eq!(
 +                clippy_minor.to_string(),
 +                *rustc_major,
 +                "clippy minor version does not equal rustc major version"
 +            );
 +            assert_eq!(
 +                clippy_patch.to_string(),
 +                *rustc_minor,
 +                "clippy patch version does not equal rustc minor version"
 +            );
 +            // do not check rustc_patch because when a stable-patch-release is made (like 1.50.2),
 +            // we don't want our tests failing suddenly
 +        },
 +        _ => {
 +            panic!("Failed to parse rustc version: {vsplit:?}");
 +        },
 +    };
 +}
index c5d602ea3035f7e0a93a79df3bb715084681141c,0000000000000000000000000000000000000000..e46ad2c6e0eec7bdf576d4cc05dc630244a6e0a8
mode 100644,000000..100644
--- /dev/null
@@@ -1,570 -1,0 +1,576 @@@
 +<!DOCTYPE html>
 +<!--
 +Welcome to a Clippy's lint list, at least the source code of it. If you are
 +interested in contributing to this website checkout `util/gh-pages/index.html`
 +inside the rust-clippy repository.
 +
 +Otherwise, have a great day =^.^=
 +-->
 +<html lang="en">
 +<head>
 +    <meta charset="UTF-8"/>
 +    <meta name="viewport" content="width=device-width, initial-scale=1"/>
 +    <meta name="description" content="A collection of lints to catch common mistakes and improve your Rust code.">
 +
 +    <title>Clippy Lints</title>
 +
 +    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css"/>
 +    <link id="githubLightHighlight" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/github.min.css" disabled="true" />
 +    <link id="githubDarkHighlight" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/github-dark.min.css" disabled="true" />
 +
 +    <!-- The files are not copied over into the Clippy project since they use the MPL-2.0 License -->
 +    <link rel="stylesheet" href="https://rust-lang.github.io/mdBook/css/variables.css"/>
 +    <link id="styleHighlight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/highlight.css">
 +    <link id="styleNight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/tomorrow-night.css" disabled="true">
 +    <link id="styleAyu" rel="stylesheet" href="https://rust-lang.github.io/mdBook/ayu-highlight.css" disabled="true">
 +    <style>
 +        blockquote { font-size: 1em; }
 +        [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; }
 +
 +        .dropdown-menu {
 +            color: var(--fg);
 +            background: var(--theme-popup-bg);
 +            border: 1px solid var(--theme-popup-border);
 +        }
 +
 +        .dropdown-menu .divider {
 +            background-color: var(--theme-popup-border);
 +        }
 +
 +        .dropdown-menu .checkbox {
 +            display: block;
 +            white-space: nowrap;
 +            margin: 0;
 +        }
 +        .dropdown-menu .checkbox label {
 +            padding: 3px 20px;
 +            width: 100%;
 +        }
 +
 +        .dropdown-menu .checkbox input {
 +            position: relative;
 +            margin: 0 0.5rem 0;
 +            padding: 0;
 +        }
 +
 +        .dropdown-menu .checkbox:hover {
 +            background-color: var(--theme-hover);
 +        }
 +
 +        div.panel div.panel-body button.dropdown-toggle {
 +            background: var(--searchbar-bg);
 +            color: var(--searchbar-fg);
 +            border-color: var(--theme-popup-border);
 +        }
 +
 +        div.panel div.panel-body button.dropdown-toggle:hover {
 +            box-shadow: 0 0 3px var(--searchbar-shadow-color);
 +        }
 +
 +        div.panel div.panel-body .open button.dropdown-toggle {
 +            background: var(--searchbar-bg);
 +            color: var(--searchbar-fg);
 +            border-color: var(--theme-popup-border);
 +            filter: brightness(90%);
 +        }
 +
 +        .dropdown-toggle .badge {
 +            background-color: #777;
 +        }
 +
 +        .panel-heading { cursor: pointer; }
 +
 +        .panel-title { display: flex; flex-wrap: wrap;}
 +        .panel-title .label { display: inline-block; }
 +
 +        .panel-title-name { flex: 1; min-width: 400px;}
 +        .panel-title-name span { vertical-align: bottom; }
 +
 +        .panel .panel-title-name .anchor { display: none; }
 +        .panel:hover .panel-title-name .anchor { display: inline;}
 +
 +        .search-control {
 +            margin-top: 15px;
 +        }
 +
 +        @media (min-width: 992px) {
 +            .search-control {
 +                margin-top: 0;
 +                float: right;
 +            }
 +        }
 +
 +        @media (min-width: 405px) {
 +            #upper-filters {
 +                display: flex;
 +            }
 +        }
 +
 +        @media (max-width: 430px) {
 +            /* Turn the version filter list to the left */
 +            #version-filter-selector {
 +                right: 0;
 +                left: auto;
 +            }
 +        }
 +
 +        @media (max-width: 412px) {
 +            #upper-filters,
 +            .panel-body .search-control  {
 +                padding-right: 8px;
 +                padding-left: 8px;
 +            }
 +        }
 +
 +        .label {
 +            padding-top: 0.3em;
 +            padding-bottom: 0.3em;
 +        }
 +
 +        .label-lint-group {
 +            min-width: 8em;
 +        }
 +        .label-lint-level {
 +            min-width: 4em;
 +        }
 +
 +        .label-lint-level-allow {
 +            background-color: #5cb85c;
 +        }
 +        .label-lint-level-warn {
 +            background-color: #f0ad4e;
 +        }
 +        .label-lint-level-deny {
 +            background-color: #d9534f;
 +        }
 +        .label-lint-level-none {
 +            background-color: #777777;
 +            opacity: 0.5;
 +        }
 +
 +        .label-group-deprecated {
 +            opacity: 0.5;
 +        }
 +
 +        .label-doc-folding {
 +            color: #000;
 +            background-color: #fff;
 +            border: 1px solid var(--theme-popup-border);
 +        }
 +        .label-doc-folding:hover {
 +            background-color: #e6e6e6;
 +        }
 +
 +        .lint-doc-md > h3 {
 +            border-top: 1px solid var(--theme-popup-border);
 +            padding: 10px 15px;
 +            margin: 0 -15px;
 +            font-size: 18px;
 +        }
 +        .lint-doc-md > h3:first-child {
 +            border-top: none;
 +            padding-top: 0px;
 +        }
 +
 +        @media (max-width:749px) {
 +            .lint-additional-info-container {
 +                display: flex;
 +                flex-flow: column;
 +            }
 +            .lint-additional-info-item + .lint-additional-info-item {
 +                border-top: 1px solid var(--theme-popup-border);
 +            }
 +        }
 +        @media (min-width:750px) {
 +            .lint-additional-info-container {
 +                display: flex;
 +                flex-flow: row;
 +            }
 +            .lint-additional-info-item + .lint-additional-info-item {
 +                border-left: 1px solid var(--theme-popup-border);
 +            }
 +        }
 +
 +        .lint-additional-info-item {
 +            display: inline-flex;
 +            min-width: 200px;
 +            flex-grow: 1;
 +            padding: 9px 5px 5px 15px;
 +        }
 +
 +        .label-applicability {
 +            background-color: #777777;
 +            margin: auto 5px;
 +        }
 +
 +        .label-version {
 +            background-color: #777777;
 +            margin: auto 5px;
 +            font-family: monospace;
 +        }
 +
 +        details {
 +            border-radius: 4px;
 +            padding: .5em .5em 0;
 +        }
 +
 +        code {
 +            white-space: pre !important;
 +        }
 +
 +        summary {
 +            font-weight: bold;
 +            margin: -.5em -.5em 0;
 +            padding: .5em;
 +            display: revert;
 +        }
 +
 +        details[open] {
 +            padding: .5em;
 +        }
 +    </style>
 +    <style>
 +        /* Expanding the mdBoom theme*/
 +        .light {
 +            --inline-code-bg: #f6f7f6;
 +        }
 +        .rust {
 +            --inline-code-bg: #f6f7f6;
 +        }
 +        .coal {
 +            --inline-code-bg: #1d1f21;
 +        }
 +        .navy {
 +            --inline-code-bg: #1d1f21;
 +        }
 +        .ayu {
 +            --inline-code-bg: #191f26;
 +        }
 +
 +        .theme-dropdown {
 +            position: absolute;
 +            margin: 0.7em;
 +            z-index: 10;
 +        }
 +
 +        /* Applying the mdBook theme */
 +        .theme-icon {
 +            text-align: center;
 +            width: 2em;
 +            height: 2em;
 +            line-height: 2em;
 +            border: solid 1px var(--icons);
 +            border-radius: 5px;
 +            user-select: none;
 +            cursor: pointer;
 +        }
 +        .theme-icon:hover {
 +            background: var(--theme-hover);
 +        }
 +        .theme-choice {
 +            display: none;
 +            list-style: none;
 +            border: 1px solid var(--theme-popup-border);
 +            border-radius: 5px;
 +            color: var(--fg);
 +            background: var(--theme-popup-bg);
 +            padding: 0 0;
 +            overflow: hidden;
 +        }
 +
 +        .theme-dropdown.open .theme-choice {
 +            display: block;
 +        }
 +
 +        .theme-choice > li {
 +            padding: 5px 10px;
 +            font-size: 0.8em;
 +            user-select: none;
 +            cursor: pointer;
 +        }
 +
 +        .theme-choice > li:hover {
 +            background: var(--theme-hover);
 +        }
 +
 +        .alert {
 +            color: var(--fg);
 +            background: var(--theme-hover);
 +            border: 1px solid var(--theme-popup-border);
 +        }
 +        .page-header {
 +            border-color: var(--theme-popup-border);
 +        }
 +        .panel-default > .panel-heading {
 +            background: var(--theme-hover);
 +            color: var(--fg);
 +            border: 1px solid var(--theme-popup-border);
 +        }
 +        .panel-default > .panel-heading:hover {
 +            filter: brightness(90%);
 +        }
 +        .list-group-item {
 +            background: 0%;
 +            border: 1px solid var(--theme-popup-border);
 +        }
 +        .panel, pre, hr {
 +            background: var(--bg);
 +            border: 1px solid var(--theme-popup-border);
 +        }
 +
 +        #version-filter-selector .checkbox {
 +            display: flex;
 +        }
 +
 +        #version-filter {
 +            min-width: available;
 +        }
 +
 +        #version-filter li label {
 +            padding-right: 0;
 +            width: 35%;
 +        }
 +
 +        .version-filter-input {
 +            height: 60%;
 +            width: 30%;
 +            text-align: center;
 +            border: none;
 +            border-bottom: 1px solid #000000;
 +        }
 +
 +        #filter-label, .filter-clear {
 +            background: var(--searchbar-bg);
 +            color: var(--searchbar-fg);
 +            border-color: var(--theme-popup-border);
 +            filter: brightness(95%);
 +        }
 +        #filter-label:hover, .filter-clear:hover {
 +            filter: brightness(90%);
 +        }
 +        .filter-input {
 +            background: var(--searchbar-bg);
 +            color: var(--searchbar-fg);
 +            border-color: var(--theme-popup-border);
 +        }
 +
 +        .filter-input::-webkit-input-placeholder,
 +        .filter-input::-moz-placeholder {
 +            color: var(--searchbar-fg);
 +            opacity: 30%;
 +        }
 +
 +        :not(pre) > code {
 +            color: var(--inline-code-color);
 +            background-color: var(--inline-code-bg);
 +        }
 +        html {
 +            scrollbar-color: var(--scrollbar) var(--bg);
 +        }
 +        body {
 +            background: var(--bg);
 +            color: var(--fg);
 +        }
 +
 +    </style>
 +</head>
 +<body ng-app="clippy" ng-controller="lintList">
 +    <div theme-dropdown class="theme-dropdown">
 +        <div id="theme-icon" class="theme-icon">&#128396;</div>
 +        <ul id="theme-menu" class="theme-choice">
 +            <li id="{{id}}" ng-repeat="(id, name) in themes" ng-click="selectTheme(id)">{{name}}</li>
 +        </ul>
 +    </div>
 +
 +    <div class="container">
 +        <div class="page-header">
 +            <h1>Clippy Lints</h1>
 +        </div>
 +
 +        <noscript>
 +            <div class="alert alert-danger" role="alert">
 +                Sorry, this site only works with JavaScript! :(
 +            </div>
 +        </noscript>
 +
 +        <div ng-cloak>
 +
 +            <div class="alert alert-info" role="alert" ng-if="loading">
 +                Loading&#x2026;
 +            </div>
 +            <div class="alert alert-danger" role="alert" ng-if="error">
 +                Error loading lints!
 +            </div>
 +
 +            <div class="panel panel-default" ng-show="data">
 +                <div class="panel-body row">
 +                    <div id="upper-filters" class="col-12 col-md-4">
 +                        <div class="btn-group" filter-dropdown>
 +                            <button type="button" class="btn btn-default dropdown-toggle">
 +                                Lint levels <span class="badge">{{selectedValuesCount(levels)}}</span> <span class="caret"></span>
 +                            </button>
 +                            <ul class="dropdown-menu">
 +                                <li class="checkbox">
 +                                    <label ng-click="toggleLevels(true)">
 +                                        <input type="checkbox" class="invisible" />
 +                                        All
 +                                    </label>
 +                                </li>
 +                                <li class="checkbox">
 +                                    <label ng-click="toggleLevels(false)">
 +                                        <input type="checkbox" class="invisible" />
 +                                        None
 +                                    </label>
 +                                </li>
 +                                <li role="separator" class="divider"></li>
 +                                <li class="checkbox" ng-repeat="(level, enabled) in levels">
 +                                    <label class="text-capitalize">
 +                                        <input type="checkbox" ng-model="levels[level]" />
 +                                        {{level}}
 +                                    </label>
 +                                </li>
 +                            </ul>
 +                        </div>
 +                        <div class="btn-group" filter-dropdown>
 +                            <button type="button" class="btn btn-default dropdown-toggle">
 +                                Lint groups <span class="badge">{{selectedValuesCount(groups)}}</span> <span class="caret"></span>
 +                            </button>
 +                            <ul class="dropdown-menu">
 +                                <li class="checkbox">
 +                                    <label ng-click="toggleGroups(true)">
 +                                        <input type="checkbox" class="invisible" />
 +                                        All
 +                                    </label>
 +                                </li>
++                                <li class="checkbox">
++                                    <label ng-click="resetGroupsToDefault()">
++                                        <input type="checkbox" class="invisible" />
++                                        Default
++                                    </label>
++                                </li>
 +                                <li class="checkbox">
 +                                    <label ng-click="toggleGroups(false)">
 +                                        <input type="checkbox" class="invisible" />
 +                                        None
 +                                    </label>
 +                                </li>
 +                                <li role="separator" class="divider"></li>
 +                                <li class="checkbox" ng-repeat="(group, enabled) in groups">
 +                                    <label class="text-capitalize">
 +                                        <input type="checkbox" ng-model="groups[group]" />
 +                                        {{group}}
 +                                    </label>
 +                                </li>
 +                            </ul>
 +                        </div>
 +                        <div id="version-filter">
 +                            <div class="btn-group" filter-dropdown>
 +                                <button type="button" class="btn btn-default dropdown-toggle">
 +                                    Version
 +                                    <span id="version-filter-count" class="badge">
 +                                        {{versionFilterCount(versionFilters)}}
 +                                    </span>
 +                                    <span class="caret"></span>
 +                                </button>
 +                                <ul id="version-filter-selector" class="dropdown-menu">
 +                                    <li class="checkbox">
 +                                        <label ng-click="clearVersionFilters()">
 +                                            <input type="checkbox" class="invisible" />
 +                                            Clear filters
 +                                        </label>
 +                                    </li>
 +                                    <li role="separator" class="divider"></li>
 +                                    <li class="checkbox" ng-repeat="(filter, vars) in versionFilters">
 +                                        <label ng-attr-for="filter-{filter}">{{filter}}</label>
 +                                        <span>1.</span>
 +                                        <input type="number"
 +                                                min="29"
 +                                                ng-attr-id="filter-{filter}"
 +                                                class="version-filter-input form-control filter-input"
 +                                                maxlength="2"
 +                                                ng-model="versionFilters[filter].minorVersion"
 +                                                ng-model-options="{debounce: 50}"
 +                                                ng-change="updateVersionFilters()" />
 +                                        <span>.0</span>
 +                                    </li>
 +                                </ul>
 +                            </div>
 +                        </div>
 +
 +                    </div>
 +                    <div class="col-12 col-md-7 search-control">
 +                        <div class="input-group">
 +                            <label class="input-group-addon" id="filter-label" for="search-input">Filter:</label>
 +                            <input type="text" class="form-control filter-input" placeholder="Keywords or search string" id="search-input" ng-model="search" ng-model-options="{debounce: 50}"/>
 +                            <span class="input-group-btn">
 +                                <button class="filter-clear btn" type="button" ng-click="search = ''">
 +                                    Clear
 +                                </button>
 +                            </span>
 +                        </div>
 +                    </div>
 +                </div>
 +            </div>
 +            <!-- The order of the filters should be from most likely to remove a lint to least likely to improve performance. -->
 +            <article class="panel panel-default" id="{{lint.id}}" ng-repeat="lint in data | filter:bySearch | filter:byGroups | filter:byLevels | filter:byVersion">
 +                <header class="panel-heading" ng-click="open[lint.id] = !open[lint.id]">
 +                    <h2 class="panel-title">
 +                        <div class="panel-title-name">
 +                            <span>{{lint.id}}</span>
 +                            <a href="#{{lint.id}}" class="anchor label label-default" ng-click="open[lint.id] = true; $event.stopPropagation()">&para;</a>
 +                            <a href="" id="clipboard-{{lint.id}}" class="anchor label label-default" ng-click="copyToClipboard(lint); $event.stopPropagation()">
 +                                &#128203;
 +                            </a>
 +                        </div>
 +
 +                        <div class="panel-title-addons">
 +                            <span class="label label-lint-group label-default label-group-{{lint.group}}">{{lint.group}}</span>
 +
 +                            <span class="label label-lint-level label-lint-level-{{lint.level}}">{{lint.level}}</span>
 +
 +
 +                            <span class="label label-doc-folding" ng-show="open[lint.id]">&minus;</span>
 +                            <span class="label label-doc-folding" ng-hide="open[lint.id]">&plus;</span>
 +                        </div>
 +                    </h2>
 +                </header>
 +
 +                <div class="list-group lint-docs" ng-if="open[lint.id]" ng-class="{collapse: true, in: open[lint.id]}">
 +                    <div class="list-group-item lint-doc-md" ng-bind-html="lint.docs | markdown"></div>
 +                    <div class="lint-additional-info-container">
 +                        <!-- Applicability -->
 +                        <div class="lint-additional-info-item">
 +                            <span> Applicability: </span>
 +                            <span class="label label-default label-applicability">{{lint.applicability.applicability}}</span>
 +                            <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/enum.Applicability.html#variants">(?)</a>
 +                        </div>
 +                        <!-- Clippy version -->
 +                        <div class="lint-additional-info-item">
 +                            <span>{{lint.group == "deprecated" ? "Deprecated" : "Added"}} in: </span>
 +                            <span class="label label-default label-version">{{lint.version}}</span>
 +                        </div>
 +                        <!-- Open related issues -->
 +                        <div class="lint-additional-info-item">
 +                            <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a>
 +                        </div>
 +                        <!-- Jump to source -->
 +                        <div class="lint-additional-info-item">
 +                            <a href="https://github.com/rust-lang/rust-clippy/blob/{{docVersion}}/clippy_lints/{{lint.id_span.path}}#L{{lint.id_span.line}}">View Source</a>
 +                        </div>
 +                    </div>
 +                </div>
 +            </article>
 +        </div>
 +    </div>
 +
 +    <a href="https://github.com/rust-lang/rust-clippy">
 +        <img style="position: absolute; top: 0; right: 0; border: 0; clip-path: polygon(0% 0%, 100% 0%, 100% 100%);" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on Github"/>
 +    </a>
 +
 +    <script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/12.3.2/markdown-it.min.js"></script>
 +    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>
 +    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/languages/rust.min.js"></script>
 +    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.12/angular.min.js"></script>
 +    <script src="script.js"></script>
 +</body>
 +</html>
index 366e7c8843f229507b1c3f3a4949d2939bd651a9,0000000000000000000000000000000000000000..1c16ecd6b0b1f13bb23cfa04696f93d0a1e87416
mode 100644,000000..100644
--- /dev/null
@@@ -1,389 -1,0 +1,400 @@@
-             var GROUPS_FILTER_DEFAULT = {
 +(function () {
 +    var md = window.markdownit({
 +        html: true,
 +        linkify: true,
 +        typographer: true,
 +        highlight: function (str, lang) {
 +            if (lang && hljs.getLanguage(lang)) {
 +                try {
 +                    return '<pre class="hljs"><code>' +
 +                        hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
 +                        '</code></pre>';
 +                } catch (__) {}
 +            }
 +
 +            return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>';
 +        }
 +    });
 +
 +    function scrollToLint(lintId) {
 +        var target = document.getElementById(lintId);
 +        if (!target) {
 +            return;
 +        }
 +        target.scrollIntoView();
 +    }
 +
 +    function scrollToLintByURL($scope) {
 +        var removeListener = $scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) {
 +            scrollToLint(window.location.hash.slice(1));
 +            removeListener();
 +        });
 +    }
 +
 +    function selectGroup($scope, selectedGroup) {
 +        var groups = $scope.groups;
 +        for (var group in groups) {
 +            if (groups.hasOwnProperty(group)) {
 +                if (group === selectedGroup) {
 +                    groups[group] = true;
 +                } else {
 +                    groups[group] = false;
 +                }
 +            }
 +        }
 +    }
 +
 +    angular.module("clippy", [])
 +        .filter('markdown', function ($sce) {
 +            return function (text) {
 +                return $sce.trustAsHtml(
 +                    md.render(text || '')
 +                        // Oh deer, what a hack :O
 +                        .replace('<table', '<table class="table"')
 +                );
 +            };
 +        })
 +        .directive('themeDropdown', function ($document) {
 +            return {
 +                restrict: 'A',
 +                link: function ($scope, $element, $attr) {
 +                    $element.bind('click', function () {
 +                        $element.toggleClass('open');
 +                        $element.addClass('open-recent');
 +                    });
 +
 +                    $document.bind('click', function () {
 +                        if (!$element.hasClass('open-recent')) {
 +                            $element.removeClass('open');
 +                        }
 +                        $element.removeClass('open-recent');
 +                    })
 +                }
 +            }
 +        })
 +        .directive('filterDropdown', function ($document) {
 +            return {
 +                restrict: 'A',
 +                link: function ($scope, $element, $attr) {
 +                    $element.bind('click', function (event) {
 +                        if (event.target.closest('button')) {
 +                            $element.toggleClass('open');
 +                        } else {
 +                            $element.addClass('open');
 +                        }
 +                        $element.addClass('open-recent');
 +                    });
 +
 +                    $document.bind('click', function () {
 +                        if (!$element.hasClass('open-recent')) {
 +                            $element.removeClass('open');
 +                        }
 +                        $element.removeClass('open-recent');
 +                    })
 +                }
 +            }
 +        })
 +        .directive('onFinishRender', function ($timeout) {
 +            return {
 +                restrict: 'A',
 +                link: function (scope, element, attr) {
 +                    if (scope.$last === true) {
 +                        $timeout(function () {
 +                            scope.$emit(attr.onFinishRender);
 +                        });
 +                    }
 +                }
 +            };
 +        })
 +        .controller("lintList", function ($scope, $http, $timeout) {
 +            // Level filter
 +            var LEVEL_FILTERS_DEFAULT = {allow: true, warn: true, deny: true, none: true};
 +            $scope.levels = LEVEL_FILTERS_DEFAULT;
 +            $scope.byLevels = function (lint) {
 +                return $scope.levels[lint.level];
 +            };
 +
-             $scope.groups = GROUPS_FILTER_DEFAULT;
++            const GROUPS_FILTER_DEFAULT = {
 +                cargo: true,
 +                complexity: true,
 +                correctness: true,
 +                deprecated: false,
 +                nursery: true,
 +                pedantic: true,
 +                perf: true,
 +                restriction: true,
 +                style: true,
 +                suspicious: true,
++            }
++
++            $scope.groups = {
++                ...GROUPS_FILTER_DEFAULT
 +            };
++
 +            const THEMES_DEFAULT = {
 +                light: "Light",
 +                rust: "Rust",
 +                coal: "Coal",
 +                navy: "Navy",
 +                ayu: "Ayu"
 +            };
 +            $scope.themes = THEMES_DEFAULT;
 +
 +            $scope.versionFilters = {
 +                "≥": {enabled: false, minorVersion: null },
 +                "≤": {enabled: false, minorVersion: null },
 +                "=": {enabled: false, minorVersion: null },
 +            };
 +
 +            $scope.selectTheme = function (theme) {
 +                setTheme(theme, true);
 +            }
 +
 +            $scope.toggleLevels = function (value) {
 +                const levels = $scope.levels;
 +                for (const key in levels) {
 +                    if (levels.hasOwnProperty(key)) {
 +                        levels[key] = value;
 +                    }
 +                }
 +            };
 +
 +            $scope.toggleGroups = function (value) {
 +                const groups = $scope.groups;
 +                for (const key in groups) {
 +                    if (groups.hasOwnProperty(key)) {
 +                        groups[key] = value;
 +                    }
 +                }
 +            };
++
++            $scope.resetGroupsToDefault = function () {
++                const groups = $scope.groups;
++                for (const [key, value] of Object.entries(GROUPS_FILTER_DEFAULT)) {
++                    groups[key] = value;
++                }
++            };
 +
 +            $scope.selectedValuesCount = function (obj) {
 +                return Object.values(obj).filter(x => x).length;
 +            }
 +
 +            $scope.clearVersionFilters = function () {
 +                for (let filter in $scope.versionFilters) {
 +                    $scope.versionFilters[filter] = { enabled: false, minorVersion: null };
 +                }
 +            }
 +
 +            $scope.versionFilterCount = function(obj) {
 +                return Object.values(obj).filter(x => x.enabled).length;
 +            }
 +
 +            $scope.updateVersionFilters = function() {
 +                for (const filter in $scope.versionFilters) {
 +                    let minorVersion = $scope.versionFilters[filter].minorVersion;
 +
 +                    // 1.29.0 and greater
 +                    if (minorVersion && minorVersion > 28) {
 +                        $scope.versionFilters[filter].enabled = true;
 +                        continue;
 +                    }
 +
 +                    $scope.versionFilters[filter].enabled = false;
 +                }
 +            }
 +
 +            $scope.byVersion = function(lint) {
 +                let filters = $scope.versionFilters;
 +                for (const filter in filters) {
 +                    if (filters[filter].enabled) {
 +                        let minorVersion = filters[filter].minorVersion;
 +
 +                        // Strip the "pre " prefix for pre 1.29.0 lints
 +                        let lintVersion = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version;
 +                        let lintMinorVersion = lintVersion.substring(2, 4);
 +
 +                        switch (filter) {
 +                            // "=" gets the highest priority, since all filters are inclusive
 +                            case "=":
 +                                return (lintMinorVersion == minorVersion);
 +                            case "≥":
 +                                if (lintMinorVersion < minorVersion) { return false; }
 +                                break;
 +                            case "≤":
 +                                if (lintMinorVersion > minorVersion) { return false; }
 +                                break;
 +                            default:
 +                                return true
 +                        }
 +                    }
 +                }
 +
 +                return true;
 +            }
 +
 +            $scope.byGroups = function (lint) {
 +                return $scope.groups[lint.group];
 +            };
 +
 +            $scope.bySearch = function (lint, index, array) {
 +                let searchStr = $scope.search;
 +                // It can be `null` I haven't missed this value
 +                if (searchStr == null || searchStr.length < 3) {
 +                    return true;
 +                }
 +                searchStr = searchStr.toLowerCase();
 +                if (searchStr.startsWith("clippy::")) {
 +                    searchStr = searchStr.slice(8);
 +                }
 +
 +                // Search by id
 +                if (lint.id.indexOf(searchStr.replace("-", "_")) !== -1) {
 +                    return true;
 +                }
 +
 +                // Search the description
 +                // The use of `for`-loops instead of `foreach` enables us to return early
 +                let terms = searchStr.split(" ");
 +                let docsLowerCase = lint.docs.toLowerCase();
 +                for (index = 0; index < terms.length; index++) {
 +                    // This is more likely and will therefor be checked first
 +                    if (docsLowerCase.indexOf(terms[index]) !== -1) {
 +                        continue;
 +                    }
 +
 +                    if (lint.id.indexOf(terms[index]) !== -1) {
 +                        continue;
 +                    }
 +
 +                    return false;
 +                }
 +
 +                return true;
 +            }
 +
 +            $scope.copyToClipboard = function (lint) {
 +                const clipboard = document.getElementById("clipboard-" + lint.id);
 +                if (clipboard) {
 +                    let resetClipboardTimeout = null;
 +                    let resetClipboardIcon = clipboard.innerHTML;
 +
 +                    function resetClipboard() {
 +                        resetClipboardTimeout = null;
 +                        clipboard.innerHTML = resetClipboardIcon;
 +                    }
 +
 +                    navigator.clipboard.writeText("clippy::" + lint.id);
 +
 +                    clipboard.innerHTML = "&#10003;";
 +                    if (resetClipboardTimeout !== null) {
 +                        clearTimeout(resetClipboardTimeout);
 +                    }
 +                    resetClipboardTimeout = setTimeout(resetClipboard, 1000);
 +                }
 +            }
 +
 +            // Get data
 +            $scope.open = {};
 +            $scope.loading = true;
 +            // This will be used to jump into the source code of the version that this documentation is for.
 +            $scope.docVersion = window.location.pathname.split('/')[2] || "master";
 +
 +            if (window.location.hash.length > 1) {
 +                $scope.search = window.location.hash.slice(1);
 +                $scope.open[window.location.hash.slice(1)] = true;
 +                scrollToLintByURL($scope);
 +            }
 +
 +            $http.get('./lints.json')
 +                .success(function (data) {
 +                    $scope.data = data;
 +                    $scope.loading = false;
 +
 +                    var selectedGroup = getQueryVariable("sel");
 +                    if (selectedGroup) {
 +                        selectGroup($scope, selectedGroup.toLowerCase());
 +                    }
 +
 +                    scrollToLintByURL($scope);
 +
 +                    setTimeout(function () {
 +                        var el = document.getElementById('filter-input');
 +                        if (el) { el.focus() }
 +                    }, 0);
 +                })
 +                .error(function (data) {
 +                    $scope.error = data;
 +                    $scope.loading = false;
 +                });
 +
 +            window.addEventListener('hashchange', function () {
 +                // trigger re-render
 +                $timeout(function () {
 +                    $scope.levels = LEVEL_FILTERS_DEFAULT;
 +                    $scope.search = window.location.hash.slice(1);
 +                    $scope.open[window.location.hash.slice(1)] = true;
 +
 +                    scrollToLintByURL($scope);
 +                });
 +                return true;
 +            }, false);
 +        });
 +})();
 +
 +function getQueryVariable(variable) {
 +    var query = window.location.search.substring(1);
 +    var vars = query.split('&');
 +    for (var i = 0; i < vars.length; i++) {
 +        var pair = vars[i].split('=');
 +        if (decodeURIComponent(pair[0]) == variable) {
 +            return decodeURIComponent(pair[1]);
 +        }
 +    }
 +}
 +
 +function setTheme(theme, store) {
 +    let enableHighlight = false;
 +    let enableNight = false;
 +    let enableAyu = false;
 +
 +    switch(theme) {
 +        case "ayu":
 +            enableAyu = true;
 +            break;
 +        case "coal":
 +        case "navy":
 +            enableNight = true;
 +            break;
 +        case "rust":
 +            enableHighlight = true;
 +            break;
 +        default:
 +            enableHighlight = true;
 +            theme = "light";
 +            break;
 +    }
 +
 +    document.getElementsByTagName("body")[0].className = theme;
 +
 +    document.getElementById("githubLightHighlight").disabled = enableNight || !enableHighlight;
 +    document.getElementById("githubDarkHighlight").disabled = !enableNight && !enableAyu;
 +
 +    document.getElementById("styleHighlight").disabled = !enableHighlight;
 +    document.getElementById("styleNight").disabled = !enableNight;
 +    document.getElementById("styleAyu").disabled = !enableAyu;
 +
 +    if (store) {
 +        try {
 +            localStorage.setItem('clippy-lint-list-theme', theme);
 +        } catch (e) { }
 +    }
 +}
 +
 +// loading the theme after the initial load
 +const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
 +const theme = localStorage.getItem('clippy-lint-list-theme');
 +if (prefersDark.matches && !theme) {
 +    setTheme("coal", false);
 +} else {
 +    setTheme(theme, false);
 +}