]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit 'fdb84cbfd25908df5683f8f62388f663d9260e39' into clippyup
authorPhilipp Krones <hello@philkrones.com>
Mon, 18 Jul 2022 07:39:37 +0000 (09:39 +0200)
committerPhilipp Krones <hello@philkrones.com>
Mon, 18 Jul 2022 07:39:37 +0000 (09:39 +0200)
165 files changed:
1  2 
src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md
src/tools/clippy/.github/workflows/clippy_bors.yml
src/tools/clippy/CHANGELOG.md
src/tools/clippy/CONTRIBUTING.md
src/tools/clippy/book/README.md
src/tools/clippy/book/src/development/basics.md
src/tools/clippy/book/src/development/infrastructure/changelog_update.md
src/tools/clippy/book/src/usage.md
src/tools/clippy/clippy_dev/src/dogfood.rs
src/tools/clippy/clippy_dev/src/lib.rs
src/tools/clippy/clippy_dev/src/main.rs
src/tools/clippy/clippy_dev/src/update_lints.rs
src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
src/tools/clippy/clippy_lints/src/casts/mod.rs
src/tools/clippy/clippy_lints/src/copies.rs
src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs
src/tools/clippy/clippy_lints/src/dereference.rs
src/tools/clippy/clippy_lints/src/derive.rs
src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
src/tools/clippy/clippy_lints/src/duplicate_mod.rs
src/tools/clippy/clippy_lints/src/empty_drop.rs
src/tools/clippy/clippy_lints/src/entry.rs
src/tools/clippy/clippy_lints/src/equatable_if_let.rs
src/tools/clippy/clippy_lints/src/format_push_string.rs
src/tools/clippy/clippy_lints/src/inherent_impl.rs
src/tools/clippy/clippy_lints/src/invalid_utf8_in_unchecked.rs
src/tools/clippy/clippy_lints/src/large_enum_variant.rs
src/tools/clippy/clippy_lints/src/lib.register_all.rs
src/tools/clippy/clippy_lints/src/lib.register_correctness.rs
src/tools/clippy/clippy_lints/src/lib.register_lints.rs
src/tools/clippy/clippy_lints/src/lib.register_perf.rs
src/tools/clippy/clippy_lints/src/lib.register_restriction.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs
src/tools/clippy/clippy_lints/src/loops/manual_find.rs
src/tools/clippy/clippy_lints/src/loops/manual_flatten.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_non_exhaustive.rs
src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
src/tools/clippy/clippy_lints/src/matches/manual_map.rs
src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs
src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
src/tools/clippy/clippy_lints/src/matches/mod.rs
src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
src/tools/clippy/clippy_lints/src/methods/filter_map.rs
src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
src/tools/clippy/clippy_lints/src/misc.rs
src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs
src/tools/clippy/clippy_lints/src/new_without_default.rs
src/tools/clippy/clippy_lints/src/octal_escapes.rs
src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
src/tools/clippy/clippy_lints/src/option_if_let_else.rs
src/tools/clippy/clippy_lints/src/ptr.rs
src/tools/clippy/clippy_lints/src/question_mark.rs
src/tools/clippy/clippy_lints/src/regex.rs
src/tools/clippy/clippy_lints/src/shadow.rs
src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs
src/tools/clippy/clippy_lints/src/trait_bounds.rs
src/tools/clippy/clippy_lints/src/types/box_collection.rs
src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
src/tools/clippy/clippy_lints/src/unit_types/mod.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
src/tools/clippy/clippy_lints/src/write.rs
src/tools/clippy/clippy_utils/src/ast_utils.rs
src/tools/clippy/clippy_utils/src/hir_utils.rs
src/tools/clippy/clippy_utils/src/lib.rs
src/tools/clippy/clippy_utils/src/msrvs.rs
src/tools/clippy/clippy_utils/src/paths.rs
src/tools/clippy/clippy_utils/src/source.rs
src/tools/clippy/clippy_utils/src/sugg.rs
src/tools/clippy/clippy_utils/src/ty.rs
src/tools/clippy/clippy_utils/src/visitors.rs
src/tools/clippy/lintcheck/Cargo.toml
src/tools/clippy/lintcheck/README.md
src/tools/clippy/lintcheck/src/main.rs
src/tools/clippy/rust-toolchain
src/tools/clippy/tests/compile-test.rs
src/tools/clippy/tests/dogfood.rs
src/tools/clippy/tests/lint_message_convention.rs
src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/d.rs
src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.rs
src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr
src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
src/tools/clippy/tests/ui/box_collection.rs
src/tools/clippy/tests/ui/box_collection.stderr
src/tools/clippy/tests/ui/branches_sharing_code/false_positives.rs
src/tools/clippy/tests/ui/cast_size_32bit.stderr
src/tools/clippy/tests/ui/crashes/ice-9041.rs
src/tools/clippy/tests/ui/crashes/ice-9041.stderr
src/tools/clippy/tests/ui/equatable_if_let.fixed
src/tools/clippy/tests/ui/equatable_if_let.rs
src/tools/clippy/tests/ui/equatable_if_let.stderr
src/tools/clippy/tests/ui/explicit_auto_deref.fixed
src/tools/clippy/tests/ui/explicit_auto_deref.rs
src/tools/clippy/tests/ui/explicit_auto_deref.stderr
src/tools/clippy/tests/ui/if_let_mutex.rs
src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.rs
src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.stderr
src/tools/clippy/tests/ui/let_unit.fixed
src/tools/clippy/tests/ui/let_unit.rs
src/tools/clippy/tests/ui/let_unit.stderr
src/tools/clippy/tests/ui/manual_filter_map.fixed
src/tools/clippy/tests/ui/manual_filter_map.rs
src/tools/clippy/tests/ui/manual_filter_map.stderr
src/tools/clippy/tests/ui/manual_find_map.fixed
src/tools/clippy/tests/ui/manual_find_map.rs
src/tools/clippy/tests/ui/manual_find_map.stderr
src/tools/clippy/tests/ui/manual_flatten.rs
src/tools/clippy/tests/ui/manual_flatten.stderr
src/tools/clippy/tests/ui/map_flatten_fixable.fixed
src/tools/clippy/tests/ui/map_flatten_fixable.rs
src/tools/clippy/tests/ui/map_flatten_fixable.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/needless_borrow.fixed
src/tools/clippy/tests/ui/needless_borrow.rs
src/tools/clippy/tests/ui/needless_match.fixed
src/tools/clippy/tests/ui/needless_match.rs
src/tools/clippy/tests/ui/needless_match.stderr
src/tools/clippy/tests/ui/new_without_default.rs
src/tools/clippy/tests/ui/no_effect.rs
src/tools/clippy/tests/ui/no_effect.stderr
src/tools/clippy/tests/ui/or_fun_call.fixed
src/tools/clippy/tests/ui/or_fun_call.stderr
src/tools/clippy/tests/ui/question_mark.fixed
src/tools/clippy/tests/ui/question_mark.rs
src/tools/clippy/tests/ui/question_mark.stderr
src/tools/clippy/tests/ui/same_name_method.rs
src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed
src/tools/clippy/tests/ui/search_is_some_fixable_some.rs
src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr
src/tools/clippy/tests/ui/shadow.stderr
src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs
src/tools/clippy/tests/ui/std_instead_of_core.rs
src/tools/clippy/tests/ui/std_instead_of_core.stderr
src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs
src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr
src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
src/tools/clippy/tests/ui/type_repetition_in_bounds.rs
src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr
src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
src/tools/clippy/tests/ui/while_let_on_iterator.fixed
src/tools/clippy/tests/ui/while_let_on_iterator.rs
src/tools/clippy/tests/ui/while_let_on_iterator.stderr
src/tools/clippy/tests/ui/wildcard_imports.fixed
src/tools/clippy/tests/ui/wildcard_imports.rs
src/tools/clippy/util/etc/vscode-tasks.json
src/tools/clippy/util/gh-pages/index.html
src/tools/clippy/util/gh-pages/script.js

index 780ee9d63dfd298afbe988e78d88d0a65b26d6d2,0000000000000000000000000000000000000000..9e49f60892d26490c047173c568d15e9690f82e3
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,39 @@@
- explaining your change. Also, it's helpful for us that
- the lint name is put into brackets `[]` and backticks `` ` ` ``,
- e.g. ``[`lint_name`]``.
 +Thank you for making Clippy better!
 +
 +We're collecting our changelog from pull request descriptions.
 +If your PR only includes internal changes, you can just write
 +`changelog: none`. Otherwise, please write a short comment
- If your PR fixes an issue, you can add "fixes #issue_number" into this
++explaining your change.
 +
++It's also helpful for us that the lint name is put within backticks (`` ` ` ``),
++and then encapsulated by square brackets (`[]`), for example:
++```
++changelog: [`lint_name`]: your change
++```
++
++If your PR fixes an issue, you can add `fixes #issue_number` into this
 +PR description. This way the issue will be automatically closed when
 +your PR is merged.
 +
 +If you added a new lint, here's a checklist for things that will be
 +checked during review or continuous integration.
 +
 +- \[ ] 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`
 +
 +[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
 +
 +Note that you can skip the above if you are just opening a WIP PR in
 +order to get feedback.
 +
 +Delete this line and everything above before opening your PR.
 +
 +---
 +
 +*Please write a short comment explaining your change (or "none" for internal only changes)*
 +
 +changelog:
index b8ea424ef3483152763d7b7e83e475a55635fdf8,0000000000000000000000000000000000000000..97453303cd6aae58c0959ede46d21704e254464e
mode 100644,000000..100644
--- /dev/null
@@@ -1,281 -1,0 +1,281 @@@
-     needs: base
 +name: Clippy Test (bors)
 +
 +on:
 +  push:
 +    branches:
 +      - auto
 +      - try
 +
 +env:
 +  RUST_BACKTRACE: 1
 +  CARGO_TARGET_DIR: '${{ github.workspace }}/target'
 +  NO_FMT_TEST: 1
 +
 +defaults:
 +  run:
 +    shell: bash
 +
 +jobs:
 +  changelog:
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
 +      with:
 +        github_token: "${{ secrets.github_token }}"
 +
 +    - name: Checkout
 +      uses: actions/checkout@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, base, integration_build, integration]
++    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, integration_build, integration]
++    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 9bc93c1cb42c91ad045e77c9a5c88f83abdf095b,0000000000000000000000000000000000000000..920d397add716b742e032d8cdd103d59b94007a2
mode 100644,000000..100644
--- /dev/null
@@@ -1,3880 -1,0 +1,4041 @@@
- See [Changelog Update](doc/changelog_update.md) if you want to update this
 +# Changelog
 +
 +All notable changes to this project will be documented in this file.
- [d0cf3481...master](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...master)
++See [Changelog Update](book/src/development/infrastructure/changelog_update.md) if you want to update this
 +document.
 +
 +## Unreleased / In Rust Nightly
 +
- Current stable, released 2022-05-19
++[7c21f91b...master](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...master)
++
++## Rust 1.62
++
++Current stable, 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
 +
- * [`chars_next_cmp`]: Correctly excapes the suggestion
++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)
- [Roadmap]: https://github.com/rust-lang/rust-clippy/blob/master/doc/roadmap-2021.md
++* [`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)
 +* [`blacklisted_name`]: Now allows blacklisted names in test code
 +  [#7379](https://github.com/rust-lang/rust-clippy/pull/7379)
 +* [`redundant_closure`]: Suggests `&mut` for `FnMut`
 +  [#7437](https://github.com/rust-lang/rust-clippy/pull/7437)
 +* [`disallowed_methods`], [`disallowed_types`]: The configuration values `disallowed-method` and `disallowed-type`
 +  no longer require fully qualified paths
 +  [#7345](https://github.com/rust-lang/rust-clippy/pull/7345)
 +* [`zst_offset`]: Fixed lint invocation after it was accidentally suppressed
 +  [#7396](https://github.com/rust-lang/rust-clippy/pull/7396)
 +
 +### False Positive Fixes
 +
 +* [`default_numeric_fallback`]: No longer lints on float literals as function arguments
 +  [#7446](https://github.com/rust-lang/rust-clippy/pull/7446)
 +* [`use_self`]: No longer lints on type parameters
 +  [#7288](https://github.com/rust-lang/rust-clippy/pull/7288)
 +* [`unimplemented`]: Now ignores the `assert` and `debug_assert` macros
 +  [#7439](https://github.com/rust-lang/rust-clippy/pull/7439)
 +* [`branches_sharing_code`]: Now always checks for block expressions
 +  [#7462](https://github.com/rust-lang/rust-clippy/pull/7462)
 +* [`field_reassign_with_default`]: No longer triggers in macros
 +  [#7160](https://github.com/rust-lang/rust-clippy/pull/7160)
 +* [`redundant_clone`]: No longer lints on required clones for borrowed data
 +  [#7346](https://github.com/rust-lang/rust-clippy/pull/7346)
 +* [`default_numeric_fallback`]: No longer triggers in external macros
 +  [#7325](https://github.com/rust-lang/rust-clippy/pull/7325)
 +* [`needless_bool`]: No longer lints in macros
 +  [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
 +* [`useless_format`]: No longer triggers when additional text is being appended
 +  [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
 +* [`assertions_on_constants`]: `cfg!(...)` is no longer considered to be a constant
 +  [#7319](https://github.com/rust-lang/rust-clippy/pull/7319)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`needless_collect`]: Now show correct lint messages for shadowed values
 +  [#7289](https://github.com/rust-lang/rust-clippy/pull/7289)
 +* [`wrong_pub_self_convention`]: The deprecated message now suggest the correct configuration value
 +  [#7382](https://github.com/rust-lang/rust-clippy/pull/7382)
 +* [`semicolon_if_nothing_returned`]: Allow missing semicolon in blocks with only one expression
 +  [#7326](https://github.com/rust-lang/rust-clippy/pull/7326)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#7470](https://github.com/rust-lang/rust-clippy/pull/7470)
 +* [`redundant_pattern_matching`]
 +  [#7471](https://github.com/rust-lang/rust-clippy/pull/7471)
 +* [`modulo_one`]
 +  [#7473](https://github.com/rust-lang/rust-clippy/pull/7473)
 +* [`use_self`]
 +  [#7428](https://github.com/rust-lang/rust-clippy/pull/7428)
 +
 +## Rust 1.54
 +
 +Released 2021-07-29
 +
 +[7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
 +
 +### New Lints
 +
 +- [`ref_binding_to_reference`]
 +  [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
 +- [`needless_bitwise_bool`]
 +  [#7133](https://github.com/rust-lang/rust-clippy/pull/7133)
 +- [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225)
 +- [`manual_str_repeat`]
 +  [#7265](https://github.com/rust-lang/rust-clippy/pull/7265)
 +- [`suspicious_splitn`]
 +  [#7292](https://github.com/rust-lang/rust-clippy/pull/7292)
 +
 +### Moves and Deprecations
 +
 +- Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of
 +  the new `avoid-breaking-exported-api` config option (see
 +  [Enhancements](#1-54-enhancements))
 +  [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
 +- Move [`inconsistent_struct_constructor`] to `pedantic`
 +  [#7193](https://github.com/rust-lang/rust-clippy/pull/7193)
 +- Move [`needless_borrow`] to `style` (now warn-by-default)
 +  [#7254](https://github.com/rust-lang/rust-clippy/pull/7254)
 +- Move [`suspicious_operation_groupings`] to `nursery`
 +  [#7266](https://github.com/rust-lang/rust-clippy/pull/7266)
 +- Move [`semicolon_if_nothing_returned`] to `pedantic`
 +  [#7268](https://github.com/rust-lang/rust-clippy/pull/7268)
 +
 +### Enhancements <a name="1-54-enhancements"></a>
 +
 +- [`while_let_on_iterator`]: Now also lints in nested loops
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix`
 +  [#7156](https://github.com/rust-lang/rust-clippy/pull/7156)
 +- [`needless_collect`]: Now also lints on assignments with type annotations
 +  [#7163](https://github.com/rust-lang/rust-clippy/pull/7163)
 +- [`if_then_some_else_none`]: Now works with the MSRV config
 +  [#7177](https://github.com/rust-lang/rust-clippy/pull/7177)
 +- Add `avoid-breaking-exported-api` config option for the lints
 +  [`enum_variant_names`], [`large_types_passed_by_value`],
 +  [`trivially_copy_pass_by_ref`], [`unnecessary_wraps`],
 +  [`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set
 +  this configuration option to `false` before a major release (1.0/2.0/...) to
 +  clean up the API [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
 +- [`needless_collect`]: Now lints on even more data structures
 +  [#7188](https://github.com/rust-lang/rust-clippy/pull/7188)
 +- [`missing_docs_in_private_items`]: No longer sees `#[<name> = "<value>"]` like
 +  attributes as sufficient documentation
 +  [#7281](https://github.com/rust-lang/rust-clippy/pull/7281)
 +- [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]:
 +  Now work as expected when used with `allow`
 +  [#7282](https://github.com/rust-lang/rust-clippy/pull/7282)
 +
 +### False Positive Fixes
 +
 +- [`implicit_return`]: Now takes all diverging functions in account to avoid
 +  false positives [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
 +- [`while_let_on_iterator`]: No longer lints when the iterator is a struct field
 +  and the struct is used in the loop
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`multiple_inherent_impl`]: No longer lints with generic arguments
 +  [#7089](https://github.com/rust-lang/rust-clippy/pull/7089)
 +- [`comparison_chain`]: No longer lints in a `const` context
 +  [#7118](https://github.com/rust-lang/rust-clippy/pull/7118)
 +- [`while_immutable_condition`]: Fix false positive where mutation in the loop
 +  variable wasn't picked up
 +  [#7144](https://github.com/rust-lang/rust-clippy/pull/7144)
 +- [`default_trait_access`]: No longer lints in macros
 +  [#7150](https://github.com/rust-lang/rust-clippy/pull/7150)
 +- [`needless_question_mark`]: No longer lints when the inner value is implicitly
 +  dereferenced [#7165](https://github.com/rust-lang/rust-clippy/pull/7165)
 +- [`unused_unit`]: No longer lints when multiple macro contexts are involved
 +  [#7167](https://github.com/rust-lang/rust-clippy/pull/7167)
 +- [`eval_order_dependence`]: Fix false positive in async context
 +  [#7174](https://github.com/rust-lang/rust-clippy/pull/7174)
 +- [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the
 +  type [#7175](https://github.com/rust-lang/rust-clippy/pull/7175)
 +- [`wrong_self_convention`]: No longer lints in trait implementations of
 +  non-`Copy` types [#7182](https://github.com/rust-lang/rust-clippy/pull/7182)
 +- [`suboptimal_flops`]: No longer lints on `powi(2)`
 +  [#7201](https://github.com/rust-lang/rust-clippy/pull/7201)
 +- [`wrong_self_convention`]: No longer lints if there is no implicit `self`
 +  [#7215](https://github.com/rust-lang/rust-clippy/pull/7215)
 +- [`option_if_let_else`]: No longer lints on `else if let` pattern
 +  [#7216](https://github.com/rust-lang/rust-clippy/pull/7216)
 +- [`use_self`], [`useless_conversion`]: Fix false positives when generic
 +  arguments are involved
 +  [#7223](https://github.com/rust-lang/rust-clippy/pull/7223)
 +- [`manual_unwrap_or`]: Fix false positive with deref coercion
 +  [#7233](https://github.com/rust-lang/rust-clippy/pull/7233)
 +- [`similar_names`]: No longer lints on `wparam`/`lparam`
 +  [#7255](https://github.com/rust-lang/rust-clippy/pull/7255)
 +- [`redundant_closure`]: No longer lints on using the `vec![]` macro in a
 +  closure [#7263](https://github.com/rust-lang/rust-clippy/pull/7263)
 +
 +### Suggestion Fixes/Improvements
 +
 +- [`implicit_return`]
 +  [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
 +    - Fix suggestion for async functions
 +    - Improve suggestion with macros
 +    - Suggest to change `break` to `return` when appropriate
 +- [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`match_single_binding`]: Improve suggestion when match scrutinee has side
 +  effects [#7095](https://github.com/rust-lang/rust-clippy/pull/7095)
 +- [`needless_borrow`]: Now suggests to also change usage sites as needed
 +  [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
 +- [`write_with_newline`]: Improve suggestion when only `\n` is written to the
 +  buffer [#7183](https://github.com/rust-lang/rust-clippy/pull/7183)
 +- [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also
 +  when a `<_ as Trait>::_` is involved
 +  [#7264](https://github.com/rust-lang/rust-clippy/pull/7264)
 +- [`not_unsafe_ptr_arg_deref`]: Improved error message
 +  [#7294](https://github.com/rust-lang/rust-clippy/pull/7294)
 +
 +### ICE Fixes
 +
 +- Fix ICE when running Clippy on `libstd`
 +  [#7140](https://github.com/rust-lang/rust-clippy/pull/7140)
 +- [`implicit_return`]
 +  [#7242](https://github.com/rust-lang/rust-clippy/pull/7242)
 +
 +## Rust 1.53
 +
 +Released 2021-06-17
 +
 +[6ed6f1e...7c7683c](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...7c7683c)
 +
 +### New Lints
 +
 +* [`option_filter_map`]
 +  [#6342](https://github.com/rust-lang/rust-clippy/pull/6342)
 +* [`branches_sharing_code`]
 +  [#6463](https://github.com/rust-lang/rust-clippy/pull/6463)
 +* [`needless_for_each`]
 +  [#6706](https://github.com/rust-lang/rust-clippy/pull/6706)
 +* [`if_then_some_else_none`]
 +  [#6859](https://github.com/rust-lang/rust-clippy/pull/6859)
 +* [`non_octal_unix_permissions`]
 +  [#7001](https://github.com/rust-lang/rust-clippy/pull/7001)
 +* [`unnecessary_self_imports`]
 +  [#7072](https://github.com/rust-lang/rust-clippy/pull/7072)
 +* [`bool_assert_comparison`]
 +  [#7083](https://github.com/rust-lang/rust-clippy/pull/7083)
 +* [`cloned_instead_of_copied`]
 +  [#7098](https://github.com/rust-lang/rust-clippy/pull/7098)
 +* [`flat_map_option`]
 +  [#7101](https://github.com/rust-lang/rust-clippy/pull/7101)
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`filter_map`] lint
 +  [#7059](https://github.com/rust-lang/rust-clippy/pull/7059)
 +* Move [`transmute_ptr_to_ptr`] to `pedantic`
 +  [#7102](https://github.com/rust-lang/rust-clippy/pull/7102)
 +
 +### Enhancements
 +
 +* [`mem_replace_with_default`]: Also lint on common std constructors
 +  [#6820](https://github.com/rust-lang/rust-clippy/pull/6820)
 +* [`wrong_self_convention`]: Also lint on `to_*_mut` methods
 +  [#6828](https://github.com/rust-lang/rust-clippy/pull/6828)
 +* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]:
 +  [#6863](https://github.com/rust-lang/rust-clippy/pull/6863)
 +    * Attempt to find a common path prefix in suggestion
 +    * Don't lint on `Option` and `Result`
 +    * Consider `Self` prefix
 +* [`explicit_deref_methods`]: Also lint on chained `deref` calls
 +  [#6865](https://github.com/rust-lang/rust-clippy/pull/6865)
 +* [`or_fun_call`]: Also lint on `unsafe` blocks
 +  [#6928](https://github.com/rust-lang/rust-clippy/pull/6928)
 +* [`vec_box`], [`linkedlist`], [`option_option`]: Also lint in `const` and
 +  `static` items [#6938](https://github.com/rust-lang/rust-clippy/pull/6938)
 +* [`search_is_some`]: Also check for `is_none`
 +  [#6942](https://github.com/rust-lang/rust-clippy/pull/6942)
 +* [`string_lit_as_bytes`]: Also lint on `into_bytes`
 +  [#6959](https://github.com/rust-lang/rust-clippy/pull/6959)
 +* [`len_without_is_empty`]: Also lint if function signatures of `len` and
 +  `is_empty` don't match
 +  [#6980](https://github.com/rust-lang/rust-clippy/pull/6980)
 +* [`redundant_pattern_matching`]: Also lint if the pattern is a `&` pattern
 +  [#6991](https://github.com/rust-lang/rust-clippy/pull/6991)
 +* [`clone_on_copy`]: Also lint on chained method calls taking `self` by value
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`missing_panics_doc`]: Also lint on `assert_eq!` and `assert_ne!`
 +  [#7029](https://github.com/rust-lang/rust-clippy/pull/7029)
 +* [`needless_return`]: Also lint in `async` functions
 +  [#7067](https://github.com/rust-lang/rust-clippy/pull/7067)
 +* [`unused_io_amount`]: Also lint on expressions like `_.read().ok()?`
 +  [#7100](https://github.com/rust-lang/rust-clippy/pull/7100)
 +* [`iter_cloned_collect`]: Also lint on large arrays, since const-generics are
 +  now stable [#7138](https://github.com/rust-lang/rust-clippy/pull/7138)
 +
 +### False Positive Fixes
 +
 +* [`upper_case_acronyms`]: No longer lints on public items
 +  [#6805](https://github.com/rust-lang/rust-clippy/pull/6805)
 +* [`suspicious_map`]: No longer lints when side effects may occur inside the
 +  `map` call [#6831](https://github.com/rust-lang/rust-clippy/pull/6831)
 +* [`manual_map`], [`manual_unwrap_or`]: No longer lints in `const` functions
 +  [#6917](https://github.com/rust-lang/rust-clippy/pull/6917)
 +* [`wrong_self_convention`]: Now respects `Copy` types
 +  [#6924](https://github.com/rust-lang/rust-clippy/pull/6924)
 +* [`needless_question_mark`]: No longer lints if the `?` and the `Some(..)` come
 +  from different macro contexts [#6935](https://github.com/rust-lang/rust-clippy/pull/6935)
 +* [`map_entry`]: Better detect if the entry API can be used
 +  [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
 +* [`or_fun_call`]: No longer lints on some `len` function calls
 +  [#6950](https://github.com/rust-lang/rust-clippy/pull/6950)
 +* [`new_ret_no_self`]: No longer lints when `Self` is returned with different
 +  generic arguments [#6952](https://github.com/rust-lang/rust-clippy/pull/6952)
 +* [`upper_case_acronyms`]: No longer lints on public items
 +  [#6981](https://github.com/rust-lang/rust-clippy/pull/6981)
 +* [`explicit_into_iter_loop`]: Only lint when `into_iter` is an implementation
 +  of `IntoIterator` [#6982](https://github.com/rust-lang/rust-clippy/pull/6982)
 +* [`expl_impl_clone_on_copy`]: Take generic constraints into account before
 +  suggesting to use `derive` instead
 +  [#6993](https://github.com/rust-lang/rust-clippy/pull/6993)
 +* [`missing_panics_doc`]: No longer lints when only debug-assertions are used
 +  [#6996](https://github.com/rust-lang/rust-clippy/pull/6996)
 +* [`clone_on_copy`]: Only lint when using the `Clone` trait
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`wrong_self_convention`]: No longer lints inside a trait implementation
 +  [#7002](https://github.com/rust-lang/rust-clippy/pull/7002)
 +* [`redundant_clone`]: No longer lints when the cloned value is modified while
 +  the clone is in use
 +  [#7011](https://github.com/rust-lang/rust-clippy/pull/7011)
 +* [`same_item_push`]: No longer lints if the `Vec` is used in the loop body
 +  [#7018](https://github.com/rust-lang/rust-clippy/pull/7018)
 +* [`cargo_common_metadata`]: Remove author requirement
 +  [#7026](https://github.com/rust-lang/rust-clippy/pull/7026)
 +* [`panic_in_result_fn`]: No longer lints on `debug_assert` family
 +  [#7060](https://github.com/rust-lang/rust-clippy/pull/7060)
 +* [`panic`]: No longer wrongfully lints on `debug_assert` with message
 +  [#7063](https://github.com/rust-lang/rust-clippy/pull/7063)
 +* [`wrong_self_convention`]: No longer lints in trait implementations where no
 +  `self` is involved [#7064](https://github.com/rust-lang/rust-clippy/pull/7064)
 +* [`missing_const_for_fn`]: No longer lints when unstable `const` function is
 +  involved [#7076](https://github.com/rust-lang/rust-clippy/pull/7076)
 +* [`suspicious_else_formatting`]: Allow Allman style braces
 +  [#7087](https://github.com/rust-lang/rust-clippy/pull/7087)
 +* [`inconsistent_struct_constructor`]: No longer lints in macros
 +  [#7097](https://github.com/rust-lang/rust-clippy/pull/7097)
 +* [`single_component_path_imports`]: No longer lints on macro re-exports
 +  [#7120](https://github.com/rust-lang/rust-clippy/pull/7120)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`redundant_pattern_matching`]: Add a note when applying this lint would
 +  change the drop order
 +  [#6568](https://github.com/rust-lang/rust-clippy/pull/6568)
 +* [`write_literal`], [`print_literal`]: Add auto-applicable suggestion
 +  [#6821](https://github.com/rust-lang/rust-clippy/pull/6821)
 +* [`manual_map`]: Fix suggestion for complex `if let ... else` chains
 +  [#6856](https://github.com/rust-lang/rust-clippy/pull/6856)
 +* [`inconsistent_struct_constructor`]: Make lint description and message clearer
 +  [#6892](https://github.com/rust-lang/rust-clippy/pull/6892)
 +* [`map_entry`]: Now suggests `or_insert`, `insert_with` or `match _.entry(_)`
 +  as appropriate [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
 +* [`manual_flatten`]: Suggest to insert `copied` if necessary
 +  [#6962](https://github.com/rust-lang/rust-clippy/pull/6962)
 +* [`redundant_slicing`]: Fix suggestion when a re-borrow might be required or
 +  when the value is from a macro call
 +  [#6975](https://github.com/rust-lang/rust-clippy/pull/6975)
 +* [`match_wildcard_for_single_variants`]: Fix suggestion for hidden variant
 +  [#6988](https://github.com/rust-lang/rust-clippy/pull/6988)
 +* [`clone_on_copy`]: Correct suggestion when the cloned value is a macro call
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`manual_map`]: Fix suggestion at the end of an if chain
 +  [#7004](https://github.com/rust-lang/rust-clippy/pull/7004)
 +* Fix needless parenthesis output in multiple lint suggestions
 +  [#7013](https://github.com/rust-lang/rust-clippy/pull/7013)
 +* [`needless_collect`]: Better explanation in the lint message
 +  [#7020](https://github.com/rust-lang/rust-clippy/pull/7020)
 +* [`useless_vec`]: Now considers mutability
 +  [#7036](https://github.com/rust-lang/rust-clippy/pull/7036)
 +* [`useless_format`]: Wrap the content in braces if necessary
 +  [#7092](https://github.com/rust-lang/rust-clippy/pull/7092)
 +* [`single_match`]: Don't suggest an equality check for types which don't
 +  implement `PartialEq`
 +  [#7093](https://github.com/rust-lang/rust-clippy/pull/7093)
 +* [`from_over_into`]: Mention type in help message
 +  [#7099](https://github.com/rust-lang/rust-clippy/pull/7099)
 +* [`manual_unwrap_or`]: Fix invalid code suggestion due to a macro call
 +  [#7136](https://github.com/rust-lang/rust-clippy/pull/7136)
 +
 +### ICE Fixes
 +
 +* [`macro_use_imports`]
 +  [#7022](https://github.com/rust-lang/rust-clippy/pull/7022)
 +* [`missing_panics_doc`]
 +  [#7034](https://github.com/rust-lang/rust-clippy/pull/7034)
 +* [`tabs_in_doc_comments`]
 +  [#7039](https://github.com/rust-lang/rust-clippy/pull/7039)
 +* [`missing_const_for_fn`]
 +  [#7128](https://github.com/rust-lang/rust-clippy/pull/7128)
 +
 +### Others
 +
 +* [Clippy's lint
 +  list](https://rust-lang.github.io/rust-clippy/master/index.html) now supports
 +  themes [#7030](https://github.com/rust-lang/rust-clippy/pull/7030)
 +* Lints that were uplifted to `rustc` now mention the new `rustc` name in the
 +  deprecation warning
 +  [#7056](https://github.com/rust-lang/rust-clippy/pull/7056)
 +
 +## Rust 1.52
 +
 +Released 2021-05-06
 +
 +[3e41797...6ed6f1e](https://github.com/rust-lang/rust-clippy/compare/3e41797...6ed6f1e)
 +
 +### New Lints
 +
 +* [`from_str_radix_10`]
 +  [#6717](https://github.com/rust-lang/rust-clippy/pull/6717)
 +* [`implicit_clone`]
 +  [#6730](https://github.com/rust-lang/rust-clippy/pull/6730)
 +* [`semicolon_if_nothing_returned`]
 +  [#6681](https://github.com/rust-lang/rust-clippy/pull/6681)
 +* [`manual_flatten`]
 +  [#6646](https://github.com/rust-lang/rust-clippy/pull/6646)
 +* [`inconsistent_struct_constructor`]
 +  [#6769](https://github.com/rust-lang/rust-clippy/pull/6769)
 +* [`iter_count`]
 +  [#6791](https://github.com/rust-lang/rust-clippy/pull/6791)
 +* [`default_numeric_fallback`]
 +  [#6662](https://github.com/rust-lang/rust-clippy/pull/6662)
 +* [`bytes_nth`]
 +  [#6695](https://github.com/rust-lang/rust-clippy/pull/6695)
 +* [`filter_map_identity`]
 +  [#6685](https://github.com/rust-lang/rust-clippy/pull/6685)
 +* [`manual_map`]
 +  [#6573](https://github.com/rust-lang/rust-clippy/pull/6573)
 +
 +### Moves and Deprecations
 +
 +* Moved [`upper_case_acronyms`] to `pedantic`
 +  [#6775](https://github.com/rust-lang/rust-clippy/pull/6775)
 +* Moved [`manual_map`] to `nursery`
 +  [#6796](https://github.com/rust-lang/rust-clippy/pull/6796)
 +* Moved [`unnecessary_wraps`] to `pedantic`
 +  [#6765](https://github.com/rust-lang/rust-clippy/pull/6765)
 +* Moved [`trivial_regex`] to `nursery`
 +  [#6696](https://github.com/rust-lang/rust-clippy/pull/6696)
 +* Moved [`naive_bytecount`] to `pedantic`
 +  [#6825](https://github.com/rust-lang/rust-clippy/pull/6825)
 +* Moved [`upper_case_acronyms`] to `style`
 +  [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
 +* Moved [`manual_map`] to `style`
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +
 +### Enhancements
 +
 +* [`disallowed_methods`]: Now supports functions in addition to methods
 +  [#6674](https://github.com/rust-lang/rust-clippy/pull/6674)
 +* [`upper_case_acronyms`]: Added a new configuration `upper-case-acronyms-aggressive` to
 +  trigger the lint if there is more than one uppercase character next to each other
 +  [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
 +* [`collapsible_match`]: Now supports block comparison with different value names
 +  [#6754](https://github.com/rust-lang/rust-clippy/pull/6754)
 +* [`unnecessary_wraps`]: Will now suggest removing unnecessary wrapped return unit type, like `Option<()>`
 +  [#6665](https://github.com/rust-lang/rust-clippy/pull/6665)
 +* Improved value usage detection in closures
 +  [#6698](https://github.com/rust-lang/rust-clippy/pull/6698)
 +
 +### False Positive Fixes
 +
 +* [`use_self`]: No longer lints in macros
 +  [#6833](https://github.com/rust-lang/rust-clippy/pull/6833)
 +* [`use_self`]: Fixed multiple false positives for: generics, associated types and derive implementations
 +  [#6179](https://github.com/rust-lang/rust-clippy/pull/6179)
 +* [`missing_inline_in_public_items`]: No longer lints for procedural macros
 +  [#6814](https://github.com/rust-lang/rust-clippy/pull/6814)
 +* [`inherent_to_string`]: No longer lints on functions with function generics
 +  [#6771](https://github.com/rust-lang/rust-clippy/pull/6771)
 +* [`doc_markdown`]: Add `OpenDNS` to the default configuration as an allowed identifier
 +  [#6783](https://github.com/rust-lang/rust-clippy/pull/6783)
 +* [`missing_panics_doc`]: No longer lints on [`unreachable!`](https://doc.rust-lang.org/std/macro.unreachable.html)
 +  [#6700](https://github.com/rust-lang/rust-clippy/pull/6700)
 +* [`collapsible_if`]: No longer lints on if statements with attributes
 +  [#6701](https://github.com/rust-lang/rust-clippy/pull/6701)
 +* [`match_same_arms`]: Only considers empty blocks as equal if the tokens contained are the same
 +  [#6843](https://github.com/rust-lang/rust-clippy/pull/6843)
 +* [`redundant_closure`]: Now ignores macros
 +  [#6871](https://github.com/rust-lang/rust-clippy/pull/6871)
 +* [`manual_map`]: Fixed false positives when control flow statements like `return`, `break` etc. are used
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +* [`vec_init_then_push`]: Fixed false positives for loops and if statements
 +  [#6697](https://github.com/rust-lang/rust-clippy/pull/6697)
 +* [`len_without_is_empty`]: Will now consider multiple impl blocks and `#[allow]` on
 +  the `len` method as well as the type definition.
 +  [#6853](https://github.com/rust-lang/rust-clippy/pull/6853)
 +* [`let_underscore_drop`]: Only lints on types which implement `Drop`
 +  [#6682](https://github.com/rust-lang/rust-clippy/pull/6682)
 +* [`unit_arg`]: No longer lints on unit arguments when they come from a path expression.
 +  [#6601](https://github.com/rust-lang/rust-clippy/pull/6601)
 +* [`cargo_common_metadata`]: No longer lints if
 +  [`publish = false`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field)
 +  is defined in the manifest
 +  [#6650](https://github.com/rust-lang/rust-clippy/pull/6650)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`collapsible_match`]: Fixed lint message capitalization
 +  [#6766](https://github.com/rust-lang/rust-clippy/pull/6766)
 +* [`or_fun_call`]: Improved suggestions for `or_insert(vec![])`
 +  [#6790](https://github.com/rust-lang/rust-clippy/pull/6790)
 +* [`manual_map`]: No longer expands macros in the suggestions
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +* Aligned Clippy's lint messages with the rustc dev guide
 +  [#6787](https://github.com/rust-lang/rust-clippy/pull/6787)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#6866](https://github.com/rust-lang/rust-clippy/pull/6866)
 +
 +### Documentation Improvements
 +
 +* [`useless_format`]: Improved the documentation example
 +  [#6854](https://github.com/rust-lang/rust-clippy/pull/6854)
 +* Clippy's [`README.md`]: Includes a new subsection on running Clippy as a rustc wrapper
 +  [#6782](https://github.com/rust-lang/rust-clippy/pull/6782)
 +
 +### Others
 +* Running `cargo clippy` after `cargo check` now works as expected
 +  (`cargo clippy` and `cargo check` no longer shares the same build cache)
 +  [#6687](https://github.com/rust-lang/rust-clippy/pull/6687)
 +* Cargo now re-runs Clippy if arguments after `--` provided to `cargo clippy` are changed.
 +  [#6834](https://github.com/rust-lang/rust-clippy/pull/6834)
 +* Extracted Clippy's `utils` module into the new `clippy_utils` crate
 +  [#6756](https://github.com/rust-lang/rust-clippy/pull/6756)
 +* Clippy lintcheck tool improvements
 +  [#6800](https://github.com/rust-lang/rust-clippy/pull/6800)
 +  [#6735](https://github.com/rust-lang/rust-clippy/pull/6735)
 +  [#6764](https://github.com/rust-lang/rust-clippy/pull/6764)
 +  [#6708](https://github.com/rust-lang/rust-clippy/pull/6708)
 +  [#6780](https://github.com/rust-lang/rust-clippy/pull/6780)
 +  [#6686](https://github.com/rust-lang/rust-clippy/pull/6686)
 +
 +## Rust 1.51
 +
 +Released 2021-03-25
 +
 +[4911ab1...3e41797](https://github.com/rust-lang/rust-clippy/compare/4911ab1...3e41797)
 +
 +### New Lints
 +
 +* [`upper_case_acronyms`]
 +  [#6475](https://github.com/rust-lang/rust-clippy/pull/6475)
 +* [`from_over_into`] [#6476](https://github.com/rust-lang/rust-clippy/pull/6476)
 +* [`case_sensitive_file_extension_comparisons`]
 +  [#6500](https://github.com/rust-lang/rust-clippy/pull/6500)
 +* [`needless_question_mark`]
 +  [#6507](https://github.com/rust-lang/rust-clippy/pull/6507)
 +* [`missing_panics_doc`]
 +  [#6523](https://github.com/rust-lang/rust-clippy/pull/6523)
 +* [`redundant_slicing`]
 +  [#6528](https://github.com/rust-lang/rust-clippy/pull/6528)
 +* [`vec_init_then_push`]
 +  [#6538](https://github.com/rust-lang/rust-clippy/pull/6538)
 +* [`ptr_as_ptr`] [#6542](https://github.com/rust-lang/rust-clippy/pull/6542)
 +* [`collapsible_else_if`] (split out from `collapsible_if`)
 +  [#6544](https://github.com/rust-lang/rust-clippy/pull/6544)
 +* [`inspect_for_each`] [#6577](https://github.com/rust-lang/rust-clippy/pull/6577)
 +* [`manual_filter_map`]
 +  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
 +* [`exhaustive_enums`]
 +  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
 +* [`exhaustive_structs`]
 +  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
 +
 +### Moves and Deprecations
 +
 +* Replace [`find_map`] with [`manual_find_map`]
 +  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
 +* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint
 +  [#6653](https://github.com/rust-lang/rust-clippy/pull/6653)
 +
 +### Enhancements
 +
 +* [`ptr_arg`] Now also suggests to use `&Path` instead of `&PathBuf`
 +  [#6506](https://github.com/rust-lang/rust-clippy/pull/6506)
 +* [`cast_ptr_alignment`] Also lint when the `pointer::cast` method is used
 +  [#6557](https://github.com/rust-lang/rust-clippy/pull/6557)
 +* [`collapsible_match`] Now also deals with `&` and `*` operators in the `match`
 +  scrutinee [#6619](https://github.com/rust-lang/rust-clippy/pull/6619)
 +
 +### False Positive Fixes
 +
 +* [`similar_names`] Ignore underscore prefixed names
 +  [#6403](https://github.com/rust-lang/rust-clippy/pull/6403)
 +* [`print_literal`] and [`write_literal`] No longer lint numeric literals
 +  [#6408](https://github.com/rust-lang/rust-clippy/pull/6408)
 +* [`large_enum_variant`] No longer lints in external macros
 +  [#6485](https://github.com/rust-lang/rust-clippy/pull/6485)
 +* [`empty_enum`] Only lint if `never_type` feature is enabled
 +  [#6513](https://github.com/rust-lang/rust-clippy/pull/6513)
 +* [`field_reassign_with_default`] No longer lints in macros
 +  [#6553](https://github.com/rust-lang/rust-clippy/pull/6553)
 +* [`size_of_in_element_count`] No longer lints when dividing by element size
 +  [#6578](https://github.com/rust-lang/rust-clippy/pull/6578)
 +* [`needless_return`] No longer lints in macros
 +  [#6586](https://github.com/rust-lang/rust-clippy/pull/6586)
 +* [`match_overlapping_arm`] No longer lint when first arm is completely included
 +  in second arm [#6603](https://github.com/rust-lang/rust-clippy/pull/6603)
 +* [`doc_markdown`] Add `WebGL` to the default configuration as an allowed
 +  identifier [#6605](https://github.com/rust-lang/rust-clippy/pull/6605)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`field_reassign_with_default`] Don't expand macro in lint suggestion
 +  [#6531](https://github.com/rust-lang/rust-clippy/pull/6531)
 +* [`match_like_matches_macro`] Strip references in suggestion
 +  [#6532](https://github.com/rust-lang/rust-clippy/pull/6532)
 +* [`single_match`] Suggest `if` over `if let` when possible
 +  [#6574](https://github.com/rust-lang/rust-clippy/pull/6574)
 +* `ref_in_deref` Use parentheses correctly in suggestion
 +  [#6609](https://github.com/rust-lang/rust-clippy/pull/6609)
 +* [`stable_sort_primitive`] Clarify error message
 +  [#6611](https://github.com/rust-lang/rust-clippy/pull/6611)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#6582](https://github.com/rust-lang/rust-clippy/pull/6582)
 +
 +### Documentation Improvements
 +
 +* Improve search performance on the Clippy website and make it possible to
 +  directly search for lints on the GitHub issue tracker
 +  [#6483](https://github.com/rust-lang/rust-clippy/pull/6483)
 +* Clean up `README.md` by removing outdated paragraph
 +  [#6488](https://github.com/rust-lang/rust-clippy/pull/6488)
 +* [`await_holding_refcell_ref`] and [`await_holding_lock`]
 +  [#6585](https://github.com/rust-lang/rust-clippy/pull/6585)
 +* [`as_conversions`] [#6608](https://github.com/rust-lang/rust-clippy/pull/6608)
 +
 +### Others
 +
 +* Clippy now has a [Roadmap] for 2021. If you like to get involved in a bigger
 +  project, take a look at the [Roadmap project page]. All issues listed there
 +  are actively mentored
 +  [#6462](https://github.com/rust-lang/rust-clippy/pull/6462)
 +* The Clippy version number now corresponds to the Rust version number
 +  [#6526](https://github.com/rust-lang/rust-clippy/pull/6526)
 +* Fix oversight which caused Clippy to lint deps in some environments, where
 +  `CLIPPY_TESTS=true` was set somewhere
 +  [#6575](https://github.com/rust-lang/rust-clippy/pull/6575)
 +* Add `cargo dev-lintcheck` tool to the Clippy Dev Tool
 +  [#6469](https://github.com/rust-lang/rust-clippy/pull/6469)
 +
- * Move `{unnnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
++[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)
 +* [`blacklisted_name`]: Remove `bar` from the default configuration
 +  [#5712](https://github.com/rust-lang/rust-clippy/pull/5712)
 +* [`redundant_pattern_matching`]: Avoid suggesting non-`const fn` calls in const contexts
 +  [#5724](https://github.com/rust-lang/rust-clippy/pull/5724)
 +
 +### Suggestion Fixes/Improvements
 +
 +* Fix suggestion of [`unit_arg`] lint, so that it suggest semantic equivalent code
 +  [#4455](https://github.com/rust-lang/rust-clippy/pull/4455)
 +* Add auto applicable suggestion to [`macro_use_imports`]
 +  [#5279](https://github.com/rust-lang/rust-clippy/pull/5279)
 +
 +### ICE Fixes
 +
 +* Fix ICE in the `consts` module of Clippy [#5709](https://github.com/rust-lang/rust-clippy/pull/5709)
 +
 +### Documentation Improvements
 +
 +* Improve code examples across multiple lints [#5664](https://github.com/rust-lang/rust-clippy/pull/5664)
 +
 +### Others
 +
 +* Introduce a `--rustc` flag to `clippy-driver`, which turns `clippy-driver`
 +  into `rustc` and passes all the given arguments to `rustc`. This is especially
 +  useful for tools that need the `rustc` version Clippy was compiled with,
 +  instead of the Clippy version. E.g. `clippy-driver --rustc --version` will
 +  print the output of `rustc --version`.
 +  [#5178](https://github.com/rust-lang/rust-clippy/pull/5178)
 +* New issue templates now make it easier to complain if Clippy is too annoying
 +  or not annoying enough! [#5735](https://github.com/rust-lang/rust-clippy/pull/5735)
 +
 +## Rust 1.45
 +
 +Released 2020-07-16
 +
 +[891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1)
 +
 +### New lints
 +
 +* [`match_wildcard_for_single_variants`] [#5582](https://github.com/rust-lang/rust-clippy/pull/5582)
 +* [`unsafe_derive_deserialize`] [#5493](https://github.com/rust-lang/rust-clippy/pull/5493)
 +* [`if_let_mutex`] [#5332](https://github.com/rust-lang/rust-clippy/pull/5332)
 +* [`mismatched_target_os`] [#5506](https://github.com/rust-lang/rust-clippy/pull/5506)
 +* [`await_holding_lock`] [#5439](https://github.com/rust-lang/rust-clippy/pull/5439)
 +* [`match_on_vec_items`] [#5522](https://github.com/rust-lang/rust-clippy/pull/5522)
 +* [`manual_async_fn`] [#5576](https://github.com/rust-lang/rust-clippy/pull/5576)
 +* [`reversed_empty_ranges`] [#5583](https://github.com/rust-lang/rust-clippy/pull/5583)
 +* [`manual_non_exhaustive`] [#5550](https://github.com/rust-lang/rust-clippy/pull/5550)
 +
 +### Moves and Deprecations
 +
 +* Downgrade [`match_bool`] to pedantic [#5408](https://github.com/rust-lang/rust-clippy/pull/5408)
 +* Downgrade [`match_wild_err_arm`] to pedantic and update help messages. [#5622](https://github.com/rust-lang/rust-clippy/pull/5622)
 +* Downgrade [`useless_let_if_seq`] to nursery. [#5599](https://github.com/rust-lang/rust-clippy/pull/5599)
 +* Generalize `option_and_then_some` and rename to [`bind_instead_of_map`]. [#5529](https://github.com/rust-lang/rust-clippy/pull/5529)
 +* Rename `identity_conversion` to [`useless_conversion`]. [#5568](https://github.com/rust-lang/rust-clippy/pull/5568)
 +* Merge `block_in_if_condition_expr` and `block_in_if_condition_stmt` into [`blocks_in_if_conditions`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_map_unwrap_or`, `option_map_unwrap_or_else` and `result_map_unwrap_or_else` into [`map_unwrap_or`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_unwrap_used` and `result_unwrap_used` into [`unwrap_used`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_expect_used` and `result_expect_used` into [`expect_used`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `for_loop_over_option` and `for_loop_over_result` into [`for_loops_over_fallibles`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +
 +### Enhancements
 +
 +* Avoid running cargo lints when not enabled to improve performance. [#5505](https://github.com/rust-lang/rust-clippy/pull/5505)
 +* Extend [`useless_conversion`] with `TryFrom` and `TryInto`. [#5631](https://github.com/rust-lang/rust-clippy/pull/5631)
 +* Lint also in type parameters and where clauses in [`unused_unit`]. [#5592](https://github.com/rust-lang/rust-clippy/pull/5592)
 +* Do not suggest deriving `Default` in [`new_without_default`]. [#5616](https://github.com/rust-lang/rust-clippy/pull/5616)
 +
 +### False Positive Fixes
 +
 +* [`while_let_on_iterator`] [#5525](https://github.com/rust-lang/rust-clippy/pull/5525)
 +* [`empty_line_after_outer_attr`] [#5609](https://github.com/rust-lang/rust-clippy/pull/5609)
 +* [`unnecessary_unwrap`] [#5558](https://github.com/rust-lang/rust-clippy/pull/5558)
 +* [`comparison_chain`] [#5596](https://github.com/rust-lang/rust-clippy/pull/5596)
 +* Don't trigger [`used_underscore_binding`] in await desugaring. [#5535](https://github.com/rust-lang/rust-clippy/pull/5535)
 +* Don't trigger [`borrowed_box`] on mutable references. [#5491](https://github.com/rust-lang/rust-clippy/pull/5491)
 +* Allow `1 << 0` in [`identity_op`]. [#5602](https://github.com/rust-lang/rust-clippy/pull/5602)
 +* Allow `use super::*;` glob imports in [`wildcard_imports`]. [#5564](https://github.com/rust-lang/rust-clippy/pull/5564)
 +* Whitelist more words in [`doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611)
 +* Skip dev and build deps in [`multiple_crate_versions`]. [#5636](https://github.com/rust-lang/rust-clippy/pull/5636)
 +* Honor `allow` attribute on arguments in [`ptr_arg`]. [#5647](https://github.com/rust-lang/rust-clippy/pull/5647)
 +* Honor lint level attributes for [`redundant_field_names`], [`just_underscores_and_digits`], [`many_single_char_names`]
 +and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/5651)
 +* Ignore calls to `len` in [`or_fun_call`]. [#4429](https://github.com/rust-lang/rust-clippy/pull/4429)
 +
 +### Suggestion Improvements
 +
 +* Simplify suggestions in [`manual_memcpy`]. [#5536](https://github.com/rust-lang/rust-clippy/pull/5536)
 +* Fix suggestion in [`redundant_pattern_matching`] for macros. [#5511](https://github.com/rust-lang/rust-clippy/pull/5511)
 +* Avoid suggesting `copied()` for mutable references in [`map_clone`]. [#5530](https://github.com/rust-lang/rust-clippy/pull/5530)
 +* Improve help message for [`clone_double_ref`]. [#5547](https://github.com/rust-lang/rust-clippy/pull/5547)
 +
 +### ICE Fixes
 +
 +* Fix ICE caused in unwrap module. [#5590](https://github.com/rust-lang/rust-clippy/pull/5590)
 +* Fix ICE on rustc test issue-69020-assoc-const-arith-overflow.rs [#5499](https://github.com/rust-lang/rust-clippy/pull/5499)
 +
 +### Documentation
 +
 +* Clarify the documentation of [`unnecessary_mut_passed`]. [#5639](https://github.com/rust-lang/rust-clippy/pull/5639)
 +* Extend example for [`unneeded_field_pattern`]. [#5541](https://github.com/rust-lang/rust-clippy/pull/5541)
 +
 +## Rust 1.44
 +
 +Released 2020-06-04
 +
 +[204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8)
 +
 +### New lints
 +
 +* [`explicit_deref_methods`] [#5226](https://github.com/rust-lang/rust-clippy/pull/5226)
 +* [`implicit_saturating_sub`] [#5427](https://github.com/rust-lang/rust-clippy/pull/5427)
 +* [`macro_use_imports`] [#5230](https://github.com/rust-lang/rust-clippy/pull/5230)
 +* [`verbose_file_reads`] [#5272](https://github.com/rust-lang/rust-clippy/pull/5272)
 +* [`future_not_send`] [#5423](https://github.com/rust-lang/rust-clippy/pull/5423)
 +* [`redundant_pub_crate`] [#5319](https://github.com/rust-lang/rust-clippy/pull/5319)
 +* [`large_const_arrays`] [#5248](https://github.com/rust-lang/rust-clippy/pull/5248)
 +* [`result_map_or_into_option`] [#5415](https://github.com/rust-lang/rust-clippy/pull/5415)
 +* [`redundant_allocation`] [#5349](https://github.com/rust-lang/rust-clippy/pull/5349)
 +* [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
 +* [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
 +
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380)
 +* Move [`cognitive_complexity`] to nursery [#5428](https://github.com/rust-lang/rust-clippy/pull/5428)
 +* Move [`useless_transmute`] to nursery [#5364](https://github.com/rust-lang/rust-clippy/pull/5364)
 +* Downgrade [`inefficient_to_string`] to pedantic [#5412](https://github.com/rust-lang/rust-clippy/pull/5412)
 +* Downgrade [`option_option`] to pedantic [#5401](https://github.com/rust-lang/rust-clippy/pull/5401)
 +* Downgrade [`unreadable_literal`] to pedantic [#5419](https://github.com/rust-lang/rust-clippy/pull/5419)
 +* Downgrade [`let_unit_value`] to pedantic [#5409](https://github.com/rust-lang/rust-clippy/pull/5409)
 +* Downgrade [`trivially_copy_pass_by_ref`] to pedantic [#5410](https://github.com/rust-lang/rust-clippy/pull/5410)
 +* Downgrade [`implicit_hasher`] to pedantic [#5411](https://github.com/rust-lang/rust-clippy/pull/5411)
 +
 +### Enhancements
 +
 +* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to
 +  auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363)
 +* Make [`redundant_clone`] also trigger on cases where the cloned value is not
 +  consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304)
 +* Expand [`integer_arithmetic`] to also disallow bit-shifting [#5430](https://github.com/rust-lang/rust-clippy/pull/5430)
 +* [`option_as_ref_deref`] now detects more deref cases [#5425](https://github.com/rust-lang/rust-clippy/pull/5425)
 +* [`large_enum_variant`] now report the sizes of the largest and second-largest variants [#5466](https://github.com/rust-lang/rust-clippy/pull/5466)
 +* [`bool_comparison`] now also checks for inequality comparisons that can be
 +  written more concisely [#5365](https://github.com/rust-lang/rust-clippy/pull/5365)
 +* Expand [`clone_on_copy`] to work in method call arguments as well [#5441](https://github.com/rust-lang/rust-clippy/pull/5441)
 +* [`redundant_pattern_matching`] now also handles `while let` [#5483](https://github.com/rust-lang/rust-clippy/pull/5483)
 +* [`integer_arithmetic`] now also lints references of integers [#5329](https://github.com/rust-lang/rust-clippy/pull/5329)
 +* Expand [`float_cmp_const`] to also work on arrays [#5345](https://github.com/rust-lang/rust-clippy/pull/5345)
 +* Trigger [`map_flatten`] when map is called on an `Option` [#5473](https://github.com/rust-lang/rust-clippy/pull/5473)
 +
 +### False Positive Fixes
 +
 +* [`many_single_char_names`] [#5468](https://github.com/rust-lang/rust-clippy/pull/5468)
 +* [`should_implement_trait`] [#5437](https://github.com/rust-lang/rust-clippy/pull/5437)
 +* [`unused_self`] [#5387](https://github.com/rust-lang/rust-clippy/pull/5387)
 +* [`redundant_clone`] [#5453](https://github.com/rust-lang/rust-clippy/pull/5453)
 +* [`precedence`] [#5445](https://github.com/rust-lang/rust-clippy/pull/5445)
 +* [`suspicious_op_assign_impl`] [#5424](https://github.com/rust-lang/rust-clippy/pull/5424)
 +* [`needless_lifetimes`] [#5293](https://github.com/rust-lang/rust-clippy/pull/5293)
 +* [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287)
 +* [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451)
 +
 +
 +### Suggestion Improvements
 +
 +* Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481)
 +* Improve the suggested placeholder in [`option_map_unit_fn`] [#5292](https://github.com/rust-lang/rust-clippy/pull/5292)
 +* Improve suggestion for [`match_single_binding`] when triggered inside a closure [#5350](https://github.com/rust-lang/rust-clippy/pull/5350)
 +
 +### ICE Fixes
 +
 +* Handle the unstable `trivial_bounds` feature [#5296](https://github.com/rust-lang/rust-clippy/pull/5296)
 +* `shadow_*` lints [#5297](https://github.com/rust-lang/rust-clippy/pull/5297)
 +
 +### Documentation
 +
 +* Fix documentation generation for configurable lints [#5353](https://github.com/rust-lang/rust-clippy/pull/5353)
 +* Update documentation for [`new_ret_no_self`] [#5448](https://github.com/rust-lang/rust-clippy/pull/5448)
 +* The documentation for [`option_option`] now suggest using a tri-state enum [#5403](https://github.com/rust-lang/rust-clippy/pull/5403)
 +* Fix bit mask example in [`verbose_bit_mask`] documentation [#5454](https://github.com/rust-lang/rust-clippy/pull/5454)
 +* [`wildcard_imports`] documentation now mentions that `use ...::prelude::*` is
 +  not linted [#5312](https://github.com/rust-lang/rust-clippy/pull/5312)
 +
 +## Rust 1.43
 +
 +Released 2020-04-23
 +
 +[4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b)
 +
 +### New lints
 +
 +* [`imprecise_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
 +* [`suboptimal_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
 +* [`wildcard_imports`] [#5029](https://github.com/rust-lang/rust-clippy/pull/5029)
 +* [`single_component_path_imports`] [#5058](https://github.com/rust-lang/rust-clippy/pull/5058)
 +* [`match_single_binding`] [#5061](https://github.com/rust-lang/rust-clippy/pull/5061)
 +* [`let_underscore_lock`] [#5101](https://github.com/rust-lang/rust-clippy/pull/5101)
 +* [`struct_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
 +* [`fn_params_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
 +* [`option_env_unwrap`] [#5148](https://github.com/rust-lang/rust-clippy/pull/5148)
 +* [`lossy_float_literal`] [#5202](https://github.com/rust-lang/rust-clippy/pull/5202)
 +* [`rest_pat_in_fully_bound_structs`] [#5258](https://github.com/rust-lang/rust-clippy/pull/5258)
 +
 +### Moves and Deprecations
 +
 +* Move [`unneeded_field_pattern`] to pedantic group [#5200](https://github.com/rust-lang/rust-clippy/pull/5200)
 +
 +### Enhancements
 +
 +* Make [`missing_errors_doc`] lint also trigger on `async` functions
 +  [#5181](https://github.com/rust-lang/rust-clippy/pull/5181)
 +* Add more constants to [`approx_constant`] [#5193](https://github.com/rust-lang/rust-clippy/pull/5193)
 +* Extend [`question_mark`] lint [#5266](https://github.com/rust-lang/rust-clippy/pull/5266)
 +
 +### False Positive Fixes
 +
 +* [`use_debug`] [#5047](https://github.com/rust-lang/rust-clippy/pull/5047)
 +* [`unnecessary_unwrap`] [#5132](https://github.com/rust-lang/rust-clippy/pull/5132)
 +* [`zero_prefixed_literal`] [#5170](https://github.com/rust-lang/rust-clippy/pull/5170)
 +* [`missing_const_for_fn`] [#5216](https://github.com/rust-lang/rust-clippy/pull/5216)
 +
 +### Suggestion Improvements
 +
 +* Improve suggestion when blocks of code are suggested [#5134](https://github.com/rust-lang/rust-clippy/pull/5134)
 +
 +### ICE Fixes
 +
 +* `misc_early` lints [#5129](https://github.com/rust-lang/rust-clippy/pull/5129)
 +* [`missing_errors_doc`] [#5213](https://github.com/rust-lang/rust-clippy/pull/5213)
 +* Fix ICE when evaluating `usize`s [#5256](https://github.com/rust-lang/rust-clippy/pull/5256)
 +
 +### Documentation
 +
 +* Improve documentation of [`iter_nth_zero`]
 +* Add documentation pages for stable releases [#5171](https://github.com/rust-lang/rust-clippy/pull/5171)
 +
 +### Others
 +
 +* Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190)
 +
 +
 +## Rust 1.42
 +
 +Released 2020-03-12
 +
 +[69f99e7...4ee1206](https://github.com/rust-lang/rust-clippy/compare/69f99e7...4ee1206)
 +
 +### New lints
 +
 +* [`filetype_is_file`] [#4543](https://github.com/rust-lang/rust-clippy/pull/4543)
 +* [`let_underscore_must_use`] [#4823](https://github.com/rust-lang/rust-clippy/pull/4823)
 +* [`modulo_arithmetic`] [#4867](https://github.com/rust-lang/rust-clippy/pull/4867)
 +* [`mem_replace_with_default`] [#4881](https://github.com/rust-lang/rust-clippy/pull/4881)
 +* [`mutable_key_type`] [#4885](https://github.com/rust-lang/rust-clippy/pull/4885)
 +* [`option_as_ref_deref`] [#4945](https://github.com/rust-lang/rust-clippy/pull/4945)
 +* [`wildcard_in_or_patterns`] [#4960](https://github.com/rust-lang/rust-clippy/pull/4960)
 +* [`iter_nth_zero`] [#4966](https://github.com/rust-lang/rust-clippy/pull/4966)
 +* `invalid_atomic_ordering` [#4999](https://github.com/rust-lang/rust-clippy/pull/4999)
 +* [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067)
 +
 +### Moves and Deprecations
 +
 +* Move [`transmute_float_to_int`] from nursery to complexity group
 +  [#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
 +* Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
 +* Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
 +
 +### Enhancements
 +
 +* Lint vectored IO in [`unused_io_amount`] [#5027](https://github.com/rust-lang/rust-clippy/pull/5027)
 +* Make [`vec_box`] configurable by adding a size threshold [#5081](https://github.com/rust-lang/rust-clippy/pull/5081)
 +* Also lint constants in [`cmp_nan`] [#4910](https://github.com/rust-lang/rust-clippy/pull/4910)
 +* Fix false negative in [`expect_fun_call`] [#4915](https://github.com/rust-lang/rust-clippy/pull/4915)
 +* Fix false negative in [`redundant_clone`] [#5017](https://github.com/rust-lang/rust-clippy/pull/5017)
 +
 +### False Positive Fixes
 +
 +* [`map_clone`] [#4937](https://github.com/rust-lang/rust-clippy/pull/4937)
 +* [`replace_consts`] [#4977](https://github.com/rust-lang/rust-clippy/pull/4977)
 +* [`let_and_return`] [#5008](https://github.com/rust-lang/rust-clippy/pull/5008)
 +* [`eq_op`] [#5079](https://github.com/rust-lang/rust-clippy/pull/5079)
 +* [`possible_missing_comma`] [#5083](https://github.com/rust-lang/rust-clippy/pull/5083)
 +* [`debug_assert_with_mut_call`] [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Don't trigger [`let_underscore_must_use`] in external macros
 +  [#5082](https://github.com/rust-lang/rust-clippy/pull/5082)
 +* Don't trigger [`empty_loop`] in `no_std` crates [#5086](https://github.com/rust-lang/rust-clippy/pull/5086)
 +
 +### Suggestion Improvements
 +
 +* `option_map_unwrap_or` [#4634](https://github.com/rust-lang/rust-clippy/pull/4634)
 +* [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
 +* [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
 +* [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
 +* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
 +* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
 +* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
 +* `if_let_some_result` [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
 +
 +### ICE fixes
 +
 +* [`unsound_collection_transmute`] [#4975](https://github.com/rust-lang/rust-clippy/pull/4975)
 +
 +### Documentation
 +
 +* Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`]
 +
 +
 +## Rust 1.41
 +
 +Released 2020-01-30
 +
 +[c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7)
 +
 +* New Lints:
 +  * [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697)
 +  * [`to_digit_is_some`] [#4801](https://github.com/rust-lang/rust-clippy/pull/4801)
 +  * [`tabs_in_doc_comments`] [#4806](https://github.com/rust-lang/rust-clippy/pull/4806)
 +  * [`large_stack_arrays`] [#4807](https://github.com/rust-lang/rust-clippy/pull/4807)
 +  * [`same_functions_in_if_condition`] [#4814](https://github.com/rust-lang/rust-clippy/pull/4814)
 +  * [`zst_offset`] [#4816](https://github.com/rust-lang/rust-clippy/pull/4816)
 +  * [`as_conversions`] [#4821](https://github.com/rust-lang/rust-clippy/pull/4821)
 +  * [`missing_errors_doc`] [#4884](https://github.com/rust-lang/rust-clippy/pull/4884)
 +  * [`transmute_float_to_int`] [#4889](https://github.com/rust-lang/rust-clippy/pull/4889)
 +* Remove plugin interface, see
 +  [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
 +  details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
 +* Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
 +* Deprecate `into_iter_on_array` [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
 +* Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
 +  [#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
 +* Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
 +* Fix false positive in `while_immutable_condition` [#4730](https://github.com/rust-lang/rust-clippy/pull/4730)
 +* Fix false positive in `explicit_counter_loop` [#4803](https://github.com/rust-lang/rust-clippy/pull/4803)
 +* Fix false positive in `must_use_candidate` [#4794](https://github.com/rust-lang/rust-clippy/pull/4794)
 +* Fix false positive in `print_with_newline` and `write_with_newline`
 +  [#4769](https://github.com/rust-lang/rust-clippy/pull/4769)
 +* Fix false positive in `derive_hash_xor_eq` [#4766](https://github.com/rust-lang/rust-clippy/pull/4766)
 +* Fix false positive in `missing_inline_in_public_items` [#4870](https://github.com/rust-lang/rust-clippy/pull/4870)
 +* Fix false positive in `string_add` [#4880](https://github.com/rust-lang/rust-clippy/pull/4880)
 +* Fix false positive in `float_arithmetic` [#4851](https://github.com/rust-lang/rust-clippy/pull/4851)
 +* Fix false positive in `cast_sign_loss` [#4883](https://github.com/rust-lang/rust-clippy/pull/4883)
 +* Fix false positive in `manual_swap` [#4877](https://github.com/rust-lang/rust-clippy/pull/4877)
 +* Fix ICEs occurring while checking some block expressions [#4772](https://github.com/rust-lang/rust-clippy/pull/4772)
 +* Fix ICE in `use_self` [#4776](https://github.com/rust-lang/rust-clippy/pull/4776)
 +* Fix ICEs related to `const_generics` [#4780](https://github.com/rust-lang/rust-clippy/pull/4780)
 +* Display help when running `clippy-driver` without arguments, instead of ICEing
 +  [#4810](https://github.com/rust-lang/rust-clippy/pull/4810)
 +* Clippy has its own ICE message now [#4588](https://github.com/rust-lang/rust-clippy/pull/4588)
 +* Show deprecated lints in the documentation again [#4757](https://github.com/rust-lang/rust-clippy/pull/4757)
 +* Improve Documentation by adding positive examples to some lints
 +  [#4832](https://github.com/rust-lang/rust-clippy/pull/4832)
 +
 +## Rust 1.40
 +
 +Released 2019-12-19
 +
 +[4e7e71b...c8e3cfb](https://github.com/rust-lang/rust-clippy/compare/4e7e71b...c8e3cfb)
 +
 +* New Lints:
 +  * [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537)
 +  * [`needless_doctest_main`] [#4603](https://github.com/rust-lang/rust-clippy/pull/4603)
 +  * [`suspicious_unary_op_formatting`] [#4615](https://github.com/rust-lang/rust-clippy/pull/4615)
 +  * [`debug_assert_with_mut_call`] [#4680](https://github.com/rust-lang/rust-clippy/pull/4680)
 +  * [`unused_self`] [#4619](https://github.com/rust-lang/rust-clippy/pull/4619)
 +  * [`inefficient_to_string`] [#4683](https://github.com/rust-lang/rust-clippy/pull/4683)
 +  * [`must_use_unit`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`must_use_candidate`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`double_must_use`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`comparison_chain`] [#4569](https://github.com/rust-lang/rust-clippy/pull/4569)
 +  * [`unsound_collection_transmute`] [#4592](https://github.com/rust-lang/rust-clippy/pull/4592)
 +  * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `option_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `result_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +* Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736)
 +* Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613)
 +* Expand `integer_arithmetic` to also detect mutating arithmetic like `+=` [#4585](https://github.com/rust-lang/rust-clippy/pull/4585)
 +* Fix false positive in `nonminimal_bool` [#4568](https://github.com/rust-lang/rust-clippy/pull/4568)
 +* Fix false positive in `missing_safety_doc` [#4611](https://github.com/rust-lang/rust-clippy/pull/4611)
 +* Fix false positive in `cast_sign_loss` [#4614](https://github.com/rust-lang/rust-clippy/pull/4614)
 +* Fix false positive in `redundant_clone` [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Fix false positive in `try_err` [#4721](https://github.com/rust-lang/rust-clippy/pull/4721)
 +* Fix false positive in `toplevel_ref_arg` [#4570](https://github.com/rust-lang/rust-clippy/pull/4570)
 +* Fix false positive in `multiple_inherent_impl` [#4593](https://github.com/rust-lang/rust-clippy/pull/4593)
 +* Improve more suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4575](https://github.com/rust-lang/rust-clippy/pull/4575)
 +* Improve suggestion for `zero_ptr` [#4599](https://github.com/rust-lang/rust-clippy/pull/4599)
 +* Improve suggestion for `explicit_counter_loop` [#4691](https://github.com/rust-lang/rust-clippy/pull/4691)
 +* Improve suggestion for `mul_add` [#4602](https://github.com/rust-lang/rust-clippy/pull/4602)
 +* Improve suggestion for `assertions_on_constants` [#4635](https://github.com/rust-lang/rust-clippy/pull/4635)
 +* Fix ICE in `use_self` [#4671](https://github.com/rust-lang/rust-clippy/pull/4671)
 +* Fix ICE when encountering const casts [#4590](https://github.com/rust-lang/rust-clippy/pull/4590)
 +
 +## Rust 1.39
 +
 +Released 2019-11-07
 +
 +[3aea860...4e7e71b](https://github.com/rust-lang/rust-clippy/compare/3aea860...4e7e71b)
 +
 +* New Lints:
 +  * [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479)
 +  * [`flat_map_identity`] [#4231](https://github.com/rust-lang/rust-clippy/pull/4231)
 +  * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535)
 +  * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511)
 +  * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394)
 +  * `option_and_then_some` [#4386](https://github.com/rust-lang/rust-clippy/pull/4386)
 +  * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498)
 +* Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348)
 +* Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403)
 +* Move `cast_lossless` to pedantic group [#4539](https://github.com/rust-lang/rust-clippy/pull/4539)
 +* `temporary_cstring_as_ptr` now catches more cases [#4425](https://github.com/rust-lang/rust-clippy/pull/4425)
 +* `use_self` now works in constructors, too [#4525](https://github.com/rust-lang/rust-clippy/pull/4525)
 +* `cargo_common_metadata` now checks for license files [#4518](https://github.com/rust-lang/rust-clippy/pull/4518)
 +* `cognitive_complexity` now includes the measured complexity in the warning message [#4469](https://github.com/rust-lang/rust-clippy/pull/4469)
 +* Fix false positives in `block_in_if_*` lints [#4458](https://github.com/rust-lang/rust-clippy/pull/4458)
 +* Fix false positive in `cast_lossless` [#4473](https://github.com/rust-lang/rust-clippy/pull/4473)
 +* Fix false positive in `clone_on_copy` [#4411](https://github.com/rust-lang/rust-clippy/pull/4411)
 +* Fix false positive in `deref_addrof` [#4487](https://github.com/rust-lang/rust-clippy/pull/4487)
 +* Fix false positive in `too_many_lines` [#4490](https://github.com/rust-lang/rust-clippy/pull/4490)
 +* Fix false positive in `new_ret_no_self` [#4365](https://github.com/rust-lang/rust-clippy/pull/4365)
 +* Fix false positive in `manual_swap` [#4478](https://github.com/rust-lang/rust-clippy/pull/4478)
 +* Fix false positive in `missing_const_for_fn` [#4450](https://github.com/rust-lang/rust-clippy/pull/4450)
 +* Fix false positive in `extra_unused_lifetimes` [#4477](https://github.com/rust-lang/rust-clippy/pull/4477)
 +* Fix false positive in `inherent_to_string` [#4460](https://github.com/rust-lang/rust-clippy/pull/4460)
 +* Fix false positive in `map_entry` [#4495](https://github.com/rust-lang/rust-clippy/pull/4495)
 +* Fix false positive in `unused_unit` [#4445](https://github.com/rust-lang/rust-clippy/pull/4445)
 +* Fix false positive in `redundant_pattern` [#4489](https://github.com/rust-lang/rust-clippy/pull/4489)
 +* Fix false positive in `wrong_self_convention` [#4369](https://github.com/rust-lang/rust-clippy/pull/4369)
 +* Improve various suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4558](https://github.com/rust-lang/rust-clippy/pull/4558)
 +* Improve suggestions for `redundant_pattern_matching` [#4352](https://github.com/rust-lang/rust-clippy/pull/4352)
 +* Improve suggestions for `explicit_write` [#4544](https://github.com/rust-lang/rust-clippy/pull/4544)
 +* Improve suggestion for `or_fun_call` [#4522](https://github.com/rust-lang/rust-clippy/pull/4522)
 +* Improve suggestion for `match_as_ref` [#4446](https://github.com/rust-lang/rust-clippy/pull/4446)
 +* Improve suggestion for `unnecessary_fold_span` [#4382](https://github.com/rust-lang/rust-clippy/pull/4382)
 +* Add suggestions for `unseparated_literal_suffix` [#4401](https://github.com/rust-lang/rust-clippy/pull/4401)
 +* Add suggestions for `char_lit_as_u8` [#4418](https://github.com/rust-lang/rust-clippy/pull/4418)
 +
 +## Rust 1.38
 +
 +Released 2019-09-26
 +
 +[e3cb40e...3aea860](https://github.com/rust-lang/rust-clippy/compare/e3cb40e...3aea860)
 +
 +* New Lints:
 +  * [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203)
 +  * [`inherent_to_string`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`inherent_to_string_shadow_display`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`type_repetition_in_bounds`] [#3766](https://github.com/rust-lang/rust-clippy/pull/3766)
 +  * [`try_err`] [#4222](https://github.com/rust-lang/rust-clippy/pull/4222)
- [adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
++* 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
 +[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
 +[`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore
 +[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
 +[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
 +[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
 +[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
 +[`await_holding_invalid_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type
 +[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
 +[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
 +[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
 +[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
 +[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
 +[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
 +[`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr
 +[`block_in_if_condition_stmt`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_stmt
 +[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
 +[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
 +[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
 +[`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr
 +[`borrow_deref_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_deref_ref
 +[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
 +[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
 +[`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
 +[`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec
 +[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
 +[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
 +[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
 +[`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len
 +[`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
 +[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
 +[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
 +[`cast_abs_to_unsigned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned
 +[`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor
 +[`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation
 +[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
 +[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
 +[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
 +[`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss
 +[`cast_ptr_alignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ptr_alignment
 +[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
 +[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
 +[`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes
 +[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
 +[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
 +[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
 +[`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions
 +[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
 +[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
 +[`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
 +[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied
 +[`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
 +[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
 +[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
 +[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
 +[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
 +[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
 +[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
 +[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
 +[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
 +[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
 +[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
 +[`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def
 +[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
 +[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
 +[`cyclomatic_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cyclomatic_complexity
 +[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
 +[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
 +[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
 +[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
 +[`default_instead_of_iter_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_instead_of_iter_empty
 +[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
 +[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
 +[`default_union_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_union_representation
 +[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
 +[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
 +[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
 +[`deref_by_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_by_slicing
 +[`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls
 +[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
 +[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
 +[`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq
 +[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
 +[`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods
 +[`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents
 +[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
 +[`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
 +[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
 +[`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes
 +[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
 +[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
 +[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
 +[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
 +[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
 +[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
 +[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
 +[`drop_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_non_drop
 +[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
 +[`duplicate_mod`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_mod
 +[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
 +[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
 +[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
 +[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop
 +[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
 +[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
 +[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
 +[`empty_structs_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_structs_with_brackets
 +[`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant
 +[`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use
 +[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
 +[`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op
 +[`equatable_if_let`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_if_let
 +[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
 +[`err_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#err_expect
 +[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
 +[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
 +[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
 +[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs
 +[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
 +[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
 +[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used
 +[`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy
 +[`explicit_auto_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_auto_deref
 +[`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop
 +[`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods
 +[`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop
 +[`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop
 +[`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write
 +[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
 +[`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain
 +[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
 +[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
 +[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
 +[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
 +[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
 +[`filter_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_identity
 +[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
 +[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
 +[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
 +[`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
 +[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option
 +[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
 +[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
 +[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
 +[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs
 +[`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons
 +[`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools
 +[`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast
 +[`fn_to_numeric_cast_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_any
 +[`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation
 +[`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map
 +[`for_loop_over_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_option
 +[`for_loop_over_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_result
 +[`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles
 +[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
 +[`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop
 +[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
 +[`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args
 +[`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string
 +[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
 +[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
 +[`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10
 +[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
 +[`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first
 +[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
 +[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
 +[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion
 +[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
 +[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
 +[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
 +[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
 +[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
 +[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
 +[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
 +[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
 +[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
 +[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
 +[`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
 +[`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
 +[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
 +[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
 +[`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
 +[`index_refutable_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice
 +[`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
 +[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
 +[`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
 +[`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
 +[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
 +[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
 +[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
 +[`init_numbered_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#init_numbered_fields
 +[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
 +[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax
 +[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax
 +[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
 +[`inspect_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#inspect_for_each
 +[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
 +[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
 +[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
 +[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
 +[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
 +[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
 +[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
 +[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
 +[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
 +[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
++[`invalid_utf8_in_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_utf8_in_unchecked
 +[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
 +[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
 +[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
 +[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
 +[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
 +[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
 +[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice
 +[`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator
 +[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
 +[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
 +[`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
 +[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
 +[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
 +[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
 +[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
 +[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
 +[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
 +[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
 +[`large_include_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file
 +[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
 +[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
 +[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
 +[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
 +[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
 +[`let_underscore_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop
 +[`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock
 +[`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
 +[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
 +[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
 +[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
 +[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
 +[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
 +[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
 +[`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
 +[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
 +[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits
 +[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
 +[`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find
 +[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
 +[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
 +[`manual_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_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
 +[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
 +[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
 +[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
 +[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
 +[`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit
 +[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
 +[`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore
 +[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
 +[`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity
 +[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or
 +[`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
 +[`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
 +[`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
 +[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
 +[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
 +[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
 +[`match_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_result_ok
 +[`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
 +[`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
 +[`match_str_case_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_str_case_mismatch
 +[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
 +[`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants
 +[`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter
 +[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
 +[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
 +[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
 +[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default
 +[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
 +[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
 +[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
 +[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
 +[`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order
 +[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
 +[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
 +[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
 +[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
 +[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
 +[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
 +[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
 +[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
 +[`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop
 +[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
 +[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
 +[`mixed_read_write_in_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_read_write_in_expression
 +[`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files
 +[`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
 +[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
 +[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
 +[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
 +[`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
 +[`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
 +[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
 +[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
 +[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
 +[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
 +[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
 +[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
 +[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
 +[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
 +[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
 +[`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl
 +[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
 +[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr
 +[`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
 +[`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
 +[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
 +[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
 +[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr
 +[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
 +[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
 +[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
 +[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
 +[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
 +[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
 +[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
 +[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
 +[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
 +[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
 +[`rc_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_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_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
 +[`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref
 +[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
 +[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
 +[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
 +[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
 +[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
 +[`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
 +[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
 +[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
 +[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
 +[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
 +[`trailing_empty_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#trailing_empty_array
 +[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds
 +[`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str
 +[`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int
 +[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
 +[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
 +[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
 +[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
 +[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
 +[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
 +[`transmute_undefined_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_undefined_repr
 +[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
 +[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
 +[`trim_split_whitespace`]: https://rust-lang.github.io/rust-clippy/master/index.html#trim_split_whitespace
 +[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
 +[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
 +[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
 +[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
 +[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
 +[`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks
 +[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops
 +[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
 +[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
 +[`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init
 +[`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec
 +[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
 +[`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp
 +[`unit_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash
 +[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
 +[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
 +[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
 +[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
 +[`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map
 +[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
 +[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
 +[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
 +[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
 +[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
 +[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
 +[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
 +[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
 +[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned
 +[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
 +[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
 +[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
 +[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
 +[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
 +[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable
 +[`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal
 +[`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize
 +[`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name
 +[`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization
 +[`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix
 +[`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute
 +[`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice
 +[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
 +[`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async
 +[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
 +[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
 +[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
 +[`unused_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 e81e7ceedcb50fe372a273cc5ee1c2b710b28dbe,0000000000000000000000000000000000000000..6e15133d267ba04cfa304a2f7f73b909cfa4fee7
mode 100644,000000..100644
--- /dev/null
@@@ -1,249 -1,0 +1,248 @@@
-   - [Writing code](#writing-code)
 +# 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)
- ```
- { "rust-analyzer.rustcSource": "discover" }
 +  - [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.
 +
 +<!-- FIXME: Link to the deployed book, once it is deployed through CI -->
 +[Clippy book]: book/src
 +
 +## 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
 +As of [#6869][6869], [`rust-analyzer`][ra_homepage] can understand that Clippy uses compiler-internals
 +using `extern crate` when `package.metadata.rust-analyzer.rustc_private` is set to `true` in Clippy's `Cargo.toml.`
 +You will require a `nightly` toolchain with the `rustc-dev` component installed.
 +Make sure that in the `rust-analyzer` configuration, you set
- ```
++```json
++{ "rust-analyzer.rustc.source": "discover" }
 +```
 +and
++```json
 +{ "rust-analyzer.updates.channel": "nightly" }
 +```
 +You should be able to see information on things like `Expr` or `EarlyContext` now if you hover them, also
 +a lot more type hints.
 +This will work with `rust-analyzer 2021-03-15` shipped in nightly `1.52.0-nightly (107896c32 2021-03-15)` or later.
 +
 +[ra_homepage]: https://rust-analyzer.github.io/
 +[6869]: https://github.com/rust-lang/rust-clippy/pull/6869
 +
 +## 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 b652194d0d13b25ec38c99d56460923b5ab7b02f,0000000000000000000000000000000000000000..6d67f80ff256520d73cfe2f5198485d6e07a329c
mode 100644,000000..100644
--- /dev/null
@@@ -1,4 -1,0 +1,4 @@@
- [book](src/infrastructure/book.md) for more information.
 +# Clippy Book
 +
 +This is the source for the Clippy Book. See the
++[book](src/development/infrastructure/book.md) for more information.
index 605897ff49cdb4dc42d468e2976cef59e7f8d9d2,0000000000000000000000000000000000000000..44ba6e32755e319b24d5797c0b95c42baddaad20
mode 100644,000000..100644
--- /dev/null
@@@ -1,188 -1,0 +1,192 @@@
- [Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
- [Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md
 +# Basics for hacking on Clippy
 +
 +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].
 +
- cargo test --test dogfood
++[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
 +```
 +
 +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
 +[here](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 e560f4c6a3e51d5b752af5b7c4479509469ac5f0,0000000000000000000000000000000000000000..80a47affe30d0c5eae3cfd392e527f129b59b4bb
mode 100644,000000..100644
--- /dev/null
@@@ -1,102 -1,0 +1,105 @@@
 +# Changelog Update
 +
 +If you want to help with updating the [changelog], you're in the right place.
 +
 +## When to update
 +
 +Typos and other small fixes/additions are _always_ welcome.
 +
 +Special care needs to be taken when it comes to updating the changelog for a new
 +Rust release. For that purpose, the changelog is ideally updated during the week
 +before an upcoming stable release. You can find the release dates on the [Rust
 +Forge][forge].
 +
 +Most of the time we only need to update the changelog for minor Rust releases.
 +It's been very rare that Clippy changes were included in a patch release.
 +
 +## Changelog update walkthrough
 +
 +### 1. Finding the relevant Clippy commits
 +
 +Each Rust release ships with its own version of Clippy. The Clippy subtree can
 +be found in the `tools` directory of the Rust repository.
 +
 +Depending on the current time and what exactly you want to update, the following
 +bullet points might be helpful:
 +
 +* When writing the release notes for the **upcoming stable release** you need to
 +  check out the Clippy commit of the current Rust `beta` branch.
 +  [Link][rust_beta_tools]
 +* When writing the release notes for the **upcoming beta release**, you need to
 +  check out the Clippy commit of the current Rust `master`.
 +  [Link][rust_master_tools]
 +* When writing the (forgotten) release notes for a **past stable release**, you
 +  need to check out the Rust release tag of the stable release.
 +  [Link][rust_stable_tools]
 +
 +Usually you want to write the changelog of the **upcoming stable release**. Make
 +sure though, that `beta` was already branched in the Rust repository.
 +
 +To find the commit hash, issue the following command when in a `rust-lang/rust`
 +checkout:
 +```
 +git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g"
 +```
 +
 +### 2. Fetching the PRs between those commits
 +
 +Once you've got the correct commit range, run
 +
 +```
 +util/fetch_prs_between.sh commit1 commit2 > changes.txt
 +```
 +
 +and open that file in your editor of choice.
 +
 +When updating the changelog it's also a good idea to make sure that `commit1` is
 +already correct in the current changelog.
 +
 +### 3. Authoring the final changelog
 +
 +The above script should have dumped all the relevant PRs to the file you
 +specified. It should have filtered out most of the irrelevant PRs already, but
 +it's a good idea to do a manual cleanup pass where you look for more irrelevant
 +PRs. If you're not sure about some PRs, just leave them in for the review and
 +ask for feedback.
 +
 +With the PRs filtered, you can start to take each PR and move the `changelog: `
 +content to `CHANGELOG.md`. Adapt the wording as you see fit but try to keep it
 +somewhat coherent.
 +
 +The order should roughly be:
 +
 +1. New lints
 +2. Moves or deprecations of lints
 +3. Changes that expand what code existing lints cover
 +4. False positive fixes
 +5. Suggestion fixes/improvements
 +6. ICE fixes
 +7. Documentation improvements
 +8. Others
 +
 +As section headers, we use:
 +
 +```
 +### New Lints
 +### Moves and Deprecations
 +### Enhancements
 +### False Positive Fixes
 +### Suggestion Fixes/Improvements
 +### ICE Fixes
 +### Documentation Improvements
 +### Others
 +```
 +
 +Please also be sure to update the Beta/Unreleased sections at the top with the
 +relevant commit ranges.
 +
++If you have the time, it would be appreciated if you double-check, that the
++`#[clippy::version]` attributes for the added lints contains the correct version.
++
 +[changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md
 +[forge]: https://forge.rust-lang.org/
 +[rust_master_tools]: https://github.com/rust-lang/rust/tree/master/src/tools/clippy
 +[rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools/clippy
 +[rust_stable_tools]: https://github.com/rust-lang/rust/releases
index 5d858e0da46898210058e2e678697b1f56081417,0000000000000000000000000000000000000000..61a90445d753c6b6f3d5b6be7a0ebf4b7e6d9150
mode 100644,000000..100644
--- /dev/null
@@@ -1,151 -1,0 +1,151 @@@
- For a full list of all lints with their description and examples, please refere
 +# Usage
 +
 +This chapter describes how to use Clippy to get the most out of it. Clippy can
 +be used as a `cargo` subcommand or, like `rustc`, directly with the
 +`clippy-driver` binary.
 +
 +> _Note:_ This chapter assumes that you have Clippy installed already. If you're
 +> not sure, take a look at the [Installation] chapter.
 +
 +## Cargo subcommand
 +
 +The easiest and most common way to run Clippy is through `cargo`. To do that,
 +just run
 +
 +```bash
 +cargo clippy
 +```
 +
 +### Lint configuration
 +
 +The above command will run the default set of lints, which are included in the
 +lint group `clippy::all`. You might want to use even more lints or you might not
 +agree with every Clippy lint, and for that there are ways to configure lint
 +levels.
 +
 +> _Note:_ Clippy is meant to be used with a generous sprinkling of
 +> `#[allow(..)]`s through your code. So if you disagree with a lint, don't feel
 +> bad disabling them for parts of your code or the whole project.
 +
 +#### Command line
 +
 +You can configure lint levels on the command line by adding
 +`-A/W/D clippy::lint_name` like this:
 +
 +```bash
 +cargo clippy -- -Aclippy::style -Wclippy::double_neg -Dclippy::perf
 +```
 +
 +For [CI] all warnings can be elevated to errors which will inturn fail
 +the build and cause Clippy to exit with a code other than `0`.
 +
 +```
 +cargo clippy -- -Dwarnings
 +```
 +
 +> _Note:_ Adding `-D warnings` will cause your build to fail if **any** warnings
 +> are found in your code. That includes warnings found by rustc (e.g.
 +> `dead_code`, etc.).
 +
 +For more information on configuring lint levels, see the [rustc documentation].
 +
 +[rustc documentation]: https://doc.rust-lang.org/rustc/lints/levels.html#configuring-warning-levels
 +
 +#### Even more lints
 +
 +Clippy has lint groups which are allow-by-default. This means, that you will
 +have to enable the lints in those groups manually.
 +
++For a full list of all lints with their description and examples, please refer
 +to [Clippy's lint list]. The two most important allow-by-default groups are
 +described below:
 +
 +[Clippy's lint list]: https://rust-lang.github.io/rust-clippy/master/index.html
 +
 +##### `clippy::pedantic`
 +
 +The first group is the `pedantic` group. This group contains really opinionated
 +lints, that may have some intentional false positives in order to prevent false
 +negatives. So while this group is ready to be used in production, you can expect
 +to sprinkle multiple `#[allow(..)]`s in your code. If you find any false
 +positives, you're still welcome to report them to us for future improvements.
 +
 +> FYI: Clippy uses the whole group to lint itself.
 +
 +##### `clippy::restriction`
 +
 +The second group is the `restriction` group. This group contains lints that
 +"restrict" the language in some way. For example the `clippy::unwrap` lint from
 +this group won't allow you to use `.unwrap()` in your code. You may want to look
 +through the lints in this group and enable the ones that fit your need.
 +
 +> _Note:_ You shouldn't enable the whole lint group, but cherry-pick lints from
 +> this group. Some lints in this group will even contradict other Clippy lints!
 +
 +#### Too many lints
 +
 +The most opinionated warn-by-default group of Clippy is the `clippy::style`
 +group. Some people prefer to disable this group completely and then cherry-pick
 +some lints they like from this group. The same is of course possible with every
 +other of Clippy's lint groups.
 +
 +> _Note:_ We try to keep the warn-by-default groups free from false positives
 +> (FP). If you find that a lint wrongly triggers, please report it in an issue
 +> (if there isn't an issue for that FP already)
 +
 +#### Source Code
 +
 +You can configure lint levels in source code the same way you can configure
 +`rustc` lints:
 +
 +```rust
 +#![allow(clippy::style)]
 +
 +#[warn(clippy::double_neg)]
 +fn main() {
 +    let x = 1;
 +    let y = --x;
 +    //      ^^ warning: double negation
 +}
 +```
 +
 +### Automatically applying Clippy suggestions
 +
 +Clippy can automatically apply some lint suggestions, just like the compiler.
 +
 +```terminal
 +cargo clippy --fix
 +```
 +
 +### Workspaces
 +
 +All the usual workspace options should work with Clippy. For example the
 +following command will run Clippy on the `example` crate in your workspace:
 +
 +```terminal
 +cargo clippy -p example
 +```
 +
 +As with `cargo check`, this includes dependencies that are members of the
 +workspace, like path dependencies. If you want to run Clippy **only** on the
 +given crate, use the `--no-deps` option like this:
 +
 +```terminal
 +cargo clippy -p example -- --no-deps
 +```
 +
 +## Using Clippy without `cargo`: `clippy-driver`
 +
 +Clippy can also be used in projects that do not use cargo. To do so, run
 +`clippy-driver` with the same arguments you use for `rustc`. For example:
 +
 +```terminal
 +clippy-driver --edition 2018 -Cpanic=abort foo.rs
 +```
 +
 +> _Note:_ `clippy-driver` is designed for running Clippy and should not be used
 +> as a general replacement for `rustc`. `clippy-driver` may produce artifacts
 +> that are not optimized as expected, for example.
 +
 +[Installation]: installation.md
 +[CI]: continuous_integration/index.md
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b69e9f649ec78c1c26f85d9b93f64b635b9ae9d8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++use crate::clippy_project_root;
++use std::process::Command;
++
++/// # Panics
++///
++/// Panics if unable to run the dogfood test
++pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool) {
++    let mut cmd = Command::new("cargo");
++
++    cmd.current_dir(clippy_project_root())
++        .args(["test", "--test", "dogfood"])
++        .args(["--features", "internal"])
++        .args(["--", "dogfood_clippy"]);
++
++    let mut dogfood_args = Vec::new();
++    if fix {
++        dogfood_args.push("--fix");
++    }
++
++    if allow_dirty {
++        dogfood_args.push("--allow-dirty");
++    }
++
++    if allow_staged {
++        dogfood_args.push("--allow-staged");
++    }
++
++    cmd.env("__CLIPPY_DOGFOOD_ARGS", dogfood_args.join(" "));
++
++    let output = cmd.output().expect("failed to run command");
++
++    println!("{}", String::from_utf8_lossy(&output.stdout));
++}
index fe69284ab10e422cc443dc618a5ac235b42ddc6a,0000000000000000000000000000000000000000..8536e2429926a6b9b726b4498db635cfd64a2b1d
mode 100644,000000..100644
--- /dev/null
@@@ -1,57 -1,0 +1,58 @@@
 +#![cfg_attr(bootstrap, feature(let_chains))]
 +#![feature(let_else)]
 +#![feature(once_cell)]
 +#![feature(rustc_private)]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +extern crate rustc_lexer;
 +
 +use std::path::PathBuf;
 +
 +pub mod bless;
++pub mod dogfood;
 +pub mod fmt;
 +pub mod lint;
 +pub mod new_lint;
 +pub mod serve;
 +pub mod setup;
 +pub mod update_lints;
 +
 +#[cfg(not(windows))]
 +static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
 +#[cfg(windows)]
 +static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe";
 +
 +/// Returns the path to the `cargo-clippy` binary
 +#[must_use]
 +pub fn cargo_clippy_path() -> PathBuf {
 +    let mut path = std::env::current_exe().expect("failed to get current executable name");
 +    path.set_file_name(CARGO_CLIPPY_EXE);
 +    path
 +}
 +
 +/// Returns the path to the Clippy project directory
 +///
 +/// # Panics
 +///
 +/// Panics if the current directory could not be retrieved, there was an error reading any of the
 +/// Cargo.toml files or ancestor directory is the clippy root directory
 +#[must_use]
 +pub fn clippy_project_root() -> PathBuf {
 +    let current_dir = std::env::current_dir().unwrap();
 +    for path in current_dir.ancestors() {
 +        let result = std::fs::read_to_string(path.join("Cargo.toml"));
 +        if let Err(err) = &result {
 +            if err.kind() == std::io::ErrorKind::NotFound {
 +                continue;
 +            }
 +        }
 +
 +        let content = result.unwrap();
 +        if content.contains("[package]\nname = \"clippy\"") {
 +            return path.to_path_buf();
 +        }
 +    }
 +    panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
 +}
index 243a901503f160f617718e30e16b2242ab99ace6,0000000000000000000000000000000000000000..a29ba2d0c85e34c4bf62da43a655381955dab95d
mode 100644,000000..100644
--- /dev/null
@@@ -1,289 -1,0 +1,307 @@@
- use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints};
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use clap::{Arg, ArgAction, ArgMatches, Command, PossibleValue};
++use clippy_dev::{bless, dogfood, fmt, lint, new_lint, serve, setup, update_lints};
 +use indoc::indoc;
 +
 +fn main() {
 +    let matches = get_clap_config();
 +
 +    match matches.subcommand() {
 +        Some(("bless", matches)) => {
 +            bless::bless(matches.contains_id("ignore-timestamp"));
 +        },
++        Some(("dogfood", matches)) => {
++            dogfood::dogfood(
++                matches.contains_id("fix"),
++                matches.contains_id("allow-dirty"),
++                matches.contains_id("allow-staged"),
++            );
++        },
 +        Some(("fmt", matches)) => {
 +            fmt::run(matches.contains_id("check"), matches.contains_id("verbose"));
 +        },
 +        Some(("update_lints", matches)) => {
 +            if matches.contains_id("print-only") {
 +                update_lints::print_lints();
 +            } else if matches.contains_id("check") {
 +                update_lints::update(update_lints::UpdateMode::Check);
 +            } else {
 +                update_lints::update(update_lints::UpdateMode::Change);
 +            }
 +        },
 +        Some(("new_lint", matches)) => {
 +            match new_lint::create(
 +                matches.get_one::<String>("pass"),
 +                matches.get_one::<String>("name"),
 +                matches.get_one::<String>("category"),
 +                matches.contains_id("msrv"),
 +            ) {
 +                Ok(_) => update_lints::update(update_lints::UpdateMode::Change),
 +                Err(e) => eprintln!("Unable to create lint: {}", e),
 +            }
 +        },
 +        Some(("setup", sub_command)) => match sub_command.subcommand() {
 +            Some(("intellij", matches)) => {
 +                if matches.contains_id("remove") {
 +                    setup::intellij::remove_rustc_src();
 +                } else {
 +                    setup::intellij::setup_rustc_src(
 +                        matches
 +                            .get_one::<String>("rustc-repo-path")
 +                            .expect("this field is mandatory and therefore always valid"),
 +                    );
 +                }
 +            },
 +            Some(("git-hook", matches)) => {
 +                if matches.contains_id("remove") {
 +                    setup::git_hook::remove_hook();
 +                } else {
 +                    setup::git_hook::install_hook(matches.contains_id("force-override"));
 +                }
 +            },
 +            Some(("vscode-tasks", matches)) => {
 +                if matches.contains_id("remove") {
 +                    setup::vscode::remove_tasks();
 +                } else {
 +                    setup::vscode::install_tasks(matches.contains_id("force-override"));
 +                }
 +            },
 +            _ => {},
 +        },
 +        Some(("remove", sub_command)) => match sub_command.subcommand() {
 +            Some(("git-hook", _)) => setup::git_hook::remove_hook(),
 +            Some(("intellij", _)) => setup::intellij::remove_rustc_src(),
 +            Some(("vscode-tasks", _)) => setup::vscode::remove_tasks(),
 +            _ => {},
 +        },
 +        Some(("serve", matches)) => {
 +            let port = *matches.get_one::<u16>("port").unwrap();
 +            let lint = matches.get_one::<String>("lint");
 +            serve::run(port, lint);
 +        },
 +        Some(("lint", matches)) => {
 +            let path = matches.get_one::<String>("path").unwrap();
 +            let args = matches.get_many::<String>("args").into_iter().flatten();
 +            lint::run(path, args);
 +        },
 +        Some(("rename_lint", matches)) => {
 +            let old_name = matches.get_one::<String>("old_name").unwrap();
 +            let new_name = matches.get_one::<String>("new_name").unwrap_or(old_name);
 +            let uplift = matches.contains_id("uplift");
 +            update_lints::rename(old_name, new_name, uplift);
 +        },
 +        Some(("deprecate", matches)) => {
 +            let name = matches.get_one::<String>("name").unwrap();
 +            let reason = matches.get_one("reason");
 +            update_lints::deprecate(name, reason);
 +        },
 +        _ => {},
 +    }
 +}
 +
 +fn get_clap_config() -> ArgMatches {
 +    Command::new("Clippy developer tooling")
 +        .arg_required_else_help(true)
 +        .subcommands([
 +            Command::new("bless").about("bless the test output changes").arg(
 +                Arg::new("ignore-timestamp")
 +                    .long("ignore-timestamp")
 +                    .help("Include files updated before clippy was built"),
 +            ),
++            Command::new("dogfood").about("Runs the dogfood test").args([
++                Arg::new("fix").long("fix").help("Apply the suggestions when possible"),
++                Arg::new("allow-dirty")
++                    .long("allow-dirty")
++                    .help("Fix code even if the working directory has changes")
++                    .requires("fix"),
++                Arg::new("allow-staged")
++                    .long("allow-staged")
++                    .help("Fix code even if the working directory has staged changes")
++                    .requires("fix"),
++            ]),
 +            Command::new("fmt")
 +                .about("Run rustfmt on all projects and tests")
 +                .args([
 +                    Arg::new("check").long("check").help("Use the rustfmt --check option"),
 +                    Arg::new("verbose").short('v').long("verbose").help("Echo commands run"),
 +                ]),
 +            Command::new("update_lints")
 +                .about("Updates lint registration and information from the source code")
 +                .long_about(
 +                    "Makes sure that:\n \
 +                    * the lint count in README.md is correct\n \
 +                    * the changelog contains markdown link references at the bottom\n \
 +                    * all lint groups include the correct lints\n \
 +                    * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \
 +                    * all lints are registered in the lint store",
 +                )
 +                .args([
 +                    Arg::new("print-only").long("print-only").help(
 +                        "Print a table of lints to STDOUT. \
 +                        This does not include deprecated and internal lints. \
 +                        (Does not modify any files)",
 +                    ),
 +                    Arg::new("check")
 +                        .long("check")
 +                        .help("Checks that `cargo dev update_lints` has been run. Used on CI."),
 +                ]),
 +            Command::new("new_lint")
 +                .about("Create new lint and run `cargo dev update_lints`")
 +                .args([
 +                    Arg::new("pass")
 +                        .short('p')
 +                        .long("pass")
 +                        .help("Specify whether the lint runs during the early or late pass")
 +                        .takes_value(true)
 +                        .value_parser([PossibleValue::new("early"), PossibleValue::new("late")])
 +                        .required(true),
 +                    Arg::new("name")
 +                        .short('n')
 +                        .long("name")
 +                        .help("Name of the new lint in snake case, ex: fn_too_long")
 +                        .takes_value(true)
 +                        .required(true),
 +                    Arg::new("category")
 +                        .short('c')
 +                        .long("category")
 +                        .help("What category the lint belongs to")
 +                        .default_value("nursery")
 +                        .value_parser([
 +                            PossibleValue::new("style"),
 +                            PossibleValue::new("correctness"),
 +                            PossibleValue::new("suspicious"),
 +                            PossibleValue::new("complexity"),
 +                            PossibleValue::new("perf"),
 +                            PossibleValue::new("pedantic"),
 +                            PossibleValue::new("restriction"),
 +                            PossibleValue::new("cargo"),
 +                            PossibleValue::new("nursery"),
 +                            PossibleValue::new("internal"),
 +                            PossibleValue::new("internal_warn"),
 +                        ])
 +                        .takes_value(true),
 +                    Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint"),
 +                ]),
 +            Command::new("setup")
 +                .about("Support for setting up your personal development environment")
 +                .arg_required_else_help(true)
 +                .subcommands([
 +                    Command::new("intellij")
 +                        .about("Alter dependencies so Intellij Rust can find rustc internals")
 +                        .args([
 +                            Arg::new("remove")
 +                                .long("remove")
 +                                .help("Remove the dependencies added with 'cargo dev setup intellij'")
 +                                .required(false),
 +                            Arg::new("rustc-repo-path")
 +                                .long("repo-path")
 +                                .short('r')
 +                                .help("The path to a rustc repo that will be used for setting the dependencies")
 +                                .takes_value(true)
 +                                .value_name("path")
 +                                .conflicts_with("remove")
 +                                .required(true),
 +                        ]),
 +                    Command::new("git-hook")
 +                        .about("Add a pre-commit git hook that formats your code to make it look pretty")
 +                        .args([
 +                            Arg::new("remove")
 +                                .long("remove")
 +                                .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'")
 +                                .required(false),
 +                            Arg::new("force-override")
 +                                .long("force-override")
 +                                .short('f')
 +                                .help("Forces the override of an existing git pre-commit hook")
 +                                .required(false),
 +                        ]),
 +                    Command::new("vscode-tasks")
 +                        .about("Add several tasks to vscode for formatting, validation and testing")
 +                        .args([
 +                            Arg::new("remove")
 +                                .long("remove")
 +                                .help("Remove the tasks added with 'cargo dev setup vscode-tasks'")
 +                                .required(false),
 +                            Arg::new("force-override")
 +                                .long("force-override")
 +                                .short('f')
 +                                .help("Forces the override of existing vscode tasks")
 +                                .required(false),
 +                        ]),
 +                ]),
 +            Command::new("remove")
 +                .about("Support for undoing changes done by the setup command")
 +                .arg_required_else_help(true)
 +                .subcommands([
 +                    Command::new("git-hook").about("Remove any existing pre-commit git hook"),
 +                    Command::new("vscode-tasks").about("Remove any existing vscode tasks"),
 +                    Command::new("intellij").about("Removes rustc source paths added via `cargo dev setup intellij`"),
 +                ]),
 +            Command::new("serve")
 +                .about("Launch a local 'ALL the Clippy Lints' website in a browser")
 +                .args([
 +                    Arg::new("port")
 +                        .long("port")
 +                        .short('p')
 +                        .help("Local port for the http server")
 +                        .default_value("8000")
 +                        .value_parser(clap::value_parser!(u16)),
 +                    Arg::new("lint").help("Which lint's page to load initially (optional)"),
 +                ]),
 +            Command::new("lint")
 +                .about("Manually run clippy on a file or package")
 +                .after_help(indoc! {"
 +                    EXAMPLES
 +                        Lint a single file:
 +                            cargo dev lint tests/ui/attrs.rs
 +
 +                        Lint a package directory:
 +                            cargo dev lint tests/ui-cargo/wildcard_dependencies/fail
 +                            cargo dev lint ~/my-project
 +
 +                        Run rustfix:
 +                            cargo dev lint ~/my-project -- --fix
 +
 +                        Set lint levels:
 +                            cargo dev lint file.rs -- -W clippy::pedantic
 +                            cargo dev lint ~/my-project -- -- -W clippy::pedantic
 +                "})
 +                .args([
 +                    Arg::new("path")
 +                        .required(true)
 +                        .help("The path to a file or package directory to lint"),
 +                    Arg::new("args")
 +                        .action(ArgAction::Append)
 +                        .help("Pass extra arguments to cargo/clippy-driver"),
 +                ]),
 +            Command::new("rename_lint").about("Renames the given lint").args([
 +                Arg::new("old_name")
 +                    .index(1)
 +                    .required(true)
 +                    .help("The name of the lint to rename"),
 +                Arg::new("new_name")
 +                    .index(2)
 +                    .required_unless_present("uplift")
 +                    .help("The new name of the lint"),
 +                Arg::new("uplift")
 +                    .long("uplift")
 +                    .help("This lint will be uplifted into rustc"),
 +            ]),
 +            Command::new("deprecate").about("Deprecates the given lint").args([
 +                Arg::new("name")
 +                    .index(1)
 +                    .required(true)
 +                    .help("The name of the lint to deprecate"),
 +                Arg::new("reason")
 +                    .long("reason")
 +                    .short('r')
 +                    .required(false)
 +                    .takes_value(true)
 +                    .help("The reason for deprecation"),
 +            ]),
 +        ])
 +        .get_matches()
 +}
index 2e0659f42d7b6b85ea248f4e45a91513c70ed2ca,0000000000000000000000000000000000000000..c089f4d8ce4bb2c3f9757351b44fdd999f338242
mode 100644,000000..100644
--- /dev/null
@@@ -1,1275 -1,0 +1,1275 @@@
-     edited.then(|| result)
 +use crate::clippy_project_root;
 +use aho_corasick::AhoCorasickBuilder;
 +use indoc::writedoc;
 +use itertools::Itertools;
 +use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
 +use std::collections::{HashMap, HashSet};
 +use std::ffi::OsStr;
 +use std::fmt::Write;
 +use std::fs::{self, OpenOptions};
 +use std::io::{self, Read, Seek, SeekFrom, Write as _};
 +use std::ops::Range;
 +use std::path::{Path, PathBuf};
 +use walkdir::{DirEntry, WalkDir};
 +
 +const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\
 +     // Use that command to update this file and do not edit by hand.\n\
 +     // Manual edits will be overwritten.\n\n";
 +
 +const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
 +
 +#[derive(Clone, Copy, PartialEq, Eq)]
 +pub enum UpdateMode {
 +    Check,
 +    Change,
 +}
 +
 +/// Runs the `update_lints` command.
 +///
 +/// This updates various generated values from the lint source code.
 +///
 +/// `update_mode` indicates if the files should be updated or if updates should be checked for.
 +///
 +/// # Panics
 +///
 +/// Panics if a file path could not read from or then written to
 +pub fn update(update_mode: UpdateMode) {
 +    let (lints, deprecated_lints, renamed_lints) = gather_all();
 +    generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints);
 +}
 +
 +fn generate_lint_files(
 +    update_mode: UpdateMode,
 +    lints: &[Lint],
 +    deprecated_lints: &[DeprecatedLint],
 +    renamed_lints: &[RenamedLint],
 +) {
 +    let internal_lints = Lint::internal_lints(lints);
 +    let usable_lints = Lint::usable_lints(lints);
 +    let mut sorted_usable_lints = usable_lints.clone();
 +    sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
 +
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("README.md"),
 +        "[There are over ",
 +        " lints included in this crate!]",
 +        |res| {
 +            write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
 +        },
 +    );
 +
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("book/src/README.md"),
 +        "[There are over ",
 +        " lints included in this crate!]",
 +        |res| {
 +            write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
 +        },
 +    );
 +
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("CHANGELOG.md"),
 +        "<!-- begin autogenerated links to lint list -->\n",
 +        "<!-- end autogenerated links to lint list -->",
 +        |res| {
 +            for lint in usable_lints
 +                .iter()
 +                .map(|l| &*l.name)
 +                .chain(deprecated_lints.iter().map(|l| &*l.name))
 +                .chain(
 +                    renamed_lints
 +                        .iter()
 +                        .map(|l| l.old_name.strip_prefix("clippy::").unwrap_or(&l.old_name)),
 +                )
 +                .sorted()
 +            {
 +                writeln!(res, "[`{}`]: {}#{}", lint, DOCS_LINK, lint).unwrap();
 +            }
 +        },
 +    );
 +
 +    // This has to be in lib.rs, otherwise rustfmt doesn't work
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("clippy_lints/src/lib.rs"),
 +        "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n",
 +        "// end lints modules, do not remove this comment, it’s used in `update_lints`",
 +        |res| {
 +            for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() {
 +                writeln!(res, "mod {};", lint_mod).unwrap();
 +            }
 +        },
 +    );
 +
 +    process_file(
 +        "clippy_lints/src/lib.register_lints.rs",
 +        update_mode,
 +        &gen_register_lint_list(internal_lints.iter(), usable_lints.iter()),
 +    );
 +    process_file(
 +        "clippy_lints/src/lib.deprecated.rs",
 +        update_mode,
 +        &gen_deprecated(deprecated_lints),
 +    );
 +
 +    let all_group_lints = usable_lints.iter().filter(|l| {
 +        matches!(
 +            &*l.group,
 +            "correctness" | "suspicious" | "style" | "complexity" | "perf"
 +        )
 +    });
 +    let content = gen_lint_group_list("all", all_group_lints);
 +    process_file("clippy_lints/src/lib.register_all.rs", update_mode, &content);
 +
 +    for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) {
 +        let content = gen_lint_group_list(&lint_group, lints.iter());
 +        process_file(
 +            &format!("clippy_lints/src/lib.register_{}.rs", lint_group),
 +            update_mode,
 +            &content,
 +        );
 +    }
 +
 +    let content = gen_deprecated_lints_test(deprecated_lints);
 +    process_file("tests/ui/deprecated.rs", update_mode, &content);
 +
 +    let content = gen_renamed_lints_test(renamed_lints);
 +    process_file("tests/ui/rename.rs", update_mode, &content);
 +}
 +
 +pub fn print_lints() {
 +    let (lint_list, _, _) = gather_all();
 +    let usable_lints = Lint::usable_lints(&lint_list);
 +    let usable_lint_count = usable_lints.len();
 +    let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
 +
 +    for (lint_group, mut lints) in grouped_by_lint_group {
 +        println!("\n## {}", lint_group);
 +
 +        lints.sort_by_key(|l| l.name.clone());
 +
 +        for lint in lints {
 +            println!("* [{}]({}#{}) ({})", lint.name, DOCS_LINK, lint.name, lint.desc);
 +        }
 +    }
 +
 +    println!("there are {} lints", usable_lint_count);
 +}
 +
 +/// Runs the `rename_lint` command.
 +///
 +/// This does the following:
 +/// * Adds an entry to `renamed_lints.rs`.
 +/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`).
 +/// * Renames the lint struct to the new name.
 +/// * Renames the module containing the lint struct to the new name if it shares a name with the
 +///   lint.
 +///
 +/// # Panics
 +/// Panics for the following conditions:
 +/// * If a file path could not read from or then written to
 +/// * If either lint name has a prefix
 +/// * If `old_name` doesn't name an existing lint.
 +/// * If `old_name` names a deprecated or renamed lint.
 +#[allow(clippy::too_many_lines)]
 +pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
 +    if let Some((prefix, _)) = old_name.split_once("::") {
 +        panic!("`{}` should not contain the `{}` prefix", old_name, prefix);
 +    }
 +    if let Some((prefix, _)) = new_name.split_once("::") {
 +        panic!("`{}` should not contain the `{}` prefix", new_name, prefix);
 +    }
 +
 +    let (mut lints, deprecated_lints, mut renamed_lints) = gather_all();
 +    let mut old_lint_index = None;
 +    let mut found_new_name = false;
 +    for (i, lint) in lints.iter().enumerate() {
 +        if lint.name == old_name {
 +            old_lint_index = Some(i);
 +        } else if lint.name == new_name {
 +            found_new_name = true;
 +        }
 +    }
 +    let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{}`", old_name));
 +
 +    let lint = RenamedLint {
 +        old_name: format!("clippy::{}", old_name),
 +        new_name: if uplift {
 +            new_name.into()
 +        } else {
 +            format!("clippy::{}", new_name)
 +        },
 +    };
 +
 +    // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in
 +    // case.
 +    assert!(
 +        !renamed_lints.iter().any(|l| lint.old_name == l.old_name),
 +        "`{}` has already been renamed",
 +        old_name
 +    );
 +    assert!(
 +        !deprecated_lints.iter().any(|l| lint.old_name == l.name),
 +        "`{}` has already been deprecated",
 +        old_name
 +    );
 +
 +    // Update all lint level attributes. (`clippy::lint_name`)
 +    for file in WalkDir::new(clippy_project_root())
 +        .into_iter()
 +        .map(Result::unwrap)
 +        .filter(|f| {
 +            let name = f.path().file_name();
 +            let ext = f.path().extension();
 +            (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed")))
 +                && name != Some(OsStr::new("rename.rs"))
 +                && name != Some(OsStr::new("renamed_lints.rs"))
 +        })
 +    {
 +        rewrite_file(file.path(), |s| {
 +            replace_ident_like(s, &[(&lint.old_name, &lint.new_name)])
 +        });
 +    }
 +
 +    renamed_lints.push(lint);
 +    renamed_lints.sort_by(|lhs, rhs| {
 +        lhs.new_name
 +            .starts_with("clippy::")
 +            .cmp(&rhs.new_name.starts_with("clippy::"))
 +            .reverse()
 +            .then_with(|| lhs.old_name.cmp(&rhs.old_name))
 +    });
 +
 +    write_file(
 +        Path::new("clippy_lints/src/renamed_lints.rs"),
 +        &gen_renamed_lints_list(&renamed_lints),
 +    );
 +
 +    if uplift {
 +        write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
 +        println!(
 +            "`{}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually.",
 +            old_name
 +        );
 +    } else if found_new_name {
 +        write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
 +        println!(
 +            "`{}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually.",
 +            new_name
 +        );
 +    } else {
 +        // Rename the lint struct and source files sharing a name with the lint.
 +        let lint = &mut lints[old_lint_index];
 +        let old_name_upper = old_name.to_uppercase();
 +        let new_name_upper = new_name.to_uppercase();
 +        lint.name = new_name.into();
 +
 +        // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist.
 +        if try_rename_file(
 +            Path::new(&format!("tests/ui/{}.rs", old_name)),
 +            Path::new(&format!("tests/ui/{}.rs", new_name)),
 +        ) {
 +            try_rename_file(
 +                Path::new(&format!("tests/ui/{}.stderr", old_name)),
 +                Path::new(&format!("tests/ui/{}.stderr", new_name)),
 +            );
 +            try_rename_file(
 +                Path::new(&format!("tests/ui/{}.fixed", old_name)),
 +                Path::new(&format!("tests/ui/{}.fixed", new_name)),
 +            );
 +        }
 +
 +        // Try to rename the file containing the lint if the file name matches the lint's name.
 +        let replacements;
 +        let replacements = if lint.module == old_name
 +            && try_rename_file(
 +                Path::new(&format!("clippy_lints/src/{}.rs", old_name)),
 +                Path::new(&format!("clippy_lints/src/{}.rs", new_name)),
 +            ) {
 +            // Edit the module name in the lint list. Note there could be multiple lints.
 +            for lint in lints.iter_mut().filter(|l| l.module == old_name) {
 +                lint.module = new_name.into();
 +            }
 +            replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)];
 +            replacements.as_slice()
 +        } else if !lint.module.contains("::")
 +            // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs`
 +            && try_rename_file(
 +                Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, old_name)),
 +                Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, new_name)),
 +            )
 +        {
 +            // Edit the module name in the lint list. Note there could be multiple lints, or none.
 +            let renamed_mod = format!("{}::{}", lint.module, old_name);
 +            for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) {
 +                lint.module = format!("{}::{}", lint.module, new_name);
 +            }
 +            replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)];
 +            replacements.as_slice()
 +        } else {
 +            replacements = [(&*old_name_upper, &*new_name_upper), ("", "")];
 +            &replacements[0..1]
 +        };
 +
 +        // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being
 +        // renamed.
 +        for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("renamed_lints.rs")) {
 +            rewrite_file(file.path(), |s| replace_ident_like(s, replacements));
 +        }
 +
 +        generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
 +        println!("{} has been successfully renamed", old_name);
 +    }
 +
 +    println!("note: `cargo uitest` still needs to be run to update the test results");
 +}
 +
 +const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note";
 +/// Runs the `deprecate` command
 +///
 +/// This does the following:
 +/// * Adds an entry to `deprecated_lints.rs`.
 +/// * Removes the lint declaration (and the entire file if applicable)
 +///
 +/// # Panics
 +///
 +/// If a file path could not read from or written to
 +pub fn deprecate(name: &str, reason: Option<&String>) {
 +    fn finish(
 +        (lints, mut deprecated_lints, renamed_lints): (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>),
 +        name: &str,
 +        reason: &str,
 +    ) {
 +        deprecated_lints.push(DeprecatedLint {
 +            name: name.to_string(),
 +            reason: reason.to_string(),
 +            declaration_range: Range::default(),
 +        });
 +
 +        generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
 +        println!("info: `{}` has successfully been deprecated", name);
 +
 +        if reason == DEFAULT_DEPRECATION_REASON {
 +            println!("note: the deprecation reason must be updated in `clippy_lints/src/deprecated_lints.rs`");
 +        }
 +        println!("note: you must run `cargo uitest` to update the test results");
 +    }
 +
 +    let reason = reason.map_or(DEFAULT_DEPRECATION_REASON, String::as_str);
 +    let name_lower = name.to_lowercase();
 +    let name_upper = name.to_uppercase();
 +
 +    let (mut lints, deprecated_lints, renamed_lints) = gather_all();
 +    let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { eprintln!("error: failed to find lint `{}`", name); return; };
 +
 +    let mod_path = {
 +        let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module));
 +        if mod_path.is_dir() {
 +            mod_path = mod_path.join("mod");
 +        }
 +
 +        mod_path.set_extension("rs");
 +        mod_path
 +    };
 +
 +    let deprecated_lints_path = &*clippy_project_root().join("clippy_lints/src/deprecated_lints.rs");
 +
 +    if remove_lint_declaration(&name_lower, &mod_path, &mut lints).unwrap_or(false) {
 +        declare_deprecated(&name_upper, deprecated_lints_path, reason).unwrap();
 +        finish((lints, deprecated_lints, renamed_lints), name, reason);
 +        return;
 +    }
 +
 +    eprintln!("error: lint not found");
 +}
 +
 +fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io::Result<bool> {
 +    fn remove_lint(name: &str, lints: &mut Vec<Lint>) {
 +        lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos));
 +    }
 +
 +    fn remove_test_assets(name: &str) {
 +        let test_file_stem = format!("tests/ui/{}", name);
 +        let path = Path::new(&test_file_stem);
 +
 +        // Some lints have their own directories, delete them
 +        if path.is_dir() {
 +            fs::remove_dir_all(path).ok();
 +            return;
 +        }
 +
 +        // Remove all related test files
 +        fs::remove_file(path.with_extension("rs")).ok();
 +        fs::remove_file(path.with_extension("stderr")).ok();
 +        fs::remove_file(path.with_extension("fixed")).ok();
 +    }
 +
 +    fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) {
 +        let impl_lint_pass_start = content.find("impl_lint_pass!").unwrap_or_else(|| {
 +            content
 +                .find("declare_lint_pass!")
 +                .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`"))
 +        });
 +        let mut impl_lint_pass_end = content[impl_lint_pass_start..]
 +            .find(']')
 +            .expect("failed to find `impl_lint_pass` terminator");
 +
 +        impl_lint_pass_end += impl_lint_pass_start;
 +        if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(&lint_name_upper) {
 +            let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len());
 +            for c in content[lint_name_end..impl_lint_pass_end].chars() {
 +                // Remove trailing whitespace
 +                if c == ',' || c.is_whitespace() {
 +                    lint_name_end += 1;
 +                } else {
 +                    break;
 +                }
 +            }
 +
 +            content.replace_range(impl_lint_pass_start + lint_name_pos..lint_name_end, "");
 +        }
 +    }
 +
 +    if path.exists() {
 +        if let Some(lint) = lints.iter().find(|l| l.name == name) {
 +            if lint.module == name {
 +                // The lint name is the same as the file, we can just delete the entire file
 +                fs::remove_file(path)?;
 +            } else {
 +                // We can't delete the entire file, just remove the declaration
 +
 +                if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) {
 +                    // Remove clippy_lints/src/some_mod/some_lint.rs
 +                    let mut lint_mod_path = path.to_path_buf();
 +                    lint_mod_path.set_file_name(name);
 +                    lint_mod_path.set_extension("rs");
 +
 +                    fs::remove_file(lint_mod_path).ok();
 +                }
 +
 +                let mut content =
 +                    fs::read_to_string(&path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
 +
 +                eprintln!(
 +                    "warn: you will have to manually remove any code related to `{}` from `{}`",
 +                    name,
 +                    path.display()
 +                );
 +
 +                assert!(
 +                    content[lint.declaration_range.clone()].contains(&name.to_uppercase()),
 +                    "error: `{}` does not contain lint `{}`'s declaration",
 +                    path.display(),
 +                    lint.name
 +                );
 +
 +                // Remove lint declaration (declare_clippy_lint!)
 +                content.replace_range(lint.declaration_range.clone(), "");
 +
 +                // Remove the module declaration (mod xyz;)
 +                let mod_decl = format!("\nmod {};", name);
 +                content = content.replacen(&mod_decl, "", 1);
 +
 +                remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content);
 +                fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy()));
 +            }
 +
 +            remove_test_assets(name);
 +            remove_lint(name, lints);
 +            return Ok(true);
 +        }
 +    }
 +
 +    Ok(false)
 +}
 +
 +fn declare_deprecated(name: &str, path: &Path, reason: &str) -> io::Result<()> {
 +    let mut file = OpenOptions::new().write(true).open(path)?;
 +
 +    file.seek(SeekFrom::End(0))?;
 +
 +    let version = crate::new_lint::get_stabilization_version();
 +    let deprecation_reason = if reason == DEFAULT_DEPRECATION_REASON {
 +        "TODO"
 +    } else {
 +        reason
 +    };
 +
 +    writedoc!(
 +        file,
 +        "
 +
 +        declare_deprecated_lint! {{
 +            /// ### What it does
 +            /// Nothing. This lint has been deprecated.
 +            ///
 +            /// ### Deprecation reason
 +            /// {}
 +            #[clippy::version = \"{}\"]
 +            pub {},
 +            \"{}\"
 +        }}
 +
 +        ",
 +        deprecation_reason,
 +        version,
 +        name,
 +        reason,
 +    )
 +}
 +
 +/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there
 +/// were no replacements.
 +fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option<String> {
 +    fn is_ident_char(c: u8) -> bool {
 +        matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_')
 +    }
 +
 +    let searcher = AhoCorasickBuilder::new()
 +        .dfa(true)
 +        .match_kind(aho_corasick::MatchKind::LeftmostLongest)
 +        .build_with_size::<u16, _, _>(replacements.iter().map(|&(x, _)| x.as_bytes()))
 +        .unwrap();
 +
 +    let mut result = String::with_capacity(contents.len() + 1024);
 +    let mut pos = 0;
 +    let mut edited = false;
 +    for m in searcher.find_iter(contents) {
 +        let (old, new) = replacements[m.pattern()];
 +        result.push_str(&contents[pos..m.start()]);
 +        result.push_str(
 +            if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0))
 +                && !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0))
 +            {
 +                edited = true;
 +                new
 +            } else {
 +                old
 +            },
 +        );
 +        pos = m.end();
 +    }
 +    result.push_str(&contents[pos..]);
++    edited.then_some(result)
 +}
 +
 +fn round_to_fifty(count: usize) -> usize {
 +    count / 50 * 50
 +}
 +
 +fn process_file(path: impl AsRef<Path>, update_mode: UpdateMode, content: &str) {
 +    if update_mode == UpdateMode::Check {
 +        let old_content =
 +            fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.as_ref().display(), e));
 +        if content != old_content {
 +            exit_with_failure();
 +        }
 +    } else {
 +        fs::write(&path, content.as_bytes())
 +            .unwrap_or_else(|e| panic!("Cannot write to {}: {}", path.as_ref().display(), e));
 +    }
 +}
 +
 +fn exit_with_failure() {
 +    println!(
 +        "Not all lints defined properly. \
 +                 Please run `cargo dev update_lints` to make sure all lints are defined properly."
 +    );
 +    std::process::exit(1);
 +}
 +
 +/// Lint data parsed from the Clippy source code.
 +#[derive(Clone, PartialEq, Eq, Debug)]
 +struct Lint {
 +    name: String,
 +    group: String,
 +    desc: String,
 +    module: String,
 +    declaration_range: Range<usize>,
 +}
 +
 +impl Lint {
 +    #[must_use]
 +    fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range<usize>) -> Self {
 +        Self {
 +            name: name.to_lowercase(),
 +            group: group.into(),
 +            desc: remove_line_splices(desc),
 +            module: module.into(),
 +            declaration_range,
 +        }
 +    }
 +
 +    /// Returns all non-deprecated lints and non-internal lints
 +    #[must_use]
 +    fn usable_lints(lints: &[Self]) -> Vec<Self> {
 +        lints
 +            .iter()
 +            .filter(|l| !l.group.starts_with("internal"))
 +            .cloned()
 +            .collect()
 +    }
 +
 +    /// Returns all internal lints (not `internal_warn` lints)
 +    #[must_use]
 +    fn internal_lints(lints: &[Self]) -> Vec<Self> {
 +        lints.iter().filter(|l| l.group == "internal").cloned().collect()
 +    }
 +
 +    /// Returns the lints in a `HashMap`, grouped by the different lint groups
 +    #[must_use]
 +    fn by_lint_group(lints: impl Iterator<Item = Self>) -> HashMap<String, Vec<Self>> {
 +        lints.map(|lint| (lint.group.to_string(), lint)).into_group_map()
 +    }
 +}
 +
 +#[derive(Clone, PartialEq, Eq, Debug)]
 +struct DeprecatedLint {
 +    name: String,
 +    reason: String,
 +    declaration_range: Range<usize>,
 +}
 +impl DeprecatedLint {
 +    fn new(name: &str, reason: &str, declaration_range: Range<usize>) -> Self {
 +        Self {
 +            name: name.to_lowercase(),
 +            reason: remove_line_splices(reason),
 +            declaration_range,
 +        }
 +    }
 +}
 +
 +struct RenamedLint {
 +    old_name: String,
 +    new_name: String,
 +}
 +impl RenamedLint {
 +    fn new(old_name: &str, new_name: &str) -> Self {
 +        Self {
 +            old_name: remove_line_splices(old_name),
 +            new_name: remove_line_splices(new_name),
 +        }
 +    }
 +}
 +
 +/// Generates the code for registering a group
 +fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lint>) -> String {
 +    let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect();
 +    details.sort_unstable();
 +
 +    let mut output = GENERATED_FILE_COMMENT.to_string();
 +
 +    let _ = writeln!(
 +        output,
 +        "store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![",
 +        group_name
 +    );
 +    for (module, name) in details {
 +        let _ = writeln!(output, "    LintId::of({}::{}),", module, name);
 +    }
 +    output.push_str("])\n");
 +
 +    output
 +}
 +
 +/// Generates the `register_removed` code
 +#[must_use]
 +fn gen_deprecated(lints: &[DeprecatedLint]) -> String {
 +    let mut output = GENERATED_FILE_COMMENT.to_string();
 +    output.push_str("{\n");
 +    for lint in lints {
 +        let _ = write!(
 +            output,
 +            concat!(
 +                "    store.register_removed(\n",
 +                "        \"clippy::{}\",\n",
 +                "        \"{}\",\n",
 +                "    );\n"
 +            ),
 +            lint.name, lint.reason,
 +        );
 +    }
 +    output.push_str("}\n");
 +
 +    output
 +}
 +
 +/// Generates the code for registering lints
 +#[must_use]
 +fn gen_register_lint_list<'a>(
 +    internal_lints: impl Iterator<Item = &'a Lint>,
 +    usable_lints: impl Iterator<Item = &'a Lint>,
 +) -> String {
 +    let mut details: Vec<_> = internal_lints
 +        .map(|l| (false, &l.module, l.name.to_uppercase()))
 +        .chain(usable_lints.map(|l| (true, &l.module, l.name.to_uppercase())))
 +        .collect();
 +    details.sort_unstable();
 +
 +    let mut output = GENERATED_FILE_COMMENT.to_string();
 +    output.push_str("store.register_lints(&[\n");
 +
 +    for (is_public, module_name, lint_name) in details {
 +        if !is_public {
 +            output.push_str("    #[cfg(feature = \"internal\")]\n");
 +        }
 +        let _ = writeln!(output, "    {}::{},", module_name, lint_name);
 +    }
 +    output.push_str("])\n");
 +
 +    output
 +}
 +
 +fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String {
 +    let mut res: String = GENERATED_FILE_COMMENT.into();
 +    for lint in lints {
 +        writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap();
 +    }
 +    res.push_str("\nfn main() {}\n");
 +    res
 +}
 +
 +fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String {
 +    let mut seen_lints = HashSet::new();
 +    let mut res: String = GENERATED_FILE_COMMENT.into();
 +    res.push_str("// run-rustfix\n\n");
 +    for lint in lints {
 +        if seen_lints.insert(&lint.new_name) {
 +            writeln!(res, "#![allow({})]", lint.new_name).unwrap();
 +        }
 +    }
 +    seen_lints.clear();
 +    for lint in lints {
 +        if seen_lints.insert(&lint.old_name) {
 +            writeln!(res, "#![warn({})]", lint.old_name).unwrap();
 +        }
 +    }
 +    res.push_str("\nfn main() {}\n");
 +    res
 +}
 +
 +fn gen_renamed_lints_list(lints: &[RenamedLint]) -> String {
 +    const HEADER: &str = "\
 +        // This file is managed by `cargo dev rename_lint`. Prefer using that when possible.\n\n\
 +        #[rustfmt::skip]\n\
 +        pub static RENAMED_LINTS: &[(&str, &str)] = &[\n";
 +
 +    let mut res = String::from(HEADER);
 +    for lint in lints {
 +        writeln!(res, "    (\"{}\", \"{}\"),", lint.old_name, lint.new_name).unwrap();
 +    }
 +    res.push_str("];\n");
 +    res
 +}
 +
 +/// Gathers all lints defined in `clippy_lints/src`
 +fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) {
 +    let mut lints = Vec::with_capacity(1000);
 +    let mut deprecated_lints = Vec::with_capacity(50);
 +    let mut renamed_lints = Vec::with_capacity(50);
 +
 +    for (rel_path, file) in clippy_lints_src_files() {
 +        let path = file.path();
 +        let contents =
 +            fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e));
 +        let module = rel_path
 +            .components()
 +            .map(|c| c.as_os_str().to_str().unwrap())
 +            .collect::<Vec<_>>()
 +            .join("::");
 +
 +        // If the lints are stored in mod.rs, we get the module name from
 +        // the containing directory:
 +        let module = if let Some(module) = module.strip_suffix("::mod.rs") {
 +            module
 +        } else {
 +            module.strip_suffix(".rs").unwrap_or(&module)
 +        };
 +
 +        match module {
 +            "deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints),
 +            "renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints),
 +            _ => parse_contents(&contents, module, &mut lints),
 +        }
 +    }
 +    (lints, deprecated_lints, renamed_lints)
 +}
 +
 +fn clippy_lints_src_files() -> impl Iterator<Item = (PathBuf, DirEntry)> {
 +    let root_path = clippy_project_root().join("clippy_lints/src");
 +    let iter = WalkDir::new(&root_path).into_iter();
 +    iter.map(Result::unwrap)
 +        .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
 +        .map(move |f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f))
 +}
 +
 +macro_rules! match_tokens {
 +    ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => {
 +         {
 +            $($(let $capture =)? if let Some(LintDeclSearchResult {
 +                    token_kind: TokenKind::$token $({$($fields)*})?,
 +                    content: _x,
 +                    ..
 +            }) = $iter.next() {
 +                _x
 +            } else {
 +                continue;
 +            };)*
 +            #[allow(clippy::unused_unit)]
 +            { ($($($capture,)?)*) }
 +        }
 +    }
 +}
 +
 +struct LintDeclSearchResult<'a> {
 +    token_kind: TokenKind,
 +    content: &'a str,
 +    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;
 +        offset = range.end;
 +
 +        LintDeclSearchResult {
 +            token_kind: t.kind,
 +            content: &contents[range.clone()],
 +            range,
 +        }
 +    });
 +
 +    while let Some(LintDeclSearchResult { range, .. }) = iter.find(
 +        |LintDeclSearchResult {
 +             token_kind, content, ..
 +         }| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint",
 +    ) {
 +        let start = range.start;
 +
 +        let mut iter = iter
 +            .by_ref()
 +            .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
 +        // matches `!{`
 +        match_tokens!(iter, Bang OpenBrace);
 +        match iter.next() {
 +            // #[clippy::version = "version"] pub
 +            Some(LintDeclSearchResult {
 +                token_kind: TokenKind::Pound,
 +                ..
 +            }) => {
 +                match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
 +            },
 +            // pub
 +            Some(LintDeclSearchResult {
 +                token_kind: TokenKind::Ident,
 +                ..
 +            }) => (),
 +            _ => continue,
 +        }
 +
 +        let (name, group, desc) = match_tokens!(
 +            iter,
 +            // LINT_NAME
 +            Ident(name) Comma
 +            // group,
 +            Ident(group) Comma
 +            // "description"
 +            Literal{..}(desc)
 +        );
 +
 +        if let Some(LintDeclSearchResult {
 +            token_kind: TokenKind::CloseBrace,
 +            range,
 +            ..
 +        }) = iter.next()
 +        {
 +            lints.push(Lint::new(name, group, desc, module, start..range.end));
 +        }
 +    }
 +}
 +
 +/// Parse a source file looking for `declare_deprecated_lint` macro invocations.
 +fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) {
 +    let mut offset = 0usize;
 +    let mut iter = tokenize(contents).map(|t| {
 +        let range = offset..offset + t.len;
 +        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;
 +            offset = range.end;
 +
 +            LintDeclSearchResult {
 +                token_kind: t.kind,
 +                content: &line[range.clone()],
 +                range,
 +            }
 +        });
 +
 +        let (old_name, new_name) = match_tokens!(
 +            iter,
 +            // ("old_name",
 +            Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(old_name) Comma
 +            // "new_name"),
 +            Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma
 +        );
 +        lints.push(RenamedLint::new(old_name, new_name));
 +    }
 +}
 +
 +/// Removes the line splices and surrounding quotes from a string literal
 +fn remove_line_splices(s: &str) -> String {
 +    let s = s
 +        .strip_prefix('r')
 +        .unwrap_or(s)
 +        .trim_matches('#')
 +        .strip_prefix('"')
 +        .and_then(|s| s.strip_suffix('"'))
 +        .unwrap_or_else(|| panic!("expected quoted string, found `{}`", s));
 +    let mut res = String::with_capacity(s.len());
 +    unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, _| res.push_str(&s[range]));
 +    res
 +}
 +
 +/// Replaces a region in a file delimited by two lines matching regexes.
 +///
 +/// `path` is the relative path to the file on which you want to perform the replacement.
 +///
 +/// See `replace_region_in_text` for documentation of the other options.
 +///
 +/// # Panics
 +///
 +/// Panics if the path could not read or then written
 +fn replace_region_in_file(
 +    update_mode: UpdateMode,
 +    path: &Path,
 +    start: &str,
 +    end: &str,
 +    write_replacement: impl FnMut(&mut String),
 +) {
 +    let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e));
 +    let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) {
 +        Ok(x) => x,
 +        Err(delim) => panic!("Couldn't find `{}` in file `{}`", delim, path.display()),
 +    };
 +
 +    match update_mode {
 +        UpdateMode::Check if contents != new_contents => exit_with_failure(),
 +        UpdateMode::Check => (),
 +        UpdateMode::Change => {
 +            if let Err(e) = fs::write(path, new_contents.as_bytes()) {
 +                panic!("Cannot write to `{}`: {}", path.display(), e);
 +            }
 +        },
 +    }
 +}
 +
 +/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters
 +/// were found, or the missing delimiter if not.
 +fn replace_region_in_text<'a>(
 +    text: &str,
 +    start: &'a str,
 +    end: &'a str,
 +    mut write_replacement: impl FnMut(&mut String),
 +) -> Result<String, &'a str> {
 +    let (text_start, rest) = text.split_once(start).ok_or(start)?;
 +    let (_, text_end) = rest.split_once(end).ok_or(end)?;
 +
 +    let mut res = String::with_capacity(text.len() + 4096);
 +    res.push_str(text_start);
 +    res.push_str(start);
 +    write_replacement(&mut res);
 +    res.push_str(end);
 +    res.push_str(text_end);
 +
 +    Ok(res)
 +}
 +
 +fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
 +    match fs::OpenOptions::new().create_new(true).write(true).open(new_name) {
 +        Ok(file) => drop(file),
 +        Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false,
 +        Err(e) => panic_file(e, new_name, "create"),
 +    };
 +    match fs::rename(old_name, new_name) {
 +        Ok(()) => true,
 +        Err(e) => {
 +            drop(fs::remove_file(new_name));
 +            if e.kind() == io::ErrorKind::NotFound {
 +                false
 +            } else {
 +                panic_file(e, old_name, "rename");
 +            }
 +        },
 +    }
 +}
 +
 +#[allow(clippy::needless_pass_by_value)]
 +fn panic_file(error: io::Error, name: &Path, action: &str) -> ! {
 +    panic!("failed to {} file `{}`: {}", action, name.display(), error)
 +}
 +
 +fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option<String>) {
 +    let mut file = fs::OpenOptions::new()
 +        .write(true)
 +        .read(true)
 +        .open(path)
 +        .unwrap_or_else(|e| panic_file(e, path, "open"));
 +    let mut buf = String::new();
 +    file.read_to_string(&mut buf)
 +        .unwrap_or_else(|e| panic_file(e, path, "read"));
 +    if let Some(new_contents) = f(&buf) {
 +        file.rewind().unwrap_or_else(|e| panic_file(e, path, "write"));
 +        file.write_all(new_contents.as_bytes())
 +            .unwrap_or_else(|e| panic_file(e, path, "write"));
 +        file.set_len(new_contents.len() as u64)
 +            .unwrap_or_else(|e| panic_file(e, path, "write"));
 +    }
 +}
 +
 +fn write_file(path: &Path, contents: &str) {
 +    fs::write(path, contents).unwrap_or_else(|e| panic_file(e, path, "write"));
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +
 +    #[test]
 +    fn test_parse_contents() {
 +        static CONTENTS: &str = r#"
 +            declare_clippy_lint! {
 +                #[clippy::version = "Hello Clippy!"]
 +                pub PTR_ARG,
 +                style,
 +                "really long \
 +                text"
 +            }
 +
 +            declare_clippy_lint!{
 +                #[clippy::version = "Test version"]
 +                pub DOC_MARKDOWN,
 +                pedantic,
 +                "single line"
 +            }
 +        "#;
 +        let mut result = Vec::new();
 +        parse_contents(CONTENTS, "module_name", &mut result);
 +        for r in &mut result {
 +            r.declaration_range = Range::default();
 +        }
 +
 +        let expected = vec![
 +            Lint::new(
 +                "ptr_arg",
 +                "style",
 +                "\"really long text\"",
 +                "module_name",
 +                Range::default(),
 +            ),
 +            Lint::new(
 +                "doc_markdown",
 +                "pedantic",
 +                "\"single line\"",
 +                "module_name",
 +                Range::default(),
 +            ),
 +        ];
 +        assert_eq!(expected, result);
 +    }
 +
 +    #[test]
 +    fn test_parse_deprecated_contents() {
 +        static DEPRECATED_CONTENTS: &str = r#"
 +            /// some doc comment
 +            declare_deprecated_lint! {
 +                #[clippy::version = "I'm a version"]
 +                pub SHOULD_ASSERT_EQ,
 +                "`assert!()` will be more flexible with RFC 2011"
 +            }
 +        "#;
 +
 +        let mut result = Vec::new();
 +        parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result);
 +        for r in &mut result {
 +            r.declaration_range = Range::default();
 +        }
 +
 +        let expected = vec![DeprecatedLint::new(
 +            "should_assert_eq",
 +            "\"`assert!()` will be more flexible with RFC 2011\"",
 +            Range::default(),
 +        )];
 +        assert_eq!(expected, result);
 +    }
 +
 +    #[test]
 +    fn test_usable_lints() {
 +        let lints = vec![
 +            Lint::new(
 +                "should_assert_eq2",
 +                "Not Deprecated",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +            ),
 +            Lint::new(
 +                "should_assert_eq2",
 +                "internal",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +            ),
 +            Lint::new(
 +                "should_assert_eq2",
 +                "internal_style",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +            ),
 +        ];
 +        let expected = vec![Lint::new(
 +            "should_assert_eq2",
 +            "Not Deprecated",
 +            "\"abc\"",
 +            "module_name",
 +            Range::default(),
 +        )];
 +        assert_eq!(expected, Lint::usable_lints(&lints));
 +    }
 +
 +    #[test]
 +    fn test_by_lint_group() {
 +        let lints = vec![
 +            Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
 +            Lint::new(
 +                "should_assert_eq2",
 +                "group2",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +            ),
 +            Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
 +        ];
 +        let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
 +        expected.insert(
 +            "group1".to_string(),
 +            vec![
 +                Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
 +                Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
 +            ],
 +        );
 +        expected.insert(
 +            "group2".to_string(),
 +            vec![Lint::new(
 +                "should_assert_eq2",
 +                "group2",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +            )],
 +        );
 +        assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
 +    }
 +
 +    #[test]
 +    fn test_gen_deprecated() {
 +        let lints = vec![
 +            DeprecatedLint::new(
 +                "should_assert_eq",
 +                "\"has been superseded by should_assert_eq2\"",
 +                Range::default(),
 +            ),
 +            DeprecatedLint::new("another_deprecated", "\"will be removed\"", Range::default()),
 +        ];
 +
 +        let expected = GENERATED_FILE_COMMENT.to_string()
 +            + &[
 +                "{",
 +                "    store.register_removed(",
 +                "        \"clippy::should_assert_eq\",",
 +                "        \"has been superseded by should_assert_eq2\",",
 +                "    );",
 +                "    store.register_removed(",
 +                "        \"clippy::another_deprecated\",",
 +                "        \"will be removed\",",
 +                "    );",
 +                "}",
 +            ]
 +            .join("\n")
 +            + "\n";
 +
 +        assert_eq!(expected, gen_deprecated(&lints));
 +    }
 +
 +    #[test]
 +    fn test_gen_lint_group_list() {
 +        let lints = vec![
 +            Lint::new("abc", "group1", "\"abc\"", "module_name", Range::default()),
 +            Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
 +            Lint::new("internal", "internal_style", "\"abc\"", "module_name", Range::default()),
 +        ];
 +        let expected = GENERATED_FILE_COMMENT.to_string()
 +            + &[
 +                "store.register_group(true, \"clippy::group1\", Some(\"clippy_group1\"), vec![",
 +                "    LintId::of(module_name::ABC),",
 +                "    LintId::of(module_name::INTERNAL),",
 +                "    LintId::of(module_name::SHOULD_ASSERT_EQ),",
 +                "])",
 +            ]
 +            .join("\n")
 +            + "\n";
 +
 +        let result = gen_lint_group_list("group1", lints.iter());
 +
 +        assert_eq!(expected, result);
 +    }
 +}
index eee5f90d178852368c1237a2a7c898ae78cdbee4,0000000000000000000000000000000000000000..1761360fb2812c8fdda7afe736d07f742d309e68
mode 100644,000000..100644
--- /dev/null
@@@ -1,289 -1,0 +1,289 @@@
-     #[clippy::version = "1.49.0"]
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::{match_def_path, paths};
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{def::Res, AsyncGeneratorKind, Body, BodyId, GeneratorKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::GeneratorInteriorTypeCause;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::Span;
 +
 +use crate::utils::conf::DisallowedType;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to await while holding a non-async-aware MutexGuard.
 +    ///
 +    /// ### Why is this bad?
 +    /// The Mutex types found in std::sync and parking_lot
 +    /// are not designed to operate in an async context across await points.
 +    ///
 +    /// There are two potential solutions. One is to use an async-aware Mutex
 +    /// type. Many asynchronous foundation crates provide such a Mutex type. The
 +    /// other solution is to ensure the mutex is unlocked before calling await,
 +    /// either by introducing a scope or an explicit call to Drop::drop.
 +    ///
 +    /// ### Known problems
 +    /// Will report false positive for explicitly dropped guards
 +    /// ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)). A workaround for this is
 +    /// to wrap the `.lock()` call in a block instead of explicitly dropping the guard.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::sync::Mutex;
 +    /// # async fn baz() {}
 +    /// async fn foo(x: &Mutex<u32>) {
 +    ///   let mut guard = x.lock().unwrap();
 +    ///   *guard += 1;
 +    ///   baz().await;
 +    /// }
 +    ///
 +    /// async fn bar(x: &Mutex<u32>) {
 +    ///   let mut guard = x.lock().unwrap();
 +    ///   *guard += 1;
 +    ///   drop(guard); // explicit drop
 +    ///   baz().await;
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::sync::Mutex;
 +    /// # async fn baz() {}
 +    /// async fn foo(x: &Mutex<u32>) {
 +    ///   {
 +    ///     let mut guard = x.lock().unwrap();
 +    ///     *guard += 1;
 +    ///   }
 +    ///   baz().await;
 +    /// }
 +    ///
 +    /// async fn bar(x: &Mutex<u32>) {
 +    ///   {
 +    ///     let mut guard = x.lock().unwrap();
 +    ///     *guard += 1;
 +    ///   } // guard dropped here at end of scope
 +    ///   baz().await;
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub AWAIT_HOLDING_LOCK,
 +    suspicious,
 +    "inside an async function, holding a `MutexGuard` while calling `await`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to await while holding a `RefCell` `Ref` or `RefMut`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `RefCell` refs only check for exclusive mutable access
 +    /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point
 +    /// risks panics from a mutable ref shared while other refs are outstanding.
 +    ///
 +    /// ### Known problems
 +    /// Will report false positive for explicitly dropped refs
 +    /// ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)). A workaround for this is
 +    /// to wrap the `.borrow[_mut]()` call in a block instead of explicitly dropping the ref.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::cell::RefCell;
 +    /// # async fn baz() {}
 +    /// async fn foo(x: &RefCell<u32>) {
 +    ///   let mut y = x.borrow_mut();
 +    ///   *y += 1;
 +    ///   baz().await;
 +    /// }
 +    ///
 +    /// async fn bar(x: &RefCell<u32>) {
 +    ///   let mut y = x.borrow_mut();
 +    ///   *y += 1;
 +    ///   drop(y); // explicit drop
 +    ///   baz().await;
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::cell::RefCell;
 +    /// # async fn baz() {}
 +    /// async fn foo(x: &RefCell<u32>) {
 +    ///   {
 +    ///      let mut y = x.borrow_mut();
 +    ///      *y += 1;
 +    ///   }
 +    ///   baz().await;
 +    /// }
 +    ///
 +    /// async fn bar(x: &RefCell<u32>) {
 +    ///   {
 +    ///     let mut y = x.borrow_mut();
 +    ///     *y += 1;
 +    ///   } // y dropped here at end of scope
 +    ///   baz().await;
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub AWAIT_HOLDING_REFCELL_REF,
 +    suspicious,
 +    "inside an async function, holding a `RefCell` ref while calling `await`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Allows users to configure types which should not be held across `await`
 +    /// suspension points.
 +    ///
 +    /// ### Why is this bad?
 +    /// There are some types which are perfectly "safe" to be used concurrently
 +    /// from a memory access perspective but will cause bugs at runtime if they
 +    /// are held in such a way.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```toml
 +    /// await-holding-invalid-types = [
 +    ///   # You can specify a type name
 +    ///   "CustomLockType",
 +    ///   # You can (optionally) specify a reason
 +    ///   { path = "OtherCustomLockType", reason = "Relies on a thread local" }
 +    /// ]
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # async fn baz() {}
 +    /// struct CustomLockType;
 +    /// struct OtherCustomLockType;
 +    /// async fn foo() {
 +    ///   let _x = CustomLockType;
 +    ///   let _y = OtherCustomLockType;
 +    ///   baz().await; // Lint violation
 +    /// }
 +    /// ```
++    #[clippy::version = "1.62.0"]
 +    pub AWAIT_HOLDING_INVALID_TYPE,
 +    suspicious,
 +    "holding a type across an await point which is not allowed to be held as per the configuration"
 +}
 +
 +impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]);
 +
 +#[derive(Debug)]
 +pub struct AwaitHolding {
 +    conf_invalid_types: Vec<DisallowedType>,
 +    def_ids: FxHashMap<DefId, DisallowedType>,
 +}
 +
 +impl AwaitHolding {
 +    pub(crate) fn new(conf_invalid_types: Vec<DisallowedType>) -> Self {
 +        Self {
 +            conf_invalid_types,
 +            def_ids: FxHashMap::default(),
 +        }
 +    }
 +}
 +
 +impl LateLintPass<'_> for AwaitHolding {
 +    fn check_crate(&mut self, cx: &LateContext<'_>) {
 +        for conf in &self.conf_invalid_types {
 +            let path = match conf {
 +                DisallowedType::Simple(path) | DisallowedType::WithReason { path, .. } => path,
 +            };
 +            let segs: Vec<_> = path.split("::").collect();
 +            if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) {
 +                self.def_ids.insert(id, conf.clone());
 +            }
 +        }
 +    }
 +
 +    fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
 +        use AsyncGeneratorKind::{Block, Closure, Fn};
 +        if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind {
 +            let body_id = BodyId {
 +                hir_id: body.value.hir_id,
 +            };
 +            let typeck_results = cx.tcx.typeck_body(body_id);
 +            self.check_interior_types(
 +                cx,
 +                typeck_results.generator_interior_types.as_ref().skip_binder(),
 +                body.value.span,
 +            );
 +        }
 +    }
 +}
 +
 +impl AwaitHolding {
 +    fn check_interior_types(&self, cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
 +        for ty_cause in ty_causes {
 +            if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
 +                if is_mutex_guard(cx, adt.did()) {
 +                    span_lint_and_then(
 +                        cx,
 +                        AWAIT_HOLDING_LOCK,
 +                        ty_cause.span,
 +                        "this `MutexGuard` is held across an `await` point",
 +                        |diag| {
 +                            diag.help(
 +                                "consider using an async-aware `Mutex` type or ensuring the \
 +                                `MutexGuard` is dropped before calling await",
 +                            );
 +                            diag.span_note(
 +                                ty_cause.scope_span.unwrap_or(span),
 +                                "these are all the `await` points this lock is held through",
 +                            );
 +                        },
 +                    );
 +                } else if is_refcell_ref(cx, adt.did()) {
 +                    span_lint_and_then(
 +                        cx,
 +                        AWAIT_HOLDING_REFCELL_REF,
 +                        ty_cause.span,
 +                        "this `RefCell` reference is held across an `await` point",
 +                        |diag| {
 +                            diag.help("ensure the reference is dropped before calling `await`");
 +                            diag.span_note(
 +                                ty_cause.scope_span.unwrap_or(span),
 +                                "these are all the `await` points this reference is held through",
 +                            );
 +                        },
 +                    );
 +                } else if let Some(disallowed) = self.def_ids.get(&adt.did()) {
 +                    emit_invalid_type(cx, ty_cause.span, disallowed);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedType) {
 +    let (type_name, reason) = match disallowed {
 +        DisallowedType::Simple(path) => (path, &None),
 +        DisallowedType::WithReason { path, reason } => (path, reason),
 +    };
 +
 +    span_lint_and_then(
 +        cx,
 +        AWAIT_HOLDING_INVALID_TYPE,
 +        span,
 +        &format!("`{type_name}` may not be held across an `await` point per `clippy.toml`",),
 +        |diag| {
 +            if let Some(reason) = reason {
 +                diag.note(reason.clone());
 +            }
 +        },
 +    );
 +}
 +
 +fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    match_def_path(cx, def_id, &paths::MUTEX_GUARD)
 +        || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD)
 +        || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD)
 +        || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD)
 +        || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
 +        || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
 +}
 +
 +fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT)
 +}
index 1582ec9ee5ce6e21776768a16d4b4159c0450795,0000000000000000000000000000000000000000..937765b66147912aa6c9172a2b61b4369f91cbec
mode 100644,000000..100644
--- /dev/null
@@@ -1,121 -1,0 +1,121 @@@
-     /// let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggerd.
 +use crate::reference::DEREF_ADDROF;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::implements_trait;
 +use clippy_utils::{get_parent_expr, is_lint_allowed};
 +use rustc_errors::Applicability;
 +use rustc_hir::{ExprKind, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::mir::Mutability;
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `&*(&T)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Dereferencing and then borrowing a reference value has no effect in most cases.
 +    ///
 +    /// ### Known problems
 +    /// False negative on such code:
 +    /// ```
 +    /// let x = &12;
 +    /// let addr_x = &x as *const _ as usize;
++    /// let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggered.
 +    ///                                         // But if we fix it, assert will fail.
 +    /// assert_ne!(addr_x, addr_y);
 +    /// ```
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo(_x: &str) {}
 +    ///
 +    /// let s = &String::new();
 +    ///
 +    /// let a: &String = &* s;
 +    /// foo(&*s);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # fn foo(_x: &str) {}
 +    /// # let s = &String::new();
 +    /// let a: &String = s;
 +    /// foo(&**s);
 +    /// ```
 +    #[clippy::version = "1.59.0"]
 +    pub BORROW_DEREF_REF,
 +    complexity,
 +    "deref on an immutable reference returns the same type as itself"
 +}
 +
 +declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]);
 +
 +impl LateLintPass<'_> for BorrowDerefRef {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, e: &rustc_hir::Expr<'_>) {
 +        if_chain! {
 +            if !e.span.from_expansion();
 +            if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind;
 +            if !addrof_target.span.from_expansion();
 +            if let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind;
 +            if !deref_target.span.from_expansion();
 +            if !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..) );
 +            let ref_ty = cx.typeck_results().expr_ty(deref_target);
 +            if let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind();
 +            then{
 +
 +                if let Some(parent_expr) = get_parent_expr(cx, e){
 +                    if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) &&
 +                       !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) {
 +                        return;
 +                    }
 +
 +                    // modification to `&mut &*x` is different from `&mut x`
 +                    if matches!(deref_target.kind, ExprKind::Path(..)
 +                                             | ExprKind::Field(..)
 +                                             | ExprKind::Index(..)
 +                                             | ExprKind::Unary(UnOp::Deref, ..))
 +                     && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) {
 +                       return;
 +                    }
 +                }
 +
 +                span_lint_and_then(
 +                    cx,
 +                    BORROW_DEREF_REF,
 +                    e.span,
 +                    "deref on an immutable reference",
 +                    |diag| {
 +                        diag.span_suggestion(
 +                            e.span,
 +                            "if you would like to reborrow, try removing `&*`",
 +                            snippet_opt(cx, deref_target.span).unwrap(),
 +                            Applicability::MachineApplicable
 +                        );
 +
 +                        // has deref trait -> give 2 help
 +                        // doesn't have deref trait -> give 1 help
 +                        if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait(){
 +                            if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) {
 +                                return;
 +                            }
 +                        }
 +
 +                        diag.span_suggestion(
 +                            e.span,
 +                            "if you would like to deref, try using `&**`",
 +                            format!(
 +                                "&**{}",
 +                                &snippet_opt(cx, deref_target.span).unwrap(),
 +                             ),
 +                            Applicability::MaybeIncorrect
 +                        );
 +
 +                    }
 +                );
 +
 +            }
 +        }
 +    }
 +}
index 02c2f30a4dd6acb1e4b37f2cf9faa1b6bc9b50ed,0000000000000000000000000000000000000000..af3798a0cc8c068520c2efad604a4947fb1c3079
mode 100644,000000..100644
--- /dev/null
@@@ -1,588 -1,0 +1,588 @@@
-     #[clippy::version = "1.61.0"]
 +mod cast_abs_to_unsigned;
 +mod cast_enum_constructor;
 +mod cast_lossless;
 +mod cast_possible_truncation;
 +mod cast_possible_wrap;
 +mod cast_precision_loss;
 +mod cast_ptr_alignment;
 +mod cast_ref_to_mut;
 +mod cast_sign_loss;
 +mod cast_slice_different_sizes;
 +mod char_lit_as_u8;
 +mod fn_to_numeric_cast;
 +mod fn_to_numeric_cast_any;
 +mod fn_to_numeric_cast_with_truncation;
 +mod ptr_as_ptr;
 +mod unnecessary_cast;
 +mod utils;
 +
 +use clippy_utils::is_hir_ty_cfg_dependant;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from any numerical to a float type where
 +    /// the receiving type cannot store all values from the original type without
 +    /// rounding errors. This possible rounding is to be expected, so this lint is
 +    /// `Allow` by default.
 +    ///
 +    /// Basically, this warns on casting any integer with 32 or more bits to `f32`
 +    /// or any 64-bit integer to `f64`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's not bad at all. But in some applications it can be
 +    /// helpful to know where precision loss can take place. This lint can help find
 +    /// those places in the code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = u64::MAX;
 +    /// x as f64;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_PRECISION_LOSS,
 +    pedantic,
 +    "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from a signed to an unsigned numerical
 +    /// type. In this case, negative values wrap around to large positive values,
 +    /// which can be quite surprising in practice. However, as the cast works as
 +    /// defined, this lint is `Allow` by default.
 +    ///
 +    /// ### Why is this bad?
 +    /// Possibly surprising results. You can activate this lint
 +    /// as a one-time check to see where numerical wrapping can arise.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let y: i8 = -1;
 +    /// y as u128; // will return 18446744073709551615
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_SIGN_LOSS,
 +    pedantic,
 +    "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts between numerical types that may
 +    /// truncate large values. This is expected behavior, so the cast is `Allow` by
 +    /// default.
 +    ///
 +    /// ### Why is this bad?
 +    /// In some problem domains, it is good practice to avoid
 +    /// truncation. This lint can be activated to help assess where additional
 +    /// checks could be beneficial.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn as_u8(x: u64) -> u8 {
 +    ///     x as u8
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_POSSIBLE_TRUNCATION,
 +    pedantic,
 +    "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from an unsigned type to a signed type of
 +    /// the same size. Performing such a cast is a 'no-op' for the compiler,
 +    /// i.e., nothing is changed at the bit level, and the binary representation of
 +    /// the value is reinterpreted. This can cause wrapping if the value is too big
 +    /// for the target signed type. However, the cast works as defined, so this lint
 +    /// is `Allow` by default.
 +    ///
 +    /// ### Why is this bad?
 +    /// While such a cast is not bad in itself, the results can
 +    /// be surprising when this is not the intended behavior, as demonstrated by the
 +    /// example below.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// u32::MAX as i32; // will yield a value of `-1`
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_POSSIBLE_WRAP,
 +    pedantic,
 +    "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts between numerical types that may
 +    /// be replaced by safe conversion functions.
 +    ///
 +    /// ### Why is this bad?
 +    /// Rust's `as` keyword will perform many kinds of
 +    /// conversions, including silently lossy conversions. Conversion functions such
 +    /// as `i32::from` will only perform lossless conversions. Using the conversion
 +    /// functions prevents conversions from turning into silent lossy conversions if
 +    /// the types of the input expressions ever change, and make it easier for
 +    /// people reading the code to know that the conversion is lossless.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn as_u64(x: u8) -> u64 {
 +    ///     x as u64
 +    /// }
 +    /// ```
 +    ///
 +    /// Using `::from` would look like this:
 +    ///
 +    /// ```rust
 +    /// fn as_u64(x: u8) -> u64 {
 +    ///     u64::from(x)
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_LOSSLESS,
 +    pedantic,
 +    "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts to the same type, casts of int literals to integer types
 +    /// and casts of float literals to float types.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's just unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = 2i32 as i32;
 +    /// let _ = 0.5 as f32;
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust
 +    /// let _ = 2_i32;
 +    /// let _ = 0.5_f32;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNNECESSARY_CAST,
 +    complexity,
 +    "cast to the same type, e.g., `x as i32` where `x: i32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts, using `as` or `pointer::cast`,
 +    /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer
 +    ///
 +    /// ### Why is this bad?
 +    /// Dereferencing the resulting pointer may be undefined
 +    /// behavior.
 +    ///
 +    /// ### Known problems
 +    /// Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar
 +    /// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like
 +    /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (&1u8 as *const u8) as *const u16;
 +    /// let _ = (&mut 1u8 as *mut u8) as *mut u16;
 +    ///
 +    /// (&1u8 as *const u8).cast::<u16>();
 +    /// (&mut 1u8 as *mut u8).cast::<u16>();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_PTR_ALIGNMENT,
 +    pedantic,
 +    "cast from a pointer to a more-strictly-aligned pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of function pointers to something other than usize
 +    ///
 +    /// ### Why is this bad?
 +    /// Casting a function pointer to anything other than usize/isize is not portable across
 +    /// architectures, because you end up losing bits if the target type is too small or end up with a
 +    /// bunch of extra bits that waste space and add more instructions to the final binary than
 +    /// strictly necessary for the problem
 +    ///
 +    /// Casting to isize also doesn't make sense since there are no signed addresses.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// 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"
 +}
 +
 +pub struct Casts {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl Casts {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(Casts => [
 +    CAST_PRECISION_LOSS,
 +    CAST_SIGN_LOSS,
 +    CAST_POSSIBLE_TRUNCATION,
 +    CAST_POSSIBLE_WRAP,
 +    CAST_LOSSLESS,
 +    CAST_REF_TO_MUT,
 +    CAST_PTR_ALIGNMENT,
 +    CAST_SLICE_DIFFERENT_SIZES,
 +    UNNECESSARY_CAST,
 +    FN_TO_NUMERIC_CAST_ANY,
 +    FN_TO_NUMERIC_CAST,
 +    FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
 +    CHAR_LIT_AS_U8,
 +    PTR_AS_PTR,
 +    CAST_ENUM_TRUNCATION,
 +    CAST_ENUM_CONSTRUCTOR,
 +    CAST_ABS_TO_UNSIGNED
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Casts {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if !in_external_macro(cx.sess(), expr.span) {
 +            ptr_as_ptr::check(cx, expr, self.msrv);
 +        }
 +
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
 +            if is_hir_ty_cfg_dependant(cx, cast_to) {
 +                return;
 +            }
 +            let (cast_from, cast_to) = (
 +                cx.typeck_results().expr_ty(cast_expr),
 +                cx.typeck_results().expr_ty(expr),
 +            );
 +
 +            if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
 +                return;
 +            }
 +
 +            fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
 +            fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
 +            fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
 +
 +            if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
 +                cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
 +                if cast_from.is_numeric() {
 +                    cast_possible_wrap::check(cx, expr, cast_from, cast_to);
 +                    cast_precision_loss::check(cx, expr, cast_from, cast_to);
 +                    cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
 +                    cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
 +                }
 +                cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
 +                cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
 +            }
 +        }
 +
 +        cast_ref_to_mut::check(cx, expr);
 +        cast_ptr_alignment::check(cx, expr);
 +        char_lit_as_u8::check(cx, expr);
 +        ptr_as_ptr::check(cx, expr, self.msrv);
 +        cast_slice_different_sizes::check(cx, expr, self.msrv);
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
index 1deff9684a140abedbe5d1da5facb0f7bba1486f,0000000000000000000000000000000000000000..0e3d9317590f3c80782c517e5c64fc66019dc40d
mode 100644,000000..100644
--- /dev/null
@@@ -1,532 -1,0 +1,584 @@@
-     eq_expr_value, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed,
-     search_same, ContainsName, HirEqInterExpr, SpanlessEq,
 +use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
 +use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
++use clippy_utils::ty::needs_ordered_drop;
++use clippy_utils::visitors::for_each_expr;
 +use clippy_utils::{
- use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Stmt, StmtKind};
++    capture_local_usage, eq_expr_value, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause,
++    is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
 +};
 +use core::iter;
++use core::ops::ControlFlow;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit;
-     blocks: &[&Block<'tcx>],
++use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::hygiene::walk_chain;
 +use rustc_span::source_map::SourceMap;
 +use rustc_span::{BytePos, Span, Symbol};
 +use std::borrow::Cow;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for consecutive `if`s with the same condition.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a copy & paste error.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// if a == b {
 +    ///     …
 +    /// } else if a == b {
 +    ///     …
 +    /// }
 +    /// ```
 +    ///
 +    /// Note that this lint ignores all conditions with a function call as it could
 +    /// have side effects:
 +    ///
 +    /// ```ignore
 +    /// if foo() {
 +    ///     …
 +    /// } else if foo() { // not linted
 +    ///     …
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub IFS_SAME_COND,
 +    correctness,
 +    "consecutive `if`s with the same condition"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for consecutive `if`s with the same function call.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a copy & paste error.
 +    /// Despite the fact that function can have side effects and `if` works as
 +    /// intended, such an approach is implicit and can be considered a "code smell".
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// if foo() == bar {
 +    ///     …
 +    /// } else if foo() == bar {
 +    ///     …
 +    /// }
 +    /// ```
 +    ///
 +    /// This probably should be:
 +    /// ```ignore
 +    /// if foo() == bar {
 +    ///     …
 +    /// } else if foo() == baz {
 +    ///     …
 +    /// }
 +    /// ```
 +    ///
 +    /// or if the original code was not a typo and called function mutates a state,
 +    /// consider move the mutation out of the `if` condition to avoid similarity to
 +    /// a copy & paste error:
 +    ///
 +    /// ```ignore
 +    /// let first = foo();
 +    /// if first == bar {
 +    ///     …
 +    /// } else {
 +    ///     let second = foo();
 +    ///     if second == bar {
 +    ///     …
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.41.0"]
 +    pub SAME_FUNCTIONS_IN_IF_CONDITION,
 +    pedantic,
 +    "consecutive `if`s with the same function call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `if/else` with the same body as the *then* part
 +    /// and the *else* part.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a copy & paste error.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// let foo = if … {
 +    ///     42
 +    /// } else {
 +    ///     42
 +    /// };
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub IF_SAME_THEN_ELSE,
 +    correctness,
 +    "`if` with the same `then` and `else` blocks"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks if the `if` and `else` block contain shared code that can be
 +    /// moved out of the blocks.
 +    ///
 +    /// ### Why is this bad?
 +    /// Duplicate code is less maintainable.
 +    ///
 +    /// ### Known problems
 +    /// * The lint doesn't check if the moved expressions modify values that are being used in
 +    ///   the if condition. The suggestion can in that case modify the behavior of the program.
 +    ///   See [rust-clippy#7452](https://github.com/rust-lang/rust-clippy/issues/7452)
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// let foo = if … {
 +    ///     println!("Hello World");
 +    ///     13
 +    /// } else {
 +    ///     println!("Hello World");
 +    ///     42
 +    /// };
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```ignore
 +    /// println!("Hello World");
 +    /// let foo = if … {
 +    ///     13
 +    /// } else {
 +    ///     42
 +    /// };
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub BRANCHES_SHARING_CODE,
 +    nursery,
 +    "`if` statement with shared code in all blocks"
 +}
 +
 +declare_lint_pass!(CopyAndPaste => [
 +    IFS_SAME_COND,
 +    SAME_FUNCTIONS_IN_IF_CONDITION,
 +    IF_SAME_THEN_ELSE,
 +    BRANCHES_SHARING_CODE
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) {
 +            let (conds, blocks) = if_sequence(expr);
 +            lint_same_cond(cx, &conds);
 +            lint_same_fns_in_if_cond(cx, &conds);
 +            let all_same =
 +                !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks);
 +            if !all_same && conds.len() != blocks.len() {
 +                lint_branches_sharing_code(cx, &conds, &blocks, expr);
 +            }
 +        }
 +    }
 +}
 +
 +/// Checks if the given expression is a let chain.
 +fn contains_let(e: &Expr<'_>) -> bool {
 +    match e.kind {
 +        ExprKind::Let(..) => true,
 +        ExprKind::Binary(op, lhs, rhs) if op.node == BinOpKind::And => {
 +            matches!(lhs.kind, ExprKind::Let(..)) || contains_let(rhs)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +fn lint_if_same_then_else(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> bool {
 +    let mut eq = SpanlessEq::new(cx);
 +    blocks
 +        .array_windows::<2>()
 +        .enumerate()
 +        .fold(true, |all_eq, (i, &[lhs, rhs])| {
 +            if eq.eq_block(lhs, rhs) && !contains_let(conds[i]) && conds.get(i + 1).map_or(true, |e| !contains_let(e)) {
 +                span_lint_and_note(
 +                    cx,
 +                    IF_SAME_THEN_ELSE,
 +                    lhs.span,
 +                    "this `if` has identical blocks",
 +                    Some(rhs.span),
 +                    "same as this",
 +                );
 +                all_eq
 +            } else {
 +                false
 +            }
 +        })
 +}
 +
 +fn lint_branches_sharing_code<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    conds: &[&'tcx Expr<'_>],
- fn scan_block_for_eq(cx: &LateContext<'_>, _conds: &[&Expr<'_>], block: &Block<'_>, blocks: &[&Block<'_>]) -> BlockEq {
++    blocks: &[&'tcx Block<'_>],
 +    expr: &'tcx Expr<'_>,
 +) {
 +    // We only lint ifs with multiple blocks
 +    let &[first_block, ref blocks @ ..] = blocks else {
 +        return;
 +    };
 +    let &[.., last_block] = blocks else {
 +        return;
 +    };
 +
 +    let res = scan_block_for_eq(cx, conds, first_block, blocks);
 +    let sm = cx.tcx.sess.source_map();
 +    let start_suggestion = res.start_span(first_block, sm).map(|span| {
 +        let first_line_span = first_line_of_span(cx, expr.span);
 +        let replace_span = first_line_span.with_hi(span.hi());
 +        let cond_span = first_line_span.until(first_block.span);
 +        let cond_snippet = reindent_multiline(snippet(cx, cond_span, "_"), false, None);
 +        let cond_indent = indent_of(cx, cond_span);
 +        let moved_snippet = reindent_multiline(snippet(cx, span, "_"), true, None);
 +        let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{";
 +        let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent);
 +        (replace_span, suggestion.to_string())
 +    });
 +    let end_suggestion = res.end_span(last_block, sm).map(|span| {
 +        let moved_snipped = reindent_multiline(snippet(cx, span, "_"), true, None);
 +        let indent = indent_of(cx, expr.span.shrink_to_hi());
 +        let suggestion = "}\n".to_string() + &moved_snipped;
 +        let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent);
 +
 +        let span = span.with_hi(last_block.span.hi());
 +        // Improve formatting if the inner block has indention (i.e. normal Rust formatting)
 +        let test_span = Span::new(span.lo() - BytePos(4), span.lo(), span.ctxt(), span.parent());
 +        let span = if snippet_opt(cx, test_span).map_or(false, |snip| snip == "    ") {
 +            span.with_lo(test_span.lo())
 +        } else {
 +            span
 +        };
 +        (span, suggestion.to_string())
 +    });
 +
 +    let (span, msg, end_span) = match (&start_suggestion, &end_suggestion) {
 +        (&Some((span, _)), &Some((end_span, _))) => (
 +            span,
 +            "all if blocks contain the same code at both the start and the end",
 +            Some(end_span),
 +        ),
 +        (&Some((span, _)), None) => (span, "all if blocks contain the same code at the start", None),
 +        (None, &Some((span, _))) => (span, "all if blocks contain the same code at the end", None),
 +        (None, None) => return,
 +    };
 +    span_lint_and_then(cx, BRANCHES_SHARING_CODE, span, msg, |diag| {
 +        if let Some(span) = end_span {
 +            diag.span_note(span, "this code is shared at the end");
 +        }
 +        if let Some((span, sugg)) = start_suggestion {
 +            diag.span_suggestion(
 +                span,
 +                "consider moving these statements before the if",
 +                sugg,
 +                Applicability::Unspecified,
 +            );
 +        }
 +        if let Some((span, sugg)) = end_suggestion {
 +            diag.span_suggestion(
 +                span,
 +                "consider moving these statements after the if",
 +                sugg,
 +                Applicability::Unspecified,
 +            );
 +            if !cx.typeck_results().expr_ty(expr).is_unit() {
 +                diag.note("the end suggestion probably needs some adjustments to use the expression result correctly");
 +            }
 +        }
 +        if check_for_warn_of_moved_symbol(cx, &res.moved_locals, expr) {
 +            diag.warn("some moved values might need to be renamed to avoid wrong references");
 +        }
 +    });
 +}
 +
 +struct BlockEq {
 +    /// The end of the range of equal stmts at the start.
 +    start_end_eq: usize,
 +    /// The start of the range of equal stmts at the end.
 +    end_begin_eq: Option<usize>,
 +    /// The name and id of every local which can be moved at the beginning and the end.
 +    moved_locals: Vec<(HirId, Symbol)>,
 +}
 +impl BlockEq {
 +    fn start_span(&self, b: &Block<'_>, sm: &SourceMap) -> Option<Span> {
 +        match &b.stmts[..self.start_end_eq] {
 +            [first, .., last] => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))),
 +            [s] => Some(sm.stmt_span(s.span, b.span)),
 +            [] => None,
 +        }
 +    }
 +
 +    fn end_span(&self, b: &Block<'_>, sm: &SourceMap) -> Option<Span> {
 +        match (&b.stmts[b.stmts.len() - self.end_begin_eq?..], b.expr) {
 +            ([first, .., last], None) => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))),
 +            ([first, ..], Some(last)) => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))),
 +            ([s], None) => Some(sm.stmt_span(s.span, b.span)),
 +            ([], Some(e)) => Some(walk_chain(e.span, b.span.ctxt())),
 +            ([], None) => None,
 +        }
 +    }
 +}
 +
 +/// If the statement is a local, checks if the bound names match the expected list of names.
 +fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool {
 +    if let StmtKind::Local(l) = s.kind {
 +        let mut i = 0usize;
 +        let mut res = true;
 +        l.pat.each_binding_or_first(&mut |_, _, _, name| {
 +            if names.get(i).map_or(false, |&(_, n)| n == name.name) {
 +                i += 1;
 +            } else {
 +                res = false;
 +            }
 +        });
 +        res && i == names.len()
 +    } else {
 +        false
 +    }
 +}
 +
++/// Checks if the statement modifies or moves any of the given locals.
++fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool {
++    for_each_expr(s, |e| {
++        if let Some(id) = path_to_local(e)
++            && locals.contains(&id)
++            && !capture_local_usage(cx, e).is_imm_ref()
++        {
++            ControlFlow::Break(())
++        } else {
++            ControlFlow::Continue(())
++        }
++    })
++    .is_some()
++}
++
 +/// Checks if the given statement should be considered equal to the statement in the same position
 +/// for each block.
 +fn eq_stmts(
 +    stmt: &Stmt<'_>,
 +    blocks: &[&Block<'_>],
 +    get_stmt: impl for<'a> Fn(&'a Block<'a>) -> Option<&'a Stmt<'a>>,
 +    eq: &mut HirEqInterExpr<'_, '_, '_>,
 +    moved_bindings: &mut Vec<(HirId, Symbol)>,
 +) -> bool {
 +    (if let StmtKind::Local(l) = stmt.kind {
 +        let old_count = moved_bindings.len();
 +        l.pat.each_binding_or_first(&mut |_, id, _, name| {
 +            moved_bindings.push((id, name.name));
 +        });
 +        let new_bindings = &moved_bindings[old_count..];
 +        blocks
 +            .iter()
 +            .all(|b| get_stmt(b).map_or(false, |s| eq_binding_names(s, new_bindings)))
 +    } else {
 +        true
 +    }) && blocks
 +        .iter()
 +        .all(|b| get_stmt(b).map_or(false, |s| eq.eq_stmt(s, stmt)))
 +}
 +
-         .find(|&(i, stmt)| !eq_stmts(stmt, blocks, |b| b.stmts.get(i), &mut eq, &mut moved_locals))
++#[expect(clippy::too_many_lines)]
++fn scan_block_for_eq<'tcx>(
++    cx: &LateContext<'tcx>,
++    conds: &[&'tcx Expr<'_>],
++    block: &'tcx Block<'_>,
++    blocks: &[&'tcx Block<'_>],
++) -> BlockEq {
 +    let mut eq = SpanlessEq::new(cx);
 +    let mut eq = eq.inter_expr();
 +    let mut moved_locals = Vec::new();
 +
++    let mut cond_locals = HirIdSet::default();
++    for &cond in conds {
++        let _: Option<!> = for_each_expr(cond, |e| {
++            if let Some(id) = path_to_local(e) {
++                cond_locals.insert(id);
++            }
++            ControlFlow::Continue(())
++        });
++    }
++
++    let mut local_needs_ordered_drop = false;
 +    let start_end_eq = block
 +        .stmts
 +        .iter()
 +        .enumerate()
++        .find(|&(i, stmt)| {
++            if let StmtKind::Local(l) = stmt.kind
++                && needs_ordered_drop(cx, cx.typeck_results().node_type(l.hir_id))
++            {
++                local_needs_ordered_drop = true;
++                return true;
++            }
++            modifies_any_local(cx, stmt, &cond_locals)
++                || !eq_stmts(stmt, blocks, |b| b.stmts.get(i), &mut eq, &mut moved_locals)
++        })
 +        .map_or(block.stmts.len(), |(i, _)| i);
 +
++    if local_needs_ordered_drop {
++        return BlockEq {
++            start_end_eq,
++            end_begin_eq: None,
++            moved_locals,
++        };
++    }
++
 +    // Walk backwards through the final expression/statements so long as their hashes are equal. Note
 +    // `SpanlessHash` treats all local references as equal allowing locals declared earlier in the block
 +    // to match those in other blocks. e.g. If each block ends with the following the hash value will be
 +    // the same even though each `x` binding will have a different `HirId`:
 +    //     let x = foo();
 +    //     x + 50
 +    let expr_hash_eq = if let Some(e) = block.expr {
 +        let hash = hash_expr(cx, e);
 +        blocks
 +            .iter()
 +            .all(|b| b.expr.map_or(false, |e| hash_expr(cx, e) == hash))
 +    } else {
 +        blocks.iter().all(|b| b.expr.is_none())
 +    };
 +    if !expr_hash_eq {
 +        return BlockEq {
 +            start_end_eq,
 +            end_begin_eq: None,
 +            moved_locals,
 +        };
 +    }
 +    let end_search_start = block.stmts[start_end_eq..]
 +        .iter()
 +        .rev()
 +        .enumerate()
 +        .find(|&(offset, stmt)| {
 +            let hash = hash_stmt(cx, stmt);
 +            blocks.iter().any(|b| {
 +                b.stmts
 +                    // the bounds check will catch the underflow
 +                    .get(b.stmts.len().wrapping_sub(offset + 1))
 +                    .map_or(true, |s| hash != hash_stmt(cx, s))
 +            })
 +        })
 +        .map_or(block.stmts.len() - start_end_eq, |(i, _)| i);
 +
 +    let moved_locals_at_start = moved_locals.len();
 +    let mut i = end_search_start;
 +    let end_begin_eq = block.stmts[block.stmts.len() - end_search_start..]
 +        .iter()
 +        .zip(iter::repeat_with(move || {
 +            let x = i;
 +            i -= 1;
 +            x
 +        }))
 +        .fold(end_search_start, |init, (stmt, offset)| {
 +            if eq_stmts(
 +                stmt,
 +                blocks,
 +                |b| b.stmts.get(b.stmts.len() - offset),
 +                &mut eq,
 +                &mut moved_locals,
 +            ) {
 +                init
 +            } else {
 +                // Clear out all locals seen at the end so far. None of them can be moved.
 +                let stmts = &blocks[0].stmts;
 +                for stmt in &stmts[stmts.len() - init..=stmts.len() - offset] {
 +                    if let StmtKind::Local(l) = stmt.kind {
 +                        l.pat.each_binding_or_first(&mut |_, id, _, _| {
 +                            eq.locals.remove(&id);
 +                        });
 +                    }
 +                }
 +                moved_locals.truncate(moved_locals_at_start);
 +                offset - 1
 +            }
 +        });
 +    if let Some(e) = block.expr {
 +        for block in blocks {
 +            if block.expr.map_or(false, |expr| !eq.eq_expr(expr, e)) {
 +                moved_locals.truncate(moved_locals_at_start);
 +                return BlockEq {
 +                    start_end_eq,
 +                    end_begin_eq: None,
 +                    moved_locals,
 +                };
 +            }
 +        }
 +    }
 +
 +    BlockEq {
 +        start_end_eq,
 +        end_begin_eq: Some(end_begin_eq),
 +        moved_locals,
 +    }
 +}
 +
 +fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbol)], if_expr: &Expr<'_>) -> bool {
 +    get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| {
 +        let ignore_span = block.span.shrink_to_lo().to(if_expr.span);
 +
 +        symbols
 +            .iter()
 +            .filter(|&&(_, name)| !name.as_str().starts_with('_'))
 +            .any(|&(_, name)| {
 +                let mut walker = ContainsName { name, result: false };
 +
 +                // Scan block
 +                block
 +                    .stmts
 +                    .iter()
 +                    .filter(|stmt| !ignore_span.overlaps(stmt.span))
 +                    .for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt));
 +
 +                if let Some(expr) = block.expr {
 +                    intravisit::walk_expr(&mut walker, expr);
 +                }
 +
 +                walker.result
 +            })
 +    })
 +}
 +
 +/// Implementation of `IFS_SAME_COND`.
 +fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
 +    for (i, j) in search_same(conds, |e| hash_expr(cx, e), |lhs, rhs| eq_expr_value(cx, lhs, rhs)) {
 +        span_lint_and_note(
 +            cx,
 +            IFS_SAME_COND,
 +            j.span,
 +            "this `if` has the same condition as a previous `if`",
 +            Some(i.span),
 +            "same as this",
 +        );
 +    }
 +}
 +
 +/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`.
 +fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
 +    let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool {
 +        // Do not lint if any expr originates from a macro
 +        if lhs.span.from_expansion() || rhs.span.from_expansion() {
 +            return false;
 +        }
 +        // Do not spawn warning if `IFS_SAME_COND` already produced it.
 +        if eq_expr_value(cx, lhs, rhs) {
 +            return false;
 +        }
 +        SpanlessEq::new(cx).eq_expr(lhs, rhs)
 +    };
 +
 +    for (i, j) in search_same(conds, |e| hash_expr(cx, e), eq) {
 +        span_lint_and_note(
 +            cx,
 +            SAME_FUNCTIONS_IN_IF_CONDITION,
 +            j.span,
 +            "this `if` has the same function call as a previous `if`",
 +            Some(i.span),
 +            "same as this",
 +        );
 +    }
 +}
index 9b8a481b6eab796157268e671ca3ebcfabbd0cd6,0000000000000000000000000000000000000000..f6ec8fe7edc1914674d6e70eccf5c45711b9ad68
mode 100644,000000..100644
--- /dev/null
@@@ -1,125 -1,0 +1,125 @@@
-     #[clippy::version = "1.61.0"]
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use rustc_ast::ast::{AttrKind, Attribute, Item, ItemKind};
 +use rustc_ast::token::{Token, TokenKind};
 +use rustc_ast::tokenstream::{TokenStream, TokenTree};
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{symbol::sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `crate` as opposed to `$crate` in a macro definition.
 +    ///
 +    /// ### Why is this bad?
 +    /// `crate` refers to the macro call's crate, whereas `$crate` refers to the macro definition's
 +    /// crate. Rarely is the former intended. See:
 +    /// https://doc.rust-lang.org/reference/macros-by-example.html#hygiene
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #[macro_export]
 +    /// macro_rules! print_message {
 +    ///     () => {
 +    ///         println!("{}", crate::MESSAGE);
 +    ///     };
 +    /// }
 +    /// pub const MESSAGE: &str = "Hello!";
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// #[macro_export]
 +    /// macro_rules! print_message {
 +    ///     () => {
 +    ///         println!("{}", $crate::MESSAGE);
 +    ///     };
 +    /// }
 +    /// pub const MESSAGE: &str = "Hello!";
 +    /// ```
 +    ///
 +    /// Note that if the use of `crate` is intentional, an `allow` attribute can be applied to the
 +    /// macro definition, e.g.:
 +    /// ```rust,ignore
 +    /// #[allow(clippy::crate_in_macro_def)]
 +    /// macro_rules! ok { ... crate::foo ... }
 +    /// ```
++    #[clippy::version = "1.62.0"]
 +    pub CRATE_IN_MACRO_DEF,
 +    suspicious,
 +    "using `crate` in a macro definition"
 +}
 +declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]);
 +
 +impl EarlyLintPass for CrateInMacroDef {
 +    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
 +        if_chain! {
 +            if item.attrs.iter().any(is_macro_export);
 +            if let ItemKind::MacroDef(macro_def) = &item.kind;
 +            let tts = macro_def.body.inner_tokens();
 +            if let Some(span) = contains_unhygienic_crate_reference(&tts);
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    CRATE_IN_MACRO_DEF,
 +                    span,
 +                    "`crate` references the macro call's crate",
 +                    "to reference the macro definition's crate, use",
 +                    String::from("$crate"),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn is_macro_export(attr: &Attribute) -> bool {
 +    if_chain! {
 +        if let AttrKind::Normal(attr_item, _) = &attr.kind;
 +        if let [segment] = attr_item.path.segments.as_slice();
 +        then {
 +            segment.ident.name == sym::macro_export
 +        } else {
 +            false
 +        }
 +    }
 +}
 +
 +fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> {
 +    let mut prev_is_dollar = false;
 +    let mut cursor = tts.trees();
 +    while let Some(curr) = cursor.next() {
 +        if_chain! {
 +            if !prev_is_dollar;
 +            if let Some(span) = is_crate_keyword(curr);
 +            if let Some(next) = cursor.look_ahead(0);
 +            if is_token(next, &TokenKind::ModSep);
 +            then {
 +                return Some(span);
 +            }
 +        }
 +        if let TokenTree::Delimited(_, _, tts) = &curr {
 +            let span = contains_unhygienic_crate_reference(tts);
 +            if span.is_some() {
 +                return span;
 +            }
 +        }
 +        prev_is_dollar = is_token(curr, &TokenKind::Dollar);
 +    }
 +    None
 +}
 +
 +fn is_crate_keyword(tt: &TokenTree) -> Option<Span> {
 +    if_chain! {
 +        if let TokenTree::Token(Token { kind: TokenKind::Ident(symbol, _), span }) = tt;
 +        if symbol.as_str() == "crate";
 +        then { Some(*span) } else { None }
 +    }
 +}
 +
 +fn is_token(tt: &TokenTree, kind: &TokenKind) -> bool {
 +    if let TokenTree::Token(Token { kind: other, .. }) = tt {
 +        kind == other
 +    } else {
 +        false
 +    }
 +}
index 0f4a2f79ac5d7a95c173fcf3d72593486ad324e5,0000000000000000000000000000000000000000..8c7cf7748be1359440197d3692fb6daaf677c024
mode 100644,000000..100644
--- /dev/null
@@@ -1,1126 -1,0 +1,1147 @@@
-     self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, GenericArg, HirId, ImplItem,
-     ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 +use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 +use clippy_utils::sugg::has_enclosing_paren;
 +use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, variant_of_res};
 +use clippy_utils::{get_parent_expr, is_lint_allowed, 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::{
-                     let output = cx
-                         .tcx
-                         .fn_sig(cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()))
-                         .skip_binder()
-                         .output();
-                     Some(if !output.is_ref() {
-                         Position::Other(precedence)
-                     } else if output.has_placeholders() || output.has_opaque_types() {
-                         Position::ReborrowStable(precedence)
-                     } else {
-                         Position::DerefStable(precedence)
-                     })
++    self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId,
++    ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
 +    TraitItemKind, TyKind, UnOp,
 +};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
 +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable, TypeckResults};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{symbol::sym, Span, Symbol};
 +use rustc_trait_selection::infer::InferCtxtExt;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for explicit `deref()` or `deref_mut()` method calls.
 +    ///
 +    /// ### Why is this bad?
 +    /// Dereferencing by `&*x` or `&mut *x` is clearer and more concise,
 +    /// when not part of a method chain.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::ops::Deref;
 +    /// let a: &mut String = &mut String::from("foo");
 +    /// let b: &str = a.deref();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let a: &mut String = &mut String::from("foo");
 +    /// let b = &*a;
 +    /// ```
 +    ///
 +    /// This lint excludes:
 +    /// ```rust,ignore
 +    /// let _ = d.unwrap().deref();
 +    /// ```
 +    #[clippy::version = "1.44.0"]
 +    pub EXPLICIT_DEREF_METHODS,
 +    pedantic,
 +    "Explicit use of deref or deref_mut method while not in a method chain."
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for address of operations (`&`) that are going to
 +    /// be dereferenced immediately by the compiler.
 +    ///
 +    /// ### Why is this bad?
 +    /// Suggests that the receiver of the expression borrows
 +    /// the expression.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn fun(_a: &i32) {}
 +    ///
 +    /// let x: &i32 = &&&&&&5;
 +    /// fun(&x);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # fn fun(_a: &i32) {}
 +    /// let x: &i32 = &5;
 +    /// fun(x);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEEDLESS_BORROW,
 +    style,
 +    "taking a reference that is going to be automatically dereferenced"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `ref` bindings which create a reference to a reference.
 +    ///
 +    /// ### Why is this bad?
 +    /// The address-of operator at the use site is clearer about the need for a reference.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = Some("");
 +    /// if let Some(ref x) = x {
 +    ///     // use `x` here
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = Some("");
 +    /// if let Some(x) = x {
 +    ///     // use `&x` here
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.54.0"]
 +    pub REF_BINDING_TO_REFERENCE,
 +    pedantic,
 +    "`ref` binding to a reference"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for dereferencing expressions which would be covered by auto-deref.
 +    ///
 +    /// ### Why is this bad?
 +    /// This unnecessarily complicates the code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = String::new();
 +    /// let y: &str = &*x;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = String::new();
 +    /// let y: &str = &x;
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub EXPLICIT_AUTO_DEREF,
 +    complexity,
 +    "dereferencing when the compiler would automatically dereference"
 +}
 +
 +impl_lint_pass!(Dereferencing => [
 +    EXPLICIT_DEREF_METHODS,
 +    NEEDLESS_BORROW,
 +    REF_BINDING_TO_REFERENCE,
 +    EXPLICIT_AUTO_DEREF,
 +]);
 +
 +#[derive(Default)]
 +pub struct Dereferencing {
 +    state: Option<(State, StateData)>,
 +
 +    // While parsing a `deref` method call in ufcs form, the path to the function is itself an
 +    // expression. This is to store the id of that expression so it can be skipped when
 +    // `check_expr` is called for it.
 +    skip_expr: Option<HirId>,
 +
 +    /// The body the first local was found in. Used to emit lints when the traversal of the body has
 +    /// been finished. Note we can't lint at the end of every body as they can be nested within each
 +    /// other.
 +    current_body: Option<BodyId>,
 +    /// The list of locals currently being checked by the lint.
 +    /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
 +    /// This is needed for or patterns where one of the branches can be linted, but another can not
 +    /// be.
 +    ///
 +    /// e.g. `m!(x) | Foo::Bar(ref x)`
 +    ref_locals: FxIndexMap<HirId, Option<RefPat>>,
 +}
 +
 +struct StateData {
 +    /// Span of the top level expression
 +    span: Span,
 +    hir_id: HirId,
 +    position: Position,
 +}
 +
 +struct DerefedBorrow {
 +    count: usize,
 +    msg: &'static str,
 +}
 +
 +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 {
 +        // Span and id of the top-level deref expression if the parent expression is a borrow.
 +        deref_span_id: Option<(Span, HirId)>,
 +    },
 +    ExplicitDerefField {
 +        name: Symbol,
 +    },
 +    Reborrow {
 +        deref_span: Span,
 +        deref_hir_id: HirId,
 +    },
 +    Borrow,
 +}
 +
 +// A reference operation considered by this lint pass
 +enum RefOp {
 +    Method(Mutability),
 +    Deref,
 +    AddrOf,
 +}
 +
 +struct RefPat {
 +    /// Whether every usage of the binding is dereferenced.
 +    always_deref: bool,
 +    /// The spans of all the ref bindings for this local.
 +    spans: Vec<Span>,
 +    /// The applicability of this suggestion.
 +    app: Applicability,
 +    /// All the replacements which need to be made.
 +    replacements: Vec<(Span, String)>,
 +    /// The [`HirId`] that the lint should be emitted at.
 +    hir_id: HirId,
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for Dereferencing {
 +    #[expect(clippy::too_many_lines)]
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        // Skip path expressions from deref calls. e.g. `Deref::deref(e)`
 +        if Some(expr.hir_id) == self.skip_expr.take() {
 +            return;
 +        }
 +
 +        if let Some(local) = path_to_local(expr) {
 +            self.check_local_usage(cx, expr, local);
 +        }
 +
 +        // Stop processing sub expressions when a macro call is seen
 +        if expr.span.from_expansion() {
 +            if let Some((state, data)) = self.state.take() {
 +                report(cx, expr, state, data);
 +            }
 +            return;
 +        }
 +
 +        let typeck = cx.typeck_results();
 +        let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) {
 +            x
 +        } else {
 +            // The whole chain of reference operations has been seen
 +            if let Some((state, data)) = self.state.take() {
 +                report(cx, expr, state, data);
 +            }
 +            return;
 +        };
 +
 +        match (self.state.take(), kind) {
 +            (None, kind) => {
 +                let expr_ty = typeck.expr_ty(expr);
 +                let (position, adjustments) = walk_parents(cx, expr);
 +
 +                match kind {
 +                    RefOp::Deref => {
 +                        if let Position::FieldAccess(name) = position
 +                            && !ty_contains_field(typeck.expr_ty(sub_expr), name)
 +                        {
 +                            self.state = Some((
 +                                State::ExplicitDerefField { name },
 +                                StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                            ));
 +                        } else if position.is_deref_stable() {
 +                            self.state = Some((
 +                                State::ExplicitDeref { deref_span_id: None },
 +                                StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                            ));
 +                        }
 +                    }
 +                    RefOp::Method(target_mut)
 +                        if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
 +                            && position.lint_explicit_deref() =>
 +                    {
 +                        self.state = Some((
 +                            State::DerefMethod {
 +                                ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) {
 +                                    0
 +                                } else {
 +                                    1
 +                                },
 +                                is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
 +                                target_mut,
 +                            },
 +                            StateData {
 +                                span: expr.span,
 +                                hir_id: expr.hir_id,
 +                                position
 +                            },
 +                        ));
 +                    },
 +                    RefOp::AddrOf => {
 +                        // 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 (required_refs, msg) = if position.can_auto_borrow() {
 +                            (1, if deref_count == 1 { borrow_msg } else { deref_msg })
 +                        } 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)
 +                            } else {
 +                                (2, deref_msg)
 +                            }
 +                        } else {
 +                            (2, deref_msg)
 +                        };
 +
 +                        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,
 +                                }),
 +                                StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                            ));
 +                        } else if position.is_deref_stable() {
 +                            self.state = Some((
 +                                State::Borrow,
 +                                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) => {
 +                let position = data.position;
 +                report(cx, expr, State::DerefedBorrow(state), data);
 +                if position.is_deref_stable() {
 +                    self.state = Some((
 +                        State::Borrow,
 +                        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 { deref_span_id: None },
 +                        StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                    ));
 +                }
 +            },
 +
 +            (Some((State::Borrow, data)), RefOp::Deref) => {
 +                if typeck.expr_ty(sub_expr).is_ref() {
 +                    self.state = Some((
 +                        State::Reborrow {
 +                            deref_span: expr.span,
 +                            deref_hir_id: expr.hir_id,
 +                        },
 +                        data,
 +                    ));
 +                } else {
 +                    self.state = Some((
 +                        State::ExplicitDeref {
 +                            deref_span_id: Some((expr.span, expr.hir_id)),
 +                        },
 +                        data,
 +                    ));
 +                }
 +            },
 +            (
 +                Some((
 +                    State::Reborrow {
 +                        deref_span,
 +                        deref_hir_id,
 +                    },
 +                    data,
 +                )),
 +                RefOp::Deref,
 +            ) => {
 +                self.state = Some((
 +                    State::ExplicitDeref {
 +                        deref_span_id: Some((deref_span, deref_hir_id)),
 +                    },
 +                    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 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;
 +        }
 +    }
 +}
 +
 +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, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)),
 +        _ => return None,
 +    };
 +    if tcx.is_diagnostic_item(sym::deref_method, def_id) {
 +        Some((RefOp::Method(Mutability::Not), arg))
 +    } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? {
 +        Some((RefOp::Method(Mutability::Mut), arg))
 +    } else {
 +        None
 +    }
 +}
 +
 +// Checks whether the type for a deref call actually changed the type, not just the mutability of
 +// the reference.
 +fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
 +    match (result_ty.kind(), arg_ty.kind()) {
 +        (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => result_ty == arg_ty,
 +
 +        // The result type for a deref method is always a reference
 +        // Not matching the previous pattern means the argument type is not a reference
 +        // This means that the type did change
 +        _ => false,
 +    }
 +}
 +
 +/// The position of an expression relative to it's parent.
 +#[derive(Clone, Copy)]
 +enum Position {
 +    MethodReceiver,
 +    /// The method is defined on a reference type. e.g. `impl Foo for &T`
 +    MethodReceiverRefImpl,
 +    Callee,
 +    FieldAccess(Symbol),
 +    Postfix,
 +    Deref,
 +    /// Any other location which will trigger auto-deref to a specific time.
 +    DerefStable(i8),
 +    /// Any other location which will trigger auto-reborrowing.
 +    ReborrowStable(i8),
 +    Other(i8),
 +}
 +impl Position {
 +    fn is_deref_stable(self) -> bool {
 +        matches!(self, Self::DerefStable(_))
 +    }
 +
 +    fn is_reborrow_stable(self) -> bool {
 +        matches!(self, Self::DerefStable(_) | Self::ReborrowStable(_))
 +    }
 +
 +    fn can_auto_borrow(self) -> bool {
 +        matches!(self, Self::MethodReceiver | Self::FieldAccess(_) | Self::Callee)
 +    }
 +
 +    fn lint_explicit_deref(self) -> bool {
 +        matches!(self, Self::Other(_) | Self::DerefStable(_) | Self::ReborrowStable(_))
 +    }
 +
 +    fn precedence(self) -> i8 {
 +        match self {
 +            Self::MethodReceiver
 +            | Self::MethodReceiverRefImpl
 +            | Self::Callee
 +            | Self::FieldAccess(_)
 +            | Self::Postfix => PREC_POSTFIX,
 +            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.
 +#[allow(clippy::too_many_lines)]
 +fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (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(ty, precedence))
 +            },
 +            Node::Item(&Item {
 +                kind: ItemKind::Static(..) | ItemKind::Const(..),
 +                def_id,
 +                span,
 +                ..
 +            })
 +            | Node::TraitItem(&TraitItem {
 +                kind: TraitItemKind::Const(..),
 +                def_id,
 +                span,
 +                ..
 +            })
 +            | Node::ImplItem(&ImplItem {
 +                kind: ImplItemKind::Const(..),
 +                def_id,
 +                span,
 +                ..
 +            }) if span.ctxt() == ctxt => {
 +                let ty = cx.tcx.type_of(def_id);
 +                Some(if ty.is_ref() {
 +                    Position::DerefStable(precedence)
 +                } else {
 +                    Position::Other(precedence)
 +                })
 +            },
 +
 +            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.fn_sig(def_id.to_def_id()).skip_binder().output();
 +                Some(if !output.is_ref() {
 +                    Position::Other(precedence)
 +                } else if output.has_placeholders() || output.has_opaque_types() {
 +                    Position::ReborrowStable(precedence)
 +                } else {
 +                    Position::DerefStable(precedence)
 +                })
 +            },
 +
 +            Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
 +                ExprKind::Ret(_) => {
-                 ExprKind::Call(func, _) if func.hir_id == child_id => (child_id == e.hir_id).then(|| Position::Callee),
++                    let owner_id = cx.tcx.hir().body_owner(cx.enclosing_body.unwrap());
++                    Some(
++                        if let Node::Expr(Expr {
++                            kind: ExprKind::Closure(&Closure { fn_decl, .. }),
++                            ..
++                        }) = cx.tcx.hir().get(owner_id)
++                        {
++                            match fn_decl.output {
++                                FnRetTy::Return(ty) => binding_ty_auto_deref_stability(ty, precedence),
++                                FnRetTy::DefaultReturn(_) => Position::Other(precedence),
++                            }
++                        } else {
++                            let output = cx
++                                .tcx
++                                .fn_sig(cx.tcx.hir().local_def_id(owner_id))
++                                .skip_binder()
++                                .output();
++                            if !output.is_ref() {
++                                Position::Other(precedence)
++                            } else if output.has_placeholders() || output.has_opaque_types() {
++                                Position::ReborrowStable(precedence)
++                            } else {
++                                Position::DerefStable(precedence)
++                            }
++                        },
++                    )
++                },
++                ExprKind::Call(func, _) if func.hir_id == child_id => {
++                    (child_id == e.hir_id).then_some(Position::Callee)
 +                },
-                                 && let subs = cx.typeck_results().node_substs_opt(child_id).unwrap_or_else(
-                                     || cx.tcx.mk_substs([].iter())
-                                 ) && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
 +                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(ty) => binding_ty_auto_deref_stability(ty, precedence),
 +                        None => param_auto_deref_stability(ty.skip_binder(), precedence),
 +                    }),
 +                ExprKind::MethodCall(_, args, _) => {
 +                    let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
 +                    args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
 +                        if i == 0 {
 +                            // Check for calls to trait methods where the trait is implemented on a reference.
 +                            // Two cases need to be handled:
 +                            // * `self` methods on `&T` will never have auto-borrow
 +                            // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
 +                            //   priority.
 +                            if e.hir_id != child_id {
 +                                Position::ReborrowStable(precedence)
 +                            } else if let Some(trait_id) = cx.tcx.trait_of_item(id)
 +                                && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
 +                                && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
++                                && let subs = match cx
++                                    .typeck_results()
++                                    .node_substs_opt(parent.hir_id)
++                                    .and_then(|subs| subs.get(1..))
++                                {
++                                    Some(subs) => cx.tcx.mk_substs(subs.iter().copied()),
++                                    None => cx.tcx.mk_substs([].iter()),
++                                } && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
 +                                    // Trait methods taking `&self`
 +                                    sub_ty
 +                                } else {
 +                                    // Trait methods taking `self`
 +                                    arg_ty
 +                                } && impl_ty.is_ref()
 +                                && cx.tcx.infer_ctxt().enter(|infcx|
 +                                    infcx
 +                                        .type_implements_trait(trait_id, impl_ty, subs, cx.param_env)
 +                                        .must_apply_modulo_regions()
 +                                )
 +                            {
 +                                Position::MethodReceiverRefImpl
 +                            } else {
 +                                Position::MethodReceiver
 +                            }
 +                        } else {
 +                            param_auto_deref_stability(cx.tcx.fn_sig(id).skip_binder().inputs()[i], precedence)
 +                        }
 +                    })
 +                },
 +                ExprKind::Struct(path, fields, _) => {
 +                    let variant = variant_of_res(cx, cx.qpath_res(path, parent.hir_id));
 +                    fields
 +                        .iter()
 +                        .find(|f| f.expr.hir_id == child_id)
 +                        .zip(variant)
 +                        .and_then(|(field, variant)| variant.fields.iter().find(|f| f.name == field.ident.name))
 +                        .map(|field| param_auto_deref_stability(cx.tcx.type_of(field.did), precedence))
 +                },
 +                ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)),
 +                ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),
 +                ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
 +                | ExprKind::Index(child, _)
 +                    if child.hir_id == e.hir_id =>
 +                {
 +                    Some(Position::Postfix)
 +                },
 +                _ if child_id == e.hir_id => {
 +                    precedence = parent.precedence().order();
 +                    None
 +                },
 +                _ => None,
 +            },
 +            _ => None,
 +        }
 +    })
 +    .unwrap_or(Position::Other(precedence));
 +    (position, adjustments)
 +}
 +
 +// 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(ty: &hir::Ty<'_>, precedence: i8) -> 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)
 +                }
 +            },
 +            TyKind::Slice(_)
 +            | TyKind::Array(..)
 +            | TyKind::BareFn(_)
 +            | TyKind::Never
 +            | TyKind::Tup(_)
 +            | TyKind::Ptr(_)
 +            | TyKind::TraitObject(..)
 +            | TyKind::Path(_) => Position::DerefStable(precedence),
 +            TyKind::OpaqueDef(..)
 +            | TyKind::Infer
 +            | TyKind::Typeof(..)
 +            | 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 a type is stable when switching to auto dereferencing,
 +fn param_auto_deref_stability(ty: Ty<'_>, precedence: i8) -> Position {
 +    let ty::Ref(_, mut ty, _) = *ty.kind() else {
 +        return Position::Other(precedence);
 +    };
 +
 +    loop {
 +        break match *ty.kind() {
 +            ty::Ref(_, ref_ty, _) => {
 +                ty = ref_ty;
 +                continue;
 +            },
 +            ty::Infer(_)
 +            | ty::Error(_)
 +            | ty::Param(_)
 +            | ty::Bound(..)
 +            | ty::Opaque(..)
 +            | ty::Placeholder(_)
 +            | ty::Dynamic(..) => Position::ReborrowStable(precedence),
 +            ty::Adt(..) if ty.has_placeholders() || ty.has_param_types_or_consts() => {
 +                Position::ReborrowStable(precedence)
 +            },
 +            ty::Adt(..)
 +            | ty::Bool
 +            | ty::Char
 +            | ty::Int(_)
 +            | ty::Uint(_)
 +            | ty::Float(_)
 +            | ty::Foreign(_)
 +            | ty::Str
 +            | ty::Array(..)
 +            | ty::Slice(..)
 +            | ty::RawPtr(..)
 +            | ty::FnDef(..)
 +            | ty::FnPtr(_)
 +            | ty::Closure(..)
 +            | ty::Generator(..)
 +            | ty::GeneratorWitness(..)
 +            | ty::Never
 +            | ty::Tuple(_)
 +            | ty::Projection(_) => Position::DerefStable(precedence),
 +        };
 +    }
 +}
 +
 +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, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
 +            span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
 +                let sugg = if !snip_is_macro
 +                    && expr.precedence().order() < data.position.precedence()
 +                    && !has_enclosing_paren(&snip)
 +                {
 +                    format!("({})", snip)
 +                } else {
 +                    snip.into()
 +                };
 +                diag.span_suggestion(data.span, "change this to", sugg, app);
 +            });
 +        },
 +        State::ExplicitDeref { deref_span_id } => {
 +            let (span, hir_id, precedence) = if let Some((span, hir_id)) = deref_span_id
 +                && !cx.typeck_results().expr_ty(expr).is_ref()
 +            {
 +                (span, hir_id, PREC_PREFIX)
 +            } else {
 +                (data.span, data.hir_id, data.position.precedence())
 +            };
 +            span_lint_hir_and_then(
 +                cx,
 +                EXPLICIT_AUTO_DEREF,
 +                hir_id,
 +                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, span.ctxt(), "..", &mut app);
 +                    let sugg =
 +                        if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
 +                            format!("({})", snip)
 +                        } else {
 +                            snip.into()
 +                        };
 +                    diag.span_suggestion(span, "try this", sugg, app);
 +                },
 +            );
 +        },
 +        State::ExplicitDerefField { .. } => {
 +            span_lint_hir_and_then(
 +                cx,
 +                EXPLICIT_AUTO_DEREF,
 +                data.hir_id,
 +                data.span,
 +                "deref which would be done by auto-deref",
 +                |diag| {
 +                    let mut app = Applicability::MachineApplicable;
 +                    let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
 +                    diag.span_suggestion(data.span, "try this", snip.into_owned(), app);
 +                },
 +            );
 +        },
 +        State::Borrow | State::Reborrow { .. } => (),
 +    }
 +}
 +
 +impl Dereferencing {
 +    fn check_local_usage<'tcx>(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) {
 +        if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
 +            if let Some(pat) = outer_pat {
 +                // Check for auto-deref
 +                if !matches!(
 +                    cx.typeck_results().expr_adjustments(e),
 +                    [
 +                        Adjustment {
 +                            kind: Adjust::Deref(_),
 +                            ..
 +                        },
 +                        Adjustment {
 +                            kind: Adjust::Deref(_),
 +                            ..
 +                        },
 +                        ..
 +                    ]
 +                ) {
 +                    match get_parent_expr(cx, e) {
 +                        // Field accesses are the same no matter the number of references.
 +                        Some(Expr {
 +                            kind: ExprKind::Field(..),
 +                            ..
 +                        }) => (),
 +                        Some(&Expr {
 +                            span,
 +                            kind: ExprKind::Unary(UnOp::Deref, _),
 +                            ..
 +                        }) if !span.from_expansion() => {
 +                            // Remove explicit deref.
 +                            let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
 +                            pat.replacements.push((span, snip.into()));
 +                        },
 +                        Some(parent) if !parent.span.from_expansion() => {
 +                            // Double reference might be needed at this point.
 +                            if parent.precedence().order() == PREC_POSTFIX {
 +                                // Parentheses would be needed here, don't lint.
 +                                *outer_pat = None;
 +                            } else {
 +                                pat.always_deref = false;
 +                                let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
 +                                pat.replacements.push((e.span, format!("&{}", snip)));
 +                            }
 +                        },
 +                        _ if !e.span.from_expansion() => {
 +                            // Double reference might be needed at this point.
 +                            pat.always_deref = false;
 +                            let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
 +                            pat.replacements.push((e.span, format!("&{}", snip)));
 +                        },
 +                        // Edge case for macros. The span of the identifier will usually match the context of the
 +                        // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
 +                        // macros
 +                        _ => *outer_pat = None,
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
index 28f218a8e344f1228585e75829795a9683580018,0000000000000000000000000000000000000000..a982990e4186c9cc6c2aef5bb9477d9a7b96651a
mode 100644,000000..100644
--- /dev/null
@@@ -1,528 -1,0 +1,528 @@@
-     #[clippy::version = "1.62.0"]
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::paths;
 +use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
 +use clippy_utils::{is_lint_allowed, match_def_path};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
 +use rustc_hir::{
 +    self as hir, BlockCheckMode, BodyId, Constness, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource,
 +    Unsafety,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::traits::Reveal;
 +use rustc_middle::ty::{
 +    self, Binder, BoundConstness, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef,
 +    Ty, TyCtxt, Visibility,
 +};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for deriving `Hash` but implementing `PartialEq`
 +    /// explicitly or vice versa.
 +    ///
 +    /// ### Why is this bad?
 +    /// The implementation of these traits must agree (for
 +    /// example for use with `HashMap`) so it’s probably a bad idea to use a
 +    /// default-generated `Hash` implementation with an explicitly defined
 +    /// `PartialEq`. In particular, the following must hold for any type:
 +    ///
 +    /// ```text
 +    /// k1 == k2 ⇒ hash(k1) == hash(k2)
 +    /// ```
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// #[derive(Hash)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialEq for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DERIVE_HASH_XOR_EQ,
 +    correctness,
 +    "deriving `Hash` but implementing `PartialEq` explicitly"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for deriving `Ord` but implementing `PartialOrd`
 +    /// explicitly or vice versa.
 +    ///
 +    /// ### Why is this bad?
 +    /// The implementation of these traits must agree (for
 +    /// example for use with `sort`) so it’s probably a bad idea to use a
 +    /// default-generated `Ord` implementation with an explicitly defined
 +    /// `PartialOrd`. In particular, the following must hold for any type
 +    /// implementing `Ord`:
 +    ///
 +    /// ```text
 +    /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()
 +    /// ```
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// #[derive(Ord, PartialEq, Eq)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialOrd for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// #[derive(PartialEq, Eq)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialOrd for Foo {
 +    ///     fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
 +    ///        Some(self.cmp(other))
 +    ///     }
 +    /// }
 +    ///
 +    /// impl Ord for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    /// or, if you don't need a custom ordering:
 +    /// ```rust,ignore
 +    /// #[derive(Ord, PartialOrd, PartialEq, Eq)]
 +    /// struct Foo;
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub DERIVE_ORD_XOR_PARTIAL_ORD,
 +    correctness,
 +    "deriving `Ord` but implementing `PartialOrd` explicitly"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for explicit `Clone` implementations for `Copy`
 +    /// types.
 +    ///
 +    /// ### Why is this bad?
 +    /// To avoid surprising behavior, these traits should
 +    /// agree and the behavior of `Copy` cannot be overridden. In almost all
 +    /// situations a `Copy` type should have a `Clone` implementation that does
 +    /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
 +    /// gets you.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// #[derive(Copy)]
 +    /// struct Foo;
 +    ///
 +    /// impl Clone for Foo {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXPL_IMPL_CLONE_ON_COPY,
 +    pedantic,
 +    "implementing `Clone` explicitly on `Copy` types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for deriving `serde::Deserialize` on a type that
 +    /// has methods using `unsafe`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Deriving `serde::Deserialize` will create a constructor
 +    /// that may violate invariants hold by another constructor.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// use serde::Deserialize;
 +    ///
 +    /// #[derive(Deserialize)]
 +    /// pub struct Foo {
 +    ///     // ..
 +    /// }
 +    ///
 +    /// impl Foo {
 +    ///     pub fn new() -> Self {
 +    ///         // setup here ..
 +    ///     }
 +    ///
 +    ///     pub unsafe fn parts() -> (&str, &str) {
 +    ///         // assumes invariants hold
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub UNSAFE_DERIVE_DESERIALIZE,
 +    pedantic,
 +    "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for types that derive `PartialEq` and could implement `Eq`.
 +    ///
 +    /// ### Why is this bad?
 +    /// If a type `T` derives `PartialEq` and all of its members implement `Eq`,
 +    /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used
 +    /// in APIs that require `Eq` types. It also allows structs containing `T` to derive
 +    /// `Eq` themselves.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #[derive(PartialEq)]
 +    /// struct Foo {
 +    ///     i_am_eq: i32,
 +    ///     i_am_eq_too: Vec<String>,
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// #[derive(PartialEq, Eq)]
 +    /// struct Foo {
 +    ///     i_am_eq: i32,
 +    ///     i_am_eq_too: Vec<String>,
 +    /// }
 +    /// ```
++    #[clippy::version = "1.63.0"]
 +    pub DERIVE_PARTIAL_EQ_WITHOUT_EQ,
 +    style,
 +    "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
 +}
 +
 +declare_lint_pass!(Derive => [
 +    EXPL_IMPL_CLONE_ON_COPY,
 +    DERIVE_HASH_XOR_EQ,
 +    DERIVE_ORD_XOR_PARTIAL_ORD,
 +    UNSAFE_DERIVE_DESERIALIZE,
 +    DERIVE_PARTIAL_EQ_WITHOUT_EQ
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Derive {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if let ItemKind::Impl(Impl {
 +            of_trait: Some(ref trait_ref),
 +            ..
 +        }) = item.kind
 +        {
 +            let ty = cx.tcx.type_of(item.def_id);
 +            let is_automatically_derived = cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived);
 +
 +            check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
 +            check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
 +
 +            if is_automatically_derived {
 +                check_unsafe_derive_deserialize(cx, item, trait_ref, ty);
 +                check_partial_eq_without_eq(cx, item.span, trait_ref, ty);
 +            } else {
 +                check_copy_clone(cx, item, trait_ref, ty);
 +            }
 +        }
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_HASH_XOR_EQ` lint.
 +fn check_hash_peq<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
 +    trait_ref: &hir::TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +    hash_is_automatically_derived: bool,
 +) {
 +    if_chain! {
 +        if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait();
 +        if let Some(def_id) = trait_ref.trait_def_id();
 +        if cx.tcx.is_diagnostic_item(sym::Hash, def_id);
 +        then {
 +            // Look for the PartialEq implementations for `ty`
 +            cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
 +                let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
 +
 +                if peq_is_automatically_derived == hash_is_automatically_derived {
 +                    return;
 +                }
 +
 +                let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
 +
 +                // Only care about `impl PartialEq<Foo> for Foo`
 +                // For `impl PartialEq<B> for A, input_types is [A, B]
 +                if trait_ref.substs.type_at(1) == ty {
 +                    let mess = if peq_is_automatically_derived {
 +                        "you are implementing `Hash` explicitly but have derived `PartialEq`"
 +                    } else {
 +                        "you are deriving `Hash` but have implemented `PartialEq` explicitly"
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        DERIVE_HASH_XOR_EQ,
 +                        span,
 +                        mess,
 +                        |diag| {
 +                            if let Some(local_def_id) = impl_id.as_local() {
 +                                let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +                                diag.span_note(
 +                                    cx.tcx.hir().span(hir_id),
 +                                    "`PartialEq` implemented here"
 +                                );
 +                            }
 +                        }
 +                    );
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
 +fn check_ord_partial_ord<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
 +    trait_ref: &hir::TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +    ord_is_automatically_derived: bool,
 +) {
 +    if_chain! {
 +        if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord);
 +        if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait();
 +        if let Some(def_id) = &trait_ref.trait_def_id();
 +        if *def_id == ord_trait_def_id;
 +        then {
 +            // Look for the PartialOrd implementations for `ty`
 +            cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
 +                let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
 +
 +                if partial_ord_is_automatically_derived == ord_is_automatically_derived {
 +                    return;
 +                }
 +
 +                let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
 +
 +                // Only care about `impl PartialOrd<Foo> for Foo`
 +                // For `impl PartialOrd<B> for A, input_types is [A, B]
 +                if trait_ref.substs.type_at(1) == ty {
 +                    let mess = if partial_ord_is_automatically_derived {
 +                        "you are implementing `Ord` explicitly but have derived `PartialOrd`"
 +                    } else {
 +                        "you are deriving `Ord` but have implemented `PartialOrd` explicitly"
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        DERIVE_ORD_XOR_PARTIAL_ORD,
 +                        span,
 +                        mess,
 +                        |diag| {
 +                            if let Some(local_def_id) = impl_id.as_local() {
 +                                let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +                                diag.span_note(
 +                                    cx.tcx.hir().span(hir_id),
 +                                    "`PartialOrd` implemented here"
 +                                );
 +                            }
 +                        }
 +                    );
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
 +fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
 +    let clone_id = match cx.tcx.lang_items().clone_trait() {
 +        Some(id) if trait_ref.trait_def_id() == Some(id) => id,
 +        _ => return,
 +    };
 +    let copy_id = match cx.tcx.lang_items().copy_trait() {
 +        Some(id) => id,
 +        None => return,
 +    };
 +    let (ty_adt, ty_subs) = match *ty.kind() {
 +        // Unions can't derive clone.
 +        ty::Adt(adt, subs) if !adt.is_union() => (adt, subs),
 +        _ => return,
 +    };
 +    // If the current self type doesn't implement Copy (due to generic constraints), search to see if
 +    // there's a Copy impl for any instance of the adt.
 +    if !is_copy(cx, ty) {
 +        if ty_subs.non_erasable_generics().next().is_some() {
 +            let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(&copy_id).map_or(false, |impls| {
 +                impls
 +                    .iter()
 +                    .any(|&id| matches!(cx.tcx.type_of(id).kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did()))
 +            });
 +            if !has_copy_impl {
 +                return;
 +            }
 +        } else {
 +            return;
 +        }
 +    }
 +    // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for
 +    // this impl.
 +    if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) {
 +        return;
 +    }
 +
 +    span_lint_and_note(
 +        cx,
 +        EXPL_IMPL_CLONE_ON_COPY,
 +        item.span,
 +        "you are implementing `Clone` explicitly on a `Copy` type",
 +        Some(item.span),
 +        "consider deriving `Clone` or removing `Copy`",
 +    );
 +}
 +
 +/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.
 +fn check_unsafe_derive_deserialize<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    item: &Item<'_>,
 +    trait_ref: &hir::TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +) {
 +    fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
 +        let mut visitor = UnsafeVisitor { cx, has_unsafe: false };
 +        walk_item(&mut visitor, item);
 +        visitor.has_unsafe
 +    }
 +
 +    if_chain! {
 +        if let Some(trait_def_id) = trait_ref.trait_def_id();
 +        if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE);
 +        if let ty::Adt(def, _) = ty.kind();
 +        if let Some(local_def_id) = def.did().as_local();
 +        let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +        if !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
 +        if cx.tcx.inherent_impls(def.did())
 +            .iter()
 +            .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local()))
 +            .any(|imp| has_unsafe(cx, imp));
 +        then {
 +            span_lint_and_help(
 +                cx,
 +                UNSAFE_DERIVE_DESERIALIZE,
 +                item.span,
 +                "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
 +                None,
 +                "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html"
 +            );
 +        }
 +    }
 +}
 +
 +struct UnsafeVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    has_unsafe: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, span: Span, id: HirId) {
 +        if self.has_unsafe {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let Some(header) = kind.header();
 +            if header.unsafety == Unsafety::Unsafe;
 +            then {
 +                self.has_unsafe = true;
 +            }
 +        }
 +
 +        walk_fn(self, kind, decl, body_id, span, id);
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.has_unsafe {
 +            return;
 +        }
 +
 +        if let ExprKind::Block(block, _) = expr.kind {
 +            if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
 +                self.has_unsafe = true;
 +            }
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
 +fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
 +    if_chain! {
 +        if let ty::Adt(adt, substs) = ty.kind();
 +        if cx.tcx.visibility(adt.did()) == Visibility::Public;
 +        if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq);
 +        if let Some(def_id) = trait_ref.trait_def_id();
 +        if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id);
 +        let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id);
 +        if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]);
 +        // If all of our fields implement `Eq`, we can implement `Eq` too
 +        if adt
 +            .all_fields()
 +            .map(|f| f.ty(cx.tcx, substs))
 +            .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]));
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                DERIVE_PARTIAL_EQ_WITHOUT_EQ,
 +                span.ctxt().outer_expn_data().call_site,
 +                "you are deriving `PartialEq` and can implement `Eq`",
 +                "consider deriving `Eq` as well",
 +                "PartialEq, Eq".to_string(),
 +                Applicability::MachineApplicable,
 +            )
 +        }
 +    }
 +}
 +
 +/// Creates the `ParamEnv` used for the give type's derived `Eq` impl.
 +fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ParamEnv<'_> {
 +    // Initial map from generic index to param def.
 +    // Vec<(param_def, needs_eq)>
 +    let mut params = tcx
 +        .generics_of(did)
 +        .params
 +        .iter()
 +        .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. })))
 +        .collect::<Vec<_>>();
 +
 +    let ty_predicates = tcx.predicates_of(did).predicates;
 +    for (p, _) in ty_predicates {
 +        if let PredicateKind::Trait(p) = p.kind().skip_binder()
 +            && p.trait_ref.def_id == eq_trait_id
 +            && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
 +            && p.constness == BoundConstness::NotConst
 +        {
 +            // Flag types which already have an `Eq` bound.
 +            params[self_ty.index as usize].1 = false;
 +        }
 +    }
 +
 +    ParamEnv::new(
 +        tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain(
 +            params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
 +                tcx.mk_predicate(Binder::dummy(PredicateKind::Trait(TraitPredicate {
 +                    trait_ref: TraitRef::new(eq_trait_id, tcx.mk_substs([tcx.mk_param_from_def(param)].into_iter())),
 +                    constness: BoundConstness::NotConst,
 +                    polarity: ImplPolarity::Positive,
 +                })))
 +            }),
 +        )),
 +        Reveal::UserFacing,
 +        Constness::NotConst,
 +    )
 +}
index 25014bfa1a5b12595b41415a2010317ac7952143,0000000000000000000000000000000000000000..b35f0b8ca52dafc0fda6c5cde9ddaea8fc19a9f4
mode 100644,000000..100644
--- /dev/null
@@@ -1,243 -1,0 +1,243 @@@
-     #[clippy::version = "1.61.0"]
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
 +use clippy_utils::is_must_use_func_call;
 +use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item};
 +use rustc_hir::{Expr, ExprKind, LangItem};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `std::mem::drop` with a reference
 +    /// instead of an owned value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `drop` on a reference will only drop the
 +    /// reference itself, which is a no-op. It will not call the `drop` method (from
 +    /// the `Drop` trait implementation) on the underlying referenced value, which
 +    /// is likely what was intended.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// let mut lock_guard = mutex.lock();
 +    /// std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex
 +    /// // still locked
 +    /// operation_that_requires_mutex_to_be_unlocked();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DROP_REF,
 +    correctness,
 +    "calls to `std::mem::drop` with a reference instead of an owned value"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `std::mem::forget` with a reference
 +    /// instead of an owned value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `forget` on a reference will only forget the
 +    /// reference itself, which is a no-op. It will not forget the underlying
 +    /// referenced
 +    /// value, which is likely what was intended.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = Box::new(1);
 +    /// std::mem::forget(&x) // Should have been forget(x), x will still be dropped
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FORGET_REF,
 +    correctness,
 +    "calls to `std::mem::forget` with a reference instead of an owned value"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `std::mem::drop` with a value
 +    /// that derives the Copy trait
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `std::mem::drop` [does nothing for types that
 +    /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the
 +    /// value will be copied and moved into the function on invocation.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: i32 = 42; // i32 implements Copy
 +    /// std::mem::drop(x) // A copy of x is passed to the function, leaving the
 +    ///                   // original unaffected
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DROP_COPY,
 +    correctness,
 +    "calls to `std::mem::drop` with a value that implements Copy"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `std::mem::forget` with a value that
 +    /// derives the Copy trait
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `std::mem::forget` [does nothing for types that
 +    /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the
 +    /// value will be copied and moved into the function on invocation.
 +    ///
 +    /// An alternative, but also valid, explanation is that Copy types do not
 +    /// implement
 +    /// the Drop trait, which means they have no destructors. Without a destructor,
 +    /// there
 +    /// is nothing for `std::mem::forget` to ignore.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: i32 = 42; // i32 implements Copy
 +    /// std::mem::forget(x) // A copy of x is passed to the function, leaving the
 +    ///                     // original unaffected
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FORGET_COPY,
 +    correctness,
 +    "calls to `std::mem::forget` with a value that implements Copy"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `std::mem::drop` with a value that does not implement `Drop`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `std::mem::drop` is no different than dropping such a type. A different value may
 +    /// have been intended.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Foo;
 +    /// let x = Foo;
 +    /// std::mem::drop(x);
 +    /// ```
-     #[clippy::version = "1.61.0"]
++    #[clippy::version = "1.62.0"]
 +    pub DROP_NON_DROP,
 +    suspicious,
 +    "call to `std::mem::drop` with a value which does not implement `Drop`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `std::mem::forget` with a value that does not implement `Drop`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `std::mem::forget` is no different than dropping such a type. A different value may
 +    /// have been intended.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Foo;
 +    /// let x = Foo;
 +    /// std::mem::forget(x);
 +    /// ```
++    #[clippy::version = "1.62.0"]
 +    pub FORGET_NON_DROP,
 +    suspicious,
 +    "call to `std::mem::forget` with a value which does not implement `Drop`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The safe `drop` function does not drop the inner value of a `ManuallyDrop`.
 +    ///
 +    /// ### Known problems
 +    /// Does not catch cases if the user binds `std::mem::drop`
 +    /// to a different name and calls it that way.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct S;
 +    /// drop(std::mem::ManuallyDrop::new(S));
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// struct S;
 +    /// unsafe {
 +    ///     std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub UNDROPPED_MANUALLY_DROPS,
 +    correctness,
 +    "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value"
 +}
 +
 +const DROP_REF_SUMMARY: &str = "calls to `std::mem::drop` with a reference instead of an owned value. \
 +                                Dropping a reference does nothing";
 +const FORGET_REF_SUMMARY: &str = "calls to `std::mem::forget` with a reference instead of an owned value. \
 +                                  Forgetting a reference does nothing";
 +const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that implements `Copy`. \
 +                                 Dropping a copy leaves the original intact";
 +const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \
 +                                   Forgetting a copy leaves the original intact";
 +const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \
 +                                 Dropping such a type only extends its contained lifetimes";
 +const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \
 +                                   Forgetting such a type is the same as dropping it";
 +
 +declare_lint_pass!(DropForgetRef => [
 +    DROP_REF,
 +    FORGET_REF,
 +    DROP_COPY,
 +    FORGET_COPY,
 +    DROP_NON_DROP,
 +    FORGET_NON_DROP,
 +    UNDROPPED_MANUALLY_DROPS
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::Call(path, [arg]) = expr.kind
 +            && let ExprKind::Path(ref qpath) = path.kind
 +            && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
 +            && let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id)
 +        {
 +            let arg_ty = cx.typeck_results().expr_ty(arg);
 +            let (lint, msg) = match fn_name {
 +                sym::mem_drop if arg_ty.is_ref() => (DROP_REF, DROP_REF_SUMMARY),
 +                sym::mem_forget if arg_ty.is_ref() => (FORGET_REF, FORGET_REF_SUMMARY),
 +                sym::mem_drop if is_copy(cx, arg_ty) => (DROP_COPY, DROP_COPY_SUMMARY),
 +                sym::mem_forget if is_copy(cx, arg_ty) => (FORGET_COPY, FORGET_COPY_SUMMARY),
 +                sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => {
 +                    span_lint_and_help(
 +                        cx,
 +                        UNDROPPED_MANUALLY_DROPS,
 +                        expr.span,
 +                        "the inner value of this ManuallyDrop will not be dropped",
 +                        None,
 +                        "to drop a `ManuallyDrop<T>`, use std::mem::ManuallyDrop::drop",
 +                    );
 +                    return;
 +                }
 +                sym::mem_drop
 +                    if !(arg_ty.needs_drop(cx.tcx, cx.param_env)
 +                        || is_must_use_func_call(cx, arg)
 +                        || is_must_use_ty(cx, arg_ty)) =>
 +                {
 +                    (DROP_NON_DROP, DROP_NON_DROP_SUMMARY)
 +                },
 +                sym::mem_forget if !arg_ty.needs_drop(cx.tcx, cx.param_env) => {
 +                    (FORGET_NON_DROP, FORGET_NON_DROP_SUMMARY)
 +                },
 +                _ => return,
 +            };
 +            span_lint_and_note(
 +                cx,
 +                lint,
 +                expr.span,
 +                msg,
 +                Some(arg.span),
 +                &format!("argument has type `{}`", arg_ty),
 +            );
 +        }
 +    }
 +}
index 4f49bb879f5035c9b66e07a354089d681046e5e9,0000000000000000000000000000000000000000..e1eb3b6324c7820091e7b10dfa23f8c80d6e157a
mode 100644,000000..100644
--- /dev/null
@@@ -1,120 -1,0 +1,128 @@@
- use rustc_lint::{EarlyContext, EarlyLintPass, LintContext, Level};
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind};
 +use rustc_errors::MultiSpan;
-         for Modules { local_path, spans, lint_levels } in self.modules.values() {
++use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{FileName, Span};
 +use std::collections::BTreeMap;
 +use std::path::PathBuf;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for files that are included as modules multiple times.
 +    ///
 +    /// ### Why is this bad?
 +    /// Loading a file as a module more than once causes it to be compiled
 +    /// multiple times, taking longer and putting duplicate content into the
 +    /// module tree.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // lib.rs
 +    /// mod a;
 +    /// mod b;
 +    /// ```
 +    /// ```rust,ignore
 +    /// // a.rs
 +    /// #[path = "./b.rs"]
 +    /// mod b;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    ///
 +    /// ```rust,ignore
 +    /// // lib.rs
 +    /// mod a;
 +    /// mod b;
 +    /// ```
 +    /// ```rust,ignore
 +    /// // a.rs
 +    /// use crate::b;
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub DUPLICATE_MOD,
 +    suspicious,
 +    "file loaded as module multiple times"
 +}
 +
 +#[derive(PartialOrd, Ord, PartialEq, Eq)]
 +struct Modules {
 +    local_path: PathBuf,
 +    spans: Vec<Span>,
 +    lint_levels: Vec<Level>,
 +}
 +
 +#[derive(Default)]
 +pub struct DuplicateMod {
 +    /// map from the canonicalized path to `Modules`, `BTreeMap` to make the
 +    /// order deterministic for tests
 +    modules: BTreeMap<PathBuf, Modules>,
 +}
 +
 +impl_lint_pass!(DuplicateMod => [DUPLICATE_MOD]);
 +
 +impl EarlyLintPass for DuplicateMod {
 +    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
 +        if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, mod_spans)) = &item.kind
 +            && let FileName::Real(real) = cx.sess().source_map().span_to_filename(mod_spans.inner_span)
 +            && let Some(local_path) = real.into_local_path()
 +            && let Ok(absolute_path) = local_path.canonicalize()
 +        {
 +            let modules = self.modules.entry(absolute_path).or_insert(Modules {
 +                local_path,
 +                spans: Vec::new(),
 +                lint_levels: Vec::new(),
 +            });
 +            modules.spans.push(item.span_with_attributes());
 +            modules.lint_levels.push(cx.get_lint_level(DUPLICATE_MOD));
 +        }
 +    }
 +
 +    fn check_crate_post(&mut self, cx: &EarlyContext<'_>, _: &Crate) {
-             let spans: Vec<_> = spans.into_iter().zip(lint_levels).filter_map(|(span, lvl)|{
-                 if let Some(id) = lvl.get_expectation_id() {
-                     cx.fulfill_expectation(id);
-                 }
++        for Modules {
++            local_path,
++            spans,
++            lint_levels,
++        } in self.modules.values()
++        {
 +            if spans.len() < 2 {
 +                continue;
 +            }
 +
 +            // At this point the lint would be emitted
 +            assert_eq!(spans.len(), lint_levels.len());
-                 (!matches!(lvl, Level::Allow | Level::Expect(_))).then_some(*span)
-             })
-             .collect();
++            let spans: Vec<_> = spans
++                .iter()
++                .zip(lint_levels)
++                .filter_map(|(span, lvl)| {
++                    if let Some(id) = lvl.get_expectation_id() {
++                        cx.fulfill_expectation(id);
++                    }
 +
++                    (!matches!(lvl, Level::Allow | Level::Expect(_))).then_some(*span)
++                })
++                .collect();
 +
 +            if spans.len() < 2 {
 +                continue;
 +            }
 +
 +            let mut multi_span = MultiSpan::from_spans(spans.clone());
 +            let (&first, duplicates) = spans.split_first().unwrap();
 +
 +            multi_span.push_span_label(first, "first loaded here");
 +            for &duplicate in duplicates {
 +                multi_span.push_span_label(duplicate, "loaded again here");
 +            }
 +
 +            span_lint_and_help(
 +                cx,
 +                DUPLICATE_MOD,
 +                multi_span,
 +                &format!("file is loaded as a module multiple times: `{}`", local_path.display()),
 +                None,
 +                "replace all but one `mod` item with `use` items",
 +            );
 +        }
 +    }
 +}
index 325ae2356c14cb255531b3df3950d3208feaa39e,0000000000000000000000000000000000000000..ec063c0f777e3759313cbc8ae7faf745385d8784
mode 100644,000000..100644
--- /dev/null
@@@ -1,65 -1,0 +1,65 @@@
-     #[clippy::version = "1.61.0"]
 +use clippy_utils::{diagnostics::span_lint_and_sugg, peel_blocks};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for empty `Drop` implementations.
 +    ///
 +    /// ### Why is this bad?
 +    /// Empty `Drop` implementations have no effect when dropping an instance of the type. They are
 +    /// most likely useless. However, an empty `Drop` implementation prevents a type from being
 +    /// destructured, which might be the intention behind adding the implementation as a marker.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct S;
 +    ///
 +    /// impl Drop for S {
 +    ///     fn drop(&mut self) {}
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// struct S;
 +    /// ```
++    #[clippy::version = "1.62.0"]
 +    pub EMPTY_DROP,
 +    restriction,
 +    "empty `Drop` implementations"
 +}
 +declare_lint_pass!(EmptyDrop => [EMPTY_DROP]);
 +
 +impl LateLintPass<'_> for EmptyDrop {
 +    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
 +        if_chain! {
 +            if let ItemKind::Impl(Impl {
 +                of_trait: Some(ref trait_ref),
 +                items: [child],
 +                ..
 +            }) = item.kind;
 +            if trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait();
 +            if let impl_item_hir = child.id.hir_id();
 +            if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir);
 +            if let ImplItemKind::Fn(_, b) = &impl_item.kind;
 +            if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
 +            let func_expr = peel_blocks(func_expr);
 +            if let ExprKind::Block(block, _) = func_expr.kind;
 +            if block.stmts.is_empty() && block.expr.is_none();
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    EMPTY_DROP,
 +                    item.span,
 +                    "empty drop implementation",
 +                    "try removing this impl",
 +                    String::new(),
 +                    Applicability::MaybeIncorrect
 +                );
 +            }
 +        }
 +    }
 +}
index 27743a0ebec7e59dc688de5547043a4b2b60e95b,0000000000000000000000000000000000000000..4e3ae4c9614113f3a1132462d8b70600c8a7f8c9
mode 100644,000000..100644
--- /dev/null
@@@ -1,658 -1,0 +1,658 @@@
-     s.can_use_entry.then(|| InsertSearchResults {
 +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 (cond_expr, then_expr, else_expr) = match higher::If::hir(expr) {
 +            Some(higher::If { cond, then, r#else }) => (cond, then, r#else),
 +            _ => return,
 +        };
 +
 +        let (map_ty, contains_expr) = match try_parse_contains(cx, cond_expr) {
 +            Some(x) => x,
 +            None => return,
 +        };
 +
 +        let then_search = match find_insert_calls(cx, &contains_expr, then_expr) {
 +            Some(x) => x,
 +            None => 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 else_search = match find_insert_calls(cx, &contains_expr, else_expr) {
 +                Some(search) => search,
 +                None => 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({}) {} else {}",
 +                    map_ty.entry_path(),
 +                    entry_kind,
 +                    map_str,
 +                    key_str,
 +                    then_str,
 +                    else_str,
 +                )
 +            } 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 {}.entry({}) {{\n{indent}    {entry}::{} => {}\n\
 +                        {indent}    {entry}::{} => {}\n{indent}}}",
 +                    map_str,
 +                    key_str,
 +                    then_entry,
 +                    reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())),
 +                    else_entry,
 +                    reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())),
 +                    entry = map_ty.entry_path(),
 +                    indent = indent_str,
 +                )
 +            }
 +        } 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({}) {}",
 +                    map_ty.entry_path(),
 +                    entry_kind,
 +                    map_str,
 +                    key_str,
 +                    body_str,
 +                )
 +            } 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!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, value_str)
 +                    } else {
 +                        format!("{}.entry({}).or_insert({});", map_str, key_str, 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!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, 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 ef1216358dd97eb8eaa6dd00ddfbf3983c056e85,0000000000000000000000000000000000000000..fdfb821ac7895becb9fa97f56463dad42f0b7632
mode 100644,000000..100644
--- /dev/null
@@@ -1,101 -1,0 +1,103 @@@
- use rustc_lint::{LateContext, LateLintPass};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_context;
 +use clippy_utils::ty::implements_trait;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, Pat, PatKind};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::Ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for pattern matchings that can be expressed using equality.
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// * It reads better and has less cognitive load because equality won't cause binding.
 +    /// * It is a [Yoda condition](https://en.wikipedia.org/wiki/Yoda_conditions). Yoda conditions are widely
 +    /// criticized for increasing the cognitive load of reading the code.
 +    /// * Equality is a simple bool expression and can be merged with `&&` and `||` and
 +    /// reuse if blocks
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// if let Some(2) = x {
 +    ///     do_thing();
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// if x == Some(2) {
 +    ///     do_thing();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.57.0"]
 +    pub EQUATABLE_IF_LET,
 +    nursery,
 +    "using pattern matching instead of equality"
 +}
 +
 +declare_lint_pass!(PatternEquality => [EQUATABLE_IF_LET]);
 +
 +/// detects if pattern matches just one thing
 +fn unary_pattern(pat: &Pat<'_>) -> bool {
 +    fn array_rec(pats: &[Pat<'_>]) -> bool {
 +        pats.iter().all(unary_pattern)
 +    }
 +    match &pat.kind {
 +        PatKind::Slice(_, _, _) | PatKind::Range(_, _, _) | PatKind::Binding(..) | PatKind::Wild | PatKind::Or(_) => {
 +            false
 +        },
 +        PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)),
 +        PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => !etc.is_some() && array_rec(a),
 +        PatKind::Ref(x, _) | PatKind::Box(x) => unary_pattern(x),
 +        PatKind::Path(_) | PatKind::Lit(_) => true,
 +    }
 +}
 +
 +fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool {
 +    if let Some(def_id) = cx.tcx.lang_items().eq_trait() {
 +        implements_trait(cx, ty, def_id, &[other.into()])
 +    } else {
 +        false
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for PatternEquality {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
 +        if_chain! {
++            if !in_external_macro(cx.sess(), expr.span);
 +            if let ExprKind::Let(let_expr) = expr.kind;
 +            if unary_pattern(let_expr.pat);
 +            let exp_ty = cx.typeck_results().expr_ty(let_expr.init);
 +            let pat_ty = cx.typeck_results().pat_ty(let_expr.pat);
 +            if is_structural_partial_eq(cx, exp_ty, pat_ty);
 +            then {
 +
 +                let mut applicability = Applicability::MachineApplicable;
 +                let pat_str = match let_expr.pat.kind {
 +                    PatKind::Struct(..) => format!(
 +                        "({})",
 +                        snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0,
 +                    ),
 +                    _ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(),
 +                };
 +                span_lint_and_sugg(
 +                    cx,
 +                    EQUATABLE_IF_LET,
 +                    expr.span,
 +                    "this pattern matching can be expressed using equality",
 +                    "try",
 +                    format!(
 +                        "{} == {}",
 +                        snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0,
 +                        pat_str,
 +                    ),
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +}
index ee15ae9f59aca8eb96429d52464b3c53ae689a5b,0000000000000000000000000000000000000000..ebf5ab086dcea7ae6966984ad8810a47c39fcf3a
mode 100644,000000..100644
--- /dev/null
@@@ -1,77 -1,0 +1,83 @@@
-     #[clippy::version = "1.61.0"]
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{match_def_path, paths, peel_hir_expr_refs};
 +use rustc_hir::{BinOpKind, Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects cases where the result of a `format!` call is
 +    /// appended to an existing `String`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Introduces an extra, avoidable heap allocation.
 +    ///
++    /// ### Known problems
++    /// `format!` returns a `String` but `write!` returns a `Result`.
++    /// Thus you are forced to ignore the `Err` variant to achieve the same API.
++    ///
++    /// While using `write!` in the suggested way should never fail, this isn't necessarily clear to the programmer.
++    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut s = String::new();
 +    /// s += &format!("0x{:X}", 1024);
 +    /// s.push_str(&format!("0x{:X}", 1024));
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::fmt::Write as _; // import without risk of name clashing
 +    ///
 +    /// let mut s = String::new();
 +    /// let _ = write!(s, "0x{:X}", 1024);
 +    /// ```
-     perf,
++    #[clippy::version = "1.62.0"]
 +    pub FORMAT_PUSH_STRING,
++    restriction,
 +    "`format!(..)` appended to existing `String`"
 +}
 +declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]);
 +
 +fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String)
 +}
 +fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    if let Some(macro_def_id) = e.span.ctxt().outer_expn_data().macro_def_id {
 +        cx.tcx.get_diagnostic_name(macro_def_id) == Some(sym::format_macro)
 +    } else {
 +        false
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for FormatPushString {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        let arg = match expr.kind {
 +            ExprKind::MethodCall(_, [_, arg], _) => {
 +                if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) &&
 +                match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
 +                    arg
 +                } else {
 +                    return;
 +                }
 +            }
 +            ExprKind::AssignOp(op, left, arg)
 +            if op.node == BinOpKind::Add && is_string(cx, left) => {
 +                arg
 +            },
 +            _ => return,
 +        };
 +        let (arg, _) = peel_hir_expr_refs(arg);
 +        if is_format(cx, arg) {
 +            span_lint_and_help(
 +                cx,
 +                FORMAT_PUSH_STRING,
 +                expr.span,
 +                "`format!(..)` appended to existing `String`",
 +                None,
 +                "consider using `write!` to avoid the extra allocation",
 +            );
 +        }
 +    }
 +}
index 6a031a627df946669e206ae77fb2255d21bf71e4,0000000000000000000000000000000000000000..c5abcc462545c935fb8a6b37e678685ce7bf639b
mode 100644,000000..100644
--- /dev/null
@@@ -1,139 -1,0 +1,139 @@@
-         .then(|| span)
 +//! lint on inherent implementations
 +
 +use clippy_utils::diagnostics::span_lint_and_note;
 +use clippy_utils::is_lint_allowed;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::{def_id::LocalDefId, Item, ItemKind, Node};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::Span;
 +use std::collections::hash_map::Entry;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for multiple inherent implementations of a struct
 +    ///
 +    /// ### Why is this bad?
 +    /// Splitting the implementation of a type makes the code harder to navigate.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct X;
 +    /// impl X {
 +    ///     fn one() {}
 +    /// }
 +    /// impl X {
 +    ///     fn other() {}
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```rust
 +    /// struct X;
 +    /// impl X {
 +    ///     fn one() {}
 +    ///     fn other() {}
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MULTIPLE_INHERENT_IMPL,
 +    restriction,
 +    "Multiple inherent impl that could be grouped"
 +}
 +
 +declare_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl {
 +    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
 +        // Map from a type to it's first impl block. Needed to distinguish generic arguments.
 +        // e.g. `Foo<Bar>` and `Foo<Baz>`
 +        let mut type_map = FxHashMap::default();
 +        // List of spans to lint. (lint_span, first_span)
 +        let mut lint_spans = Vec::new();
 +
 +        for (_, impl_ids) in cx
 +            .tcx
 +            .crate_inherent_impls(())
 +            .inherent_impls
 +            .iter()
 +            .filter(|(&id, impls)| {
 +                impls.len() > 1
 +                    // Check for `#[allow]` on the type definition
 +                    && !is_lint_allowed(
 +                        cx,
 +                        MULTIPLE_INHERENT_IMPL,
 +                        cx.tcx.hir().local_def_id_to_hir_id(id),
 +                    )
 +            })
 +        {
 +            for impl_id in impl_ids.iter().map(|id| id.expect_local()) {
 +                match type_map.entry(cx.tcx.type_of(impl_id)) {
 +                    Entry::Vacant(e) => {
 +                        // Store the id for the first impl block of this type. The span is retrieved lazily.
 +                        e.insert(IdOrSpan::Id(impl_id));
 +                    },
 +                    Entry::Occupied(mut e) => {
 +                        if let Some(span) = get_impl_span(cx, impl_id) {
 +                            let first_span = match *e.get() {
 +                                IdOrSpan::Span(s) => s,
 +                                IdOrSpan::Id(id) => {
 +                                    if let Some(s) = get_impl_span(cx, id) {
 +                                        // Remember the span of the first block.
 +                                        *e.get_mut() = IdOrSpan::Span(s);
 +                                        s
 +                                    } else {
 +                                        // The first impl block isn't considered by the lint. Replace it with the
 +                                        // current one.
 +                                        *e.get_mut() = IdOrSpan::Span(span);
 +                                        continue;
 +                                    }
 +                                },
 +                            };
 +                            lint_spans.push((span, first_span));
 +                        }
 +                    },
 +                }
 +            }
 +
 +            // Switching to the next type definition, no need to keep the current entries around.
 +            type_map.clear();
 +        }
 +
 +        // `TyCtxt::crate_inherent_impls` doesn't have a defined order. Sort the lint output first.
 +        lint_spans.sort_by_key(|x| x.0.lo());
 +        for (span, first_span) in lint_spans {
 +            span_lint_and_note(
 +                cx,
 +                MULTIPLE_INHERENT_IMPL,
 +                span,
 +                "multiple implementations of this structure",
 +                Some(first_span),
 +                "first implementation here",
 +            );
 +        }
 +    }
 +}
 +
 +/// Gets the span for the given impl block unless it's not being considered by the lint.
 +fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option<Span> {
 +    let id = cx.tcx.hir().local_def_id_to_hir_id(id);
 +    if let Node::Item(&Item {
 +        kind: ItemKind::Impl(impl_item),
 +        span,
 +        ..
 +    }) = cx.tcx.hir().get(id)
 +    {
 +        (!span.from_expansion()
 +            && impl_item.generics.params.is_empty()
 +            && !is_lint_allowed(cx, MULTIPLE_INHERENT_IMPL, id))
++        .then_some(span)
 +    } else {
 +        None
 +    }
 +}
 +
 +enum IdOrSpan {
 +    Id(LocalDefId),
 +    Span(Span),
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e0a607f9a95b68391d69d69a76ea2418b5b681bf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,74 @@@
++use clippy_utils::diagnostics::span_lint;
++use clippy_utils::{match_function_call, paths};
++use rustc_ast::{BorrowKind, LitKind};
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Spanned;
++use rustc_span::Span;
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for `std::str::from_utf8_unchecked` with an invalid UTF-8 literal
++    ///
++    /// ### Why is this bad?
++    /// Creating such a `str` would result in undefined behavior
++    ///
++    /// ### Example
++    /// ```rust
++    /// # #[allow(unused)]
++    /// unsafe {
++    ///     std::str::from_utf8_unchecked(b"cl\x82ippy");
++    /// }
++    /// ```
++    #[clippy::version = "1.64.0"]
++    pub INVALID_UTF8_IN_UNCHECKED,
++    correctness,
++    "using a non UTF-8 literal in `std::std::from_utf8_unchecked`"
++}
++declare_lint_pass!(InvalidUtf8InUnchecked => [INVALID_UTF8_IN_UNCHECKED]);
++
++impl<'tcx> LateLintPass<'tcx> for InvalidUtf8InUnchecked {
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
++        if let Some([arg]) = match_function_call(cx, expr, &paths::STR_FROM_UTF8_UNCHECKED) {
++            match &arg.kind {
++                ExprKind::Lit(Spanned { node: lit, .. }) => {
++                    if let LitKind::ByteStr(bytes) = &lit
++                        && std::str::from_utf8(bytes).is_err()
++                    {
++                        lint(cx, expr.span);
++                    }
++                },
++                ExprKind::AddrOf(BorrowKind::Ref, _, Expr { kind: ExprKind::Array(args), .. }) => {
++                    let elements = args.iter().map(|e|{
++                        match &e.kind {
++                            ExprKind::Lit(Spanned { node: lit, .. }) => match lit {
++                                LitKind::Byte(b) => Some(*b),
++                                #[allow(clippy::cast_possible_truncation)]
++                                LitKind::Int(b, _) => Some(*b as u8),
++                                _ => None
++                            }
++                            _ => None
++                        }
++                    }).collect::<Option<Vec<_>>>();
++
++                    if let Some(elements) = elements
++                        && std::str::from_utf8(&elements).is_err()
++                    {
++                        lint(cx, expr.span);
++                    }
++                }
++                _ => {}
++            }
++        }
++    }
++}
++
++fn lint(cx: &LateContext<'_>, span: Span) {
++    span_lint(
++        cx,
++        INVALID_UTF8_IN_UNCHECKED,
++        span,
++        "non UTF-8 literal in `std::str::from_utf8_unchecked`",
++    );
++}
index 9be057bcf901f5255564173d5b8ea446b800cd61,0000000000000000000000000000000000000000..c58df126d62442a056d63437518699c3d189012c
mode 100644,000000..100644
--- /dev/null
@@@ -1,200 -1,0 +1,200 @@@
-     /// use case it may be possible to store the large data in an auxillary
 +//! 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::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::layout::LayoutOf;
 +use rustc_middle::ty::{Adt, 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 a
 +    /// large variant can penalize the memory layout of that enum.
 +    ///
 +    /// ### Known problems
 +    /// This lint obviously cannot take the distribution of
 +    /// variants in your running program into account. It is possible that the
 +    /// smaller variants make up less than 1% of all instances, in which case
 +    /// the overhead is negligible and the boxing is counter-productive. Always
 +    /// measure the change this lint suggests.
 +    ///
 +    /// 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 generic types if the layout depends on the
 +    /// generics, even if the size difference will be large anyway.
 +    ///
 +    /// ### 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>,
 +}
 +
 +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 = ty.ty_adt_def().expect("already checked whether this is an enum");
 +            if adt.variants().len() <= 1 {
 +                return;
 +            }
 +            let mut variants_size: Vec<VariantInfo> = Vec::new();
 +            for (i, variant) in adt.variants().iter().enumerate() {
 +                let mut fields_size = Vec::new();
 +                for (i, f) in variant.fields.iter().enumerate() {
 +                    let ty = cx.tcx.type_of(f.did);
 +                    // don't lint variants which have a field of generic type.
 +                    match cx.layout_of(ty) {
 +                        Ok(l) => {
 +                            let fsize = l.size.bytes();
 +                            fields_size.push(FieldInfo { ind: i, size: fsize });
 +                        },
 +                        Err(_) => {
 +                            return;
 +                        },
 +                    }
 +                }
 +                let size: u64 = fields_size.iter().map(|info| info.size).sum();
 +
 +                variants_size.push(VariantInfo {
 +                    ind: i,
 +                    size,
 +                    fields_size,
 +                });
 +            }
 +
 +            variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
 +
 +            let mut difference = variants_size[0].size - variants_size[1].size;
 +            if difference > self.maximum_size_difference_allowed {
 +                let help_text = "consider boxing the large fields to reduce the total size of the enum";
 +                span_lint_and_then(
 +                    cx,
 +                    LARGE_ENUM_VARIANT,
 +                    def.variants[variants_size[0].ind].span,
 +                    "large size difference between variants",
 +                    |diag| {
 +                        diag.span_label(
 +                            def.variants[variants_size[0].ind].span,
 +                            &format!("this variant is {} bytes", variants_size[0].size),
 +                        );
 +                        diag.span_note(
 +                            def.variants[variants_size[1].ind].span,
 +                            &format!("and the second-largest variant is {} bytes:", variants_size[1].size),
 +                        );
 +
 +                        let fields = def.variants[variants_size[0].ind].data.fields();
 +                        variants_size[0].fields_size.sort_by(|a, b| (a.size.cmp(&b.size)));
 +                        let mut applicability = Applicability::MaybeIncorrect;
 +                        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 563ad891603a7b74882191c203f72da3fedf2c34,0000000000000000000000000000000000000000..9afc714b11ca1613c2dea5936e022703707b1515
mode 100644,000000..100644
--- /dev/null
@@@ -1,353 -1,0 +1,353 @@@
-     LintId::of(format_push_string::FORMAT_PUSH_STRING),
 +// 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(blacklisted_name::BLACKLISTED_NAME),
 +    LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
 +    LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
 +    LintId::of(booleans::LOGIC_BUG),
 +    LintId::of(booleans::NONMINIMAL_BOOL),
 +    LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
 +    LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
 +    LintId::of(casts::CAST_ABS_TO_UNSIGNED),
 +    LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
 +    LintId::of(casts::CAST_ENUM_TRUNCATION),
 +    LintId::of(casts::CAST_REF_TO_MUT),
 +    LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
 +    LintId::of(casts::CHAR_LIT_AS_U8),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
 +    LintId::of(casts::UNNECESSARY_CAST),
 +    LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
 +    LintId::of(collapsible_if::COLLAPSIBLE_IF),
 +    LintId::of(comparison_chain::COMPARISON_CHAIN),
 +    LintId::of(copies::IFS_SAME_COND),
 +    LintId::of(copies::IF_SAME_THEN_ELSE),
 +    LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
 +    LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
 +    LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY),
 +    LintId::of(dereference::EXPLICIT_AUTO_DEREF),
 +    LintId::of(dereference::NEEDLESS_BORROW),
 +    LintId::of(derivable_impls::DERIVABLE_IMPLS),
 +    LintId::of(derive::DERIVE_HASH_XOR_EQ),
 +    LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
 +    LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),
 +    LintId::of(disallowed_methods::DISALLOWED_METHODS),
 +    LintId::of(disallowed_types::DISALLOWED_TYPES),
 +    LintId::of(doc::MISSING_SAFETY_DOC),
 +    LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
 +    LintId::of(double_parens::DOUBLE_PARENS),
 +    LintId::of(drop_forget_ref::DROP_COPY),
 +    LintId::of(drop_forget_ref::DROP_NON_DROP),
 +    LintId::of(drop_forget_ref::DROP_REF),
 +    LintId::of(drop_forget_ref::FORGET_COPY),
 +    LintId::of(drop_forget_ref::FORGET_NON_DROP),
 +    LintId::of(drop_forget_ref::FORGET_REF),
 +    LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
 +    LintId::of(duplicate_mod::DUPLICATE_MOD),
 +    LintId::of(entry::MAP_ENTRY),
 +    LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
 +    LintId::of(enum_variants::ENUM_VARIANT_NAMES),
 +    LintId::of(enum_variants::MODULE_INCEPTION),
 +    LintId::of(escape::BOXED_LOCAL),
 +    LintId::of(eta_reduction::REDUNDANT_CLOSURE),
 +    LintId::of(explicit_write::EXPLICIT_WRITE),
 +    LintId::of(float_literal::EXCESSIVE_PRECISION),
 +    LintId::of(format::USELESS_FORMAT),
 +    LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
 +    LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
 +    LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
 +    LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
 +    LintId::of(formatting::POSSIBLE_MISSING_COMMA),
 +    LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
 +    LintId::of(from_over_into::FROM_OVER_INTO),
 +    LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
 +    LintId::of(functions::DOUBLE_MUST_USE),
 +    LintId::of(functions::MUST_USE_UNIT),
 +    LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
 +    LintId::of(functions::RESULT_UNIT_ERR),
 +    LintId::of(functions::TOO_MANY_ARGUMENTS),
 +    LintId::of(get_first::GET_FIRST),
 +    LintId::of(if_let_mutex::IF_LET_MUTEX),
 +    LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
 +    LintId::of(infinite_iter::INFINITE_ITER),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
 +    LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
 +    LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
 +    LintId::of(int_plus_one::INT_PLUS_ONE),
++    LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED),
 +    LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
 +    LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
 +    LintId::of(len_zero::COMPARISON_TO_EMPTY),
 +    LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
 +    LintId::of(len_zero::LEN_ZERO),
 +    LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
 +    LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
 +    LintId::of(lifetimes::NEEDLESS_LIFETIMES),
 +    LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
 +    LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
 +    LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
 +    LintId::of(loops::EMPTY_LOOP),
 +    LintId::of(loops::EXPLICIT_COUNTER_LOOP),
 +    LintId::of(loops::FOR_KV_MAP),
 +    LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
 +    LintId::of(loops::ITER_NEXT_LOOP),
 +    LintId::of(loops::MANUAL_FIND),
 +    LintId::of(loops::MANUAL_FLATTEN),
 +    LintId::of(loops::MANUAL_MEMCPY),
 +    LintId::of(loops::MISSING_SPIN_LOOP),
 +    LintId::of(loops::MUT_RANGE_BOUND),
 +    LintId::of(loops::NEEDLESS_COLLECT),
 +    LintId::of(loops::NEEDLESS_RANGE_LOOP),
 +    LintId::of(loops::NEVER_LOOP),
 +    LintId::of(loops::SAME_ITEM_PUSH),
 +    LintId::of(loops::SINGLE_ELEMENT_LOOP),
 +    LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
 +    LintId::of(loops::WHILE_LET_LOOP),
 +    LintId::of(loops::WHILE_LET_ON_ITERATOR),
 +    LintId::of(main_recursion::MAIN_RECURSION),
 +    LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
 +    LintId::of(manual_bits::MANUAL_BITS),
 +    LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
 +    LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
 +    LintId::of(manual_retain::MANUAL_RETAIN),
 +    LintId::of(manual_strip::MANUAL_STRIP),
 +    LintId::of(map_clone::MAP_CLONE),
 +    LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
 +    LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
 +    LintId::of(match_result_ok::MATCH_RESULT_OK),
 +    LintId::of(matches::COLLAPSIBLE_MATCH),
 +    LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
 +    LintId::of(matches::MANUAL_MAP),
 +    LintId::of(matches::MANUAL_UNWRAP_OR),
 +    LintId::of(matches::MATCH_AS_REF),
 +    LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
 +    LintId::of(matches::MATCH_OVERLAPPING_ARM),
 +    LintId::of(matches::MATCH_REF_PATS),
 +    LintId::of(matches::MATCH_SINGLE_BINDING),
 +    LintId::of(matches::MATCH_STR_CASE_MISMATCH),
 +    LintId::of(matches::NEEDLESS_MATCH),
 +    LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
 +    LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
 +    LintId::of(matches::SINGLE_MATCH),
 +    LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
 +    LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
 +    LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
 +    LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
 +    LintId::of(methods::BIND_INSTEAD_OF_MAP),
 +    LintId::of(methods::BYTES_NTH),
 +    LintId::of(methods::CHARS_LAST_CMP),
 +    LintId::of(methods::CHARS_NEXT_CMP),
 +    LintId::of(methods::CLONE_DOUBLE_REF),
 +    LintId::of(methods::CLONE_ON_COPY),
 +    LintId::of(methods::ERR_EXPECT),
 +    LintId::of(methods::EXPECT_FUN_CALL),
 +    LintId::of(methods::EXTEND_WITH_DRAIN),
 +    LintId::of(methods::FILTER_MAP_IDENTITY),
 +    LintId::of(methods::FILTER_NEXT),
 +    LintId::of(methods::FLAT_MAP_IDENTITY),
 +    LintId::of(methods::GET_LAST_WITH_LEN),
 +    LintId::of(methods::INSPECT_FOR_EACH),
 +    LintId::of(methods::INTO_ITER_ON_REF),
 +    LintId::of(methods::IS_DIGIT_ASCII_RADIX),
 +    LintId::of(methods::ITERATOR_STEP_BY_ZERO),
 +    LintId::of(methods::ITER_CLONED_COLLECT),
 +    LintId::of(methods::ITER_COUNT),
 +    LintId::of(methods::ITER_NEXT_SLICE),
 +    LintId::of(methods::ITER_NTH),
 +    LintId::of(methods::ITER_NTH_ZERO),
 +    LintId::of(methods::ITER_OVEREAGER_CLONED),
 +    LintId::of(methods::ITER_SKIP_NEXT),
 +    LintId::of(methods::MANUAL_FILTER_MAP),
 +    LintId::of(methods::MANUAL_FIND_MAP),
 +    LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
 +    LintId::of(methods::MANUAL_SPLIT_ONCE),
 +    LintId::of(methods::MANUAL_STR_REPEAT),
 +    LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
 +    LintId::of(methods::MAP_FLATTEN),
 +    LintId::of(methods::MAP_IDENTITY),
 +    LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
 +    LintId::of(methods::NEEDLESS_OPTION_TAKE),
 +    LintId::of(methods::NEEDLESS_SPLITN),
 +    LintId::of(methods::NEW_RET_NO_SELF),
 +    LintId::of(methods::NO_EFFECT_REPLACE),
 +    LintId::of(methods::OK_EXPECT),
 +    LintId::of(methods::OPTION_AS_REF_DEREF),
 +    LintId::of(methods::OPTION_FILTER_MAP),
 +    LintId::of(methods::OPTION_MAP_OR_NONE),
 +    LintId::of(methods::OR_FUN_CALL),
 +    LintId::of(methods::OR_THEN_UNWRAP),
 +    LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
 +    LintId::of(methods::SEARCH_IS_SOME),
 +    LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
 +    LintId::of(methods::SINGLE_CHAR_ADD_STR),
 +    LintId::of(methods::SINGLE_CHAR_PATTERN),
 +    LintId::of(methods::SKIP_WHILE_NEXT),
 +    LintId::of(methods::STRING_EXTEND_CHARS),
 +    LintId::of(methods::SUSPICIOUS_MAP),
 +    LintId::of(methods::SUSPICIOUS_SPLITN),
 +    LintId::of(methods::UNINIT_ASSUMED_INIT),
 +    LintId::of(methods::UNNECESSARY_FILTER_MAP),
 +    LintId::of(methods::UNNECESSARY_FIND_MAP),
 +    LintId::of(methods::UNNECESSARY_FOLD),
 +    LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
 +    LintId::of(methods::UNNECESSARY_TO_OWNED),
 +    LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
 +    LintId::of(methods::USELESS_ASREF),
 +    LintId::of(methods::WRONG_SELF_CONVENTION),
 +    LintId::of(methods::ZST_OFFSET),
 +    LintId::of(minmax::MIN_MAX),
 +    LintId::of(misc::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(mut_key::MUTABLE_KEY_TYPE),
 +    LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
 +    LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
 +    LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
 +    LintId::of(needless_bool::BOOL_COMPARISON),
 +    LintId::of(needless_bool::NEEDLESS_BOOL),
 +    LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
 +    LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
 +    LintId::of(needless_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(open_options::NONSENSICAL_OPEN_OPTIONS),
 +    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(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::RANGE_ZIP_WITH_LEN),
 +    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(repeat_once::REPEAT_ONCE),
 +    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::UNSOUND_COLLECTION_TRANSMUTE),
 +    LintId::of(transmute::USELESS_TRANSMUTE),
 +    LintId::of(transmute::WRONG_TRANSMUTE),
 +    LintId::of(transmuting_null::TRANSMUTING_NULL),
 +    LintId::of(types::BORROWED_BOX),
 +    LintId::of(types::BOX_COLLECTION),
 +    LintId::of(types::REDUNDANT_ALLOCATION),
 +    LintId::of(types::TYPE_COMPLEXITY),
 +    LintId::of(types::VEC_BOX),
 +    LintId::of(unicode::INVISIBLE_CHARACTERS),
 +    LintId::of(uninit_vec::UNINIT_VEC),
 +    LintId::of(unit_hash::UNIT_HASH),
 +    LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
 +    LintId::of(unit_types::LET_UNIT_VALUE),
 +    LintId::of(unit_types::UNIT_ARG),
 +    LintId::of(unit_types::UNIT_CMP),
 +    LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
 +    LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
 +    LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
 +    LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
 +    LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
 +    LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
 +    LintId::of(unused_unit::UNUSED_UNIT),
 +    LintId::of(unwrap::PANICKING_UNWRAP),
 +    LintId::of(unwrap::UNNECESSARY_UNWRAP),
 +    LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
 +    LintId::of(useless_conversion::USELESS_CONVERSION),
 +    LintId::of(vec::USELESS_VEC),
 +    LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
 +    LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
 +    LintId::of(write::PRINTLN_EMPTY_STRING),
 +    LintId::of(write::PRINT_LITERAL),
 +    LintId::of(write::PRINT_WITH_NEWLINE),
 +    LintId::of(write::WRITELN_EMPTY_STRING),
 +    LintId::of(write::WRITE_LITERAL),
 +    LintId::of(write::WRITE_WITH_NEWLINE),
 +    LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
 +])
index 7d5e65cb27a1f709177d69e5b2105d16a69cb581,0000000000000000000000000000000000000000..9975859c54fea5a7bd676730d8b2d24cf58b5385
mode 100644,000000..100644
--- /dev/null
@@@ -1,77 -1,0 +1,78 @@@
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![
 +    LintId::of(approx_const::APPROX_CONSTANT),
 +    LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
 +    LintId::of(attrs::DEPRECATED_SEMVER),
 +    LintId::of(attrs::MISMATCHED_TARGET_OS),
 +    LintId::of(attrs::USELESS_ATTRIBUTE),
 +    LintId::of(booleans::LOGIC_BUG),
 +    LintId::of(casts::CAST_REF_TO_MUT),
 +    LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
 +    LintId::of(copies::IFS_SAME_COND),
 +    LintId::of(copies::IF_SAME_THEN_ELSE),
 +    LintId::of(derive::DERIVE_HASH_XOR_EQ),
 +    LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
 +    LintId::of(drop_forget_ref::DROP_COPY),
 +    LintId::of(drop_forget_ref::DROP_REF),
 +    LintId::of(drop_forget_ref::FORGET_COPY),
 +    LintId::of(drop_forget_ref::FORGET_REF),
 +    LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
 +    LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
 +    LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
 +    LintId::of(formatting::POSSIBLE_MISSING_COMMA),
 +    LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
 +    LintId::of(if_let_mutex::IF_LET_MUTEX),
 +    LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
 +    LintId::of(infinite_iter::INFINITE_ITER),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
 +    LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
++    LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED),
 +    LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
 +    LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
 +    LintId::of(loops::ITER_NEXT_LOOP),
 +    LintId::of(loops::NEVER_LOOP),
 +    LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
 +    LintId::of(matches::MATCH_STR_CASE_MISMATCH),
 +    LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
 +    LintId::of(methods::CLONE_DOUBLE_REF),
 +    LintId::of(methods::ITERATOR_STEP_BY_ZERO),
 +    LintId::of(methods::SUSPICIOUS_SPLITN),
 +    LintId::of(methods::UNINIT_ASSUMED_INIT),
 +    LintId::of(methods::ZST_OFFSET),
 +    LintId::of(minmax::MIN_MAX),
 +    LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
 +    LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
 +    LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
 +    LintId::of(operators::BAD_BIT_MASK),
 +    LintId::of(operators::CMP_NAN),
 +    LintId::of(operators::EQ_OP),
 +    LintId::of(operators::ERASING_OP),
 +    LintId::of(operators::INEFFECTIVE_BIT_MASK),
 +    LintId::of(operators::MODULO_ONE),
 +    LintId::of(operators::SELF_ASSIGNMENT),
 +    LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
 +    LintId::of(ptr::INVALID_NULL_PTR_USAGE),
 +    LintId::of(ptr::MUT_FROM_REF),
 +    LintId::of(ranges::REVERSED_EMPTY_RANGES),
 +    LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
 +    LintId::of(regex::INVALID_REGEX),
 +    LintId::of(serde_api::SERDE_API_MISUSE),
 +    LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
 +    LintId::of(swap::ALMOST_SWAPPED),
 +    LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
 +    LintId::of(transmute::WRONG_TRANSMUTE),
 +    LintId::of(transmuting_null::TRANSMUTING_NULL),
 +    LintId::of(unicode::INVISIBLE_CHARACTERS),
 +    LintId::of(uninit_vec::UNINIT_VEC),
 +    LintId::of(unit_hash::UNIT_HASH),
 +    LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
 +    LintId::of(unit_types::UNIT_CMP),
 +    LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
 +    LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
 +    LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
 +    LintId::of(unwrap::PANICKING_UNWRAP),
 +    LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
 +])
index d3c75f8b519107654df8d992bb677017deb594a1,0000000000000000000000000000000000000000..91d27bf526d096674e2dd1b6e8323c97725eb35e
mode 100644,000000..100644
--- /dev/null
@@@ -1,590 -1,0 +1,594 @@@
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_lints(&[
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::CLIPPY_LINTS_INTERNAL,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::COMPILER_LINT_FUNCTIONS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::DEFAULT_DEPRECATION_REASON,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::DEFAULT_LINT,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::IF_CHAIN_STYLE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::INTERNING_DEFINED_SYMBOL,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::INVALID_PATHS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::LINT_WITHOUT_LINT_PASS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::MISSING_MSRV_ATTR_IMPL,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::OUTER_EXPN_EXPN_DATA,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::PRODUCE_ICE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::UNNECESSARY_SYMBOL_STR,
 +    almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
 +    approx_const::APPROX_CONSTANT,
 +    as_conversions::AS_CONVERSIONS,
 +    as_underscore::AS_UNDERSCORE,
 +    asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
 +    asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
 +    assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
 +    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,
 +    blacklisted_name::BLACKLISTED_NAME,
 +    blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
 +    bool_assert_comparison::BOOL_ASSERT_COMPARISON,
 +    booleans::LOGIC_BUG,
 +    booleans::NONMINIMAL_BOOL,
 +    borrow_as_ptr::BORROW_AS_PTR,
 +    borrow_deref_ref::BORROW_DEREF_REF,
 +    bytecount::NAIVE_BYTECOUNT,
 +    bytes_count_to_len::BYTES_COUNT_TO_LEN,
 +    cargo::CARGO_COMMON_METADATA,
 +    cargo::MULTIPLE_CRATE_VERSIONS,
 +    cargo::NEGATIVE_FEATURE_NAMES,
 +    cargo::REDUNDANT_FEATURE_NAMES,
 +    cargo::WILDCARD_DEPENDENCIES,
 +    case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
 +    casts::CAST_ABS_TO_UNSIGNED,
 +    casts::CAST_ENUM_CONSTRUCTOR,
 +    casts::CAST_ENUM_TRUNCATION,
 +    casts::CAST_LOSSLESS,
 +    casts::CAST_POSSIBLE_TRUNCATION,
 +    casts::CAST_POSSIBLE_WRAP,
 +    casts::CAST_PRECISION_LOSS,
 +    casts::CAST_PTR_ALIGNMENT,
 +    casts::CAST_REF_TO_MUT,
 +    casts::CAST_SIGN_LOSS,
 +    casts::CAST_SLICE_DIFFERENT_SIZES,
 +    casts::CHAR_LIT_AS_U8,
 +    casts::FN_TO_NUMERIC_CAST,
 +    casts::FN_TO_NUMERIC_CAST_ANY,
 +    casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
 +    casts::PTR_AS_PTR,
 +    casts::UNNECESSARY_CAST,
 +    checked_conversions::CHECKED_CONVERSIONS,
 +    cognitive_complexity::COGNITIVE_COMPLEXITY,
 +    collapsible_if::COLLAPSIBLE_ELSE_IF,
 +    collapsible_if::COLLAPSIBLE_IF,
 +    comparison_chain::COMPARISON_CHAIN,
 +    copies::BRANCHES_SHARING_CODE,
 +    copies::IFS_SAME_COND,
 +    copies::IF_SAME_THEN_ELSE,
 +    copies::SAME_FUNCTIONS_IN_IF_CONDITION,
 +    copy_iterator::COPY_ITERATOR,
 +    crate_in_macro_def::CRATE_IN_MACRO_DEF,
 +    create_dir::CREATE_DIR,
 +    dbg_macro::DBG_MACRO,
 +    default::DEFAULT_TRAIT_ACCESS,
 +    default::FIELD_REASSIGN_WITH_DEFAULT,
 +    default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY,
 +    default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
 +    default_union_representation::DEFAULT_UNION_REPRESENTATION,
 +    dereference::EXPLICIT_AUTO_DEREF,
 +    dereference::EXPLICIT_DEREF_METHODS,
 +    dereference::NEEDLESS_BORROW,
 +    dereference::REF_BINDING_TO_REFERENCE,
 +    derivable_impls::DERIVABLE_IMPLS,
 +    derive::DERIVE_HASH_XOR_EQ,
 +    derive::DERIVE_ORD_XOR_PARTIAL_ORD,
 +    derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ,
 +    derive::EXPL_IMPL_CLONE_ON_COPY,
 +    derive::UNSAFE_DERIVE_DESERIALIZE,
 +    disallowed_methods::DISALLOWED_METHODS,
 +    disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
 +    disallowed_types::DISALLOWED_TYPES,
 +    doc::DOC_MARKDOWN,
 +    doc::MISSING_ERRORS_DOC,
 +    doc::MISSING_PANICS_DOC,
 +    doc::MISSING_SAFETY_DOC,
 +    doc::NEEDLESS_DOCTEST_MAIN,
 +    doc_link_with_quotes::DOC_LINK_WITH_QUOTES,
 +    double_parens::DOUBLE_PARENS,
 +    drop_forget_ref::DROP_COPY,
 +    drop_forget_ref::DROP_NON_DROP,
 +    drop_forget_ref::DROP_REF,
 +    drop_forget_ref::FORGET_COPY,
 +    drop_forget_ref::FORGET_NON_DROP,
 +    drop_forget_ref::FORGET_REF,
 +    drop_forget_ref::UNDROPPED_MANUALLY_DROPS,
 +    duplicate_mod::DUPLICATE_MOD,
 +    else_if_without_else::ELSE_IF_WITHOUT_ELSE,
 +    empty_drop::EMPTY_DROP,
 +    empty_enum::EMPTY_ENUM,
 +    empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS,
 +    entry::MAP_ENTRY,
 +    enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
 +    enum_variants::ENUM_VARIANT_NAMES,
 +    enum_variants::MODULE_INCEPTION,
 +    enum_variants::MODULE_NAME_REPETITIONS,
 +    equatable_if_let::EQUATABLE_IF_LET,
 +    escape::BOXED_LOCAL,
 +    eta_reduction::REDUNDANT_CLOSURE,
 +    eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
 +    excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
 +    excessive_bools::STRUCT_EXCESSIVE_BOOLS,
 +    exhaustive_items::EXHAUSTIVE_ENUMS,
 +    exhaustive_items::EXHAUSTIVE_STRUCTS,
 +    exit::EXIT,
 +    explicit_write::EXPLICIT_WRITE,
 +    fallible_impl_from::FALLIBLE_IMPL_FROM,
 +    float_literal::EXCESSIVE_PRECISION,
 +    float_literal::LOSSY_FLOAT_LITERAL,
 +    floating_point_arithmetic::IMPRECISE_FLOPS,
 +    floating_point_arithmetic::SUBOPTIMAL_FLOPS,
 +    format::USELESS_FORMAT,
 +    format_args::FORMAT_IN_FORMAT_ARGS,
 +    format_args::TO_STRING_IN_FORMAT_ARGS,
 +    format_impl::PRINT_IN_FORMAT_IMPL,
 +    format_impl::RECURSIVE_FORMAT_IMPL,
 +    format_push_string::FORMAT_PUSH_STRING,
 +    formatting::POSSIBLE_MISSING_COMMA,
 +    formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
 +    formatting::SUSPICIOUS_ELSE_FORMATTING,
 +    formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
 +    from_over_into::FROM_OVER_INTO,
 +    from_str_radix_10::FROM_STR_RADIX_10,
 +    functions::DOUBLE_MUST_USE,
 +    functions::MUST_USE_CANDIDATE,
 +    functions::MUST_USE_UNIT,
 +    functions::NOT_UNSAFE_PTR_ARG_DEREF,
 +    functions::RESULT_UNIT_ERR,
 +    functions::TOO_MANY_ARGUMENTS,
 +    functions::TOO_MANY_LINES,
 +    future_not_send::FUTURE_NOT_SEND,
 +    get_first::GET_FIRST,
 +    if_let_mutex::IF_LET_MUTEX,
 +    if_not_else::IF_NOT_ELSE,
 +    if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
 +    implicit_hasher::IMPLICIT_HASHER,
 +    implicit_return::IMPLICIT_RETURN,
 +    implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
 +    inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
 +    index_refutable_slice::INDEX_REFUTABLE_SLICE,
 +    indexing_slicing::INDEXING_SLICING,
 +    indexing_slicing::OUT_OF_BOUNDS_INDEXING,
 +    infinite_iter::INFINITE_ITER,
 +    infinite_iter::MAYBE_INFINITE_ITER,
 +    inherent_impl::MULTIPLE_INHERENT_IMPL,
 +    inherent_to_string::INHERENT_TO_STRING,
 +    inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY,
 +    init_numbered_fields::INIT_NUMBERED_FIELDS,
 +    inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
 +    int_plus_one::INT_PLUS_ONE,
 +    invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
++    invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED,
 +    items_after_statements::ITEMS_AFTER_STATEMENTS,
 +    iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
 +    large_const_arrays::LARGE_CONST_ARRAYS,
 +    large_enum_variant::LARGE_ENUM_VARIANT,
 +    large_include_file::LARGE_INCLUDE_FILE,
 +    large_stack_arrays::LARGE_STACK_ARRAYS,
 +    len_zero::COMPARISON_TO_EMPTY,
 +    len_zero::LEN_WITHOUT_IS_EMPTY,
 +    len_zero::LEN_ZERO,
 +    let_if_seq::USELESS_LET_IF_SEQ,
 +    let_underscore::LET_UNDERSCORE_DROP,
 +    let_underscore::LET_UNDERSCORE_LOCK,
 +    let_underscore::LET_UNDERSCORE_MUST_USE,
 +    lifetimes::EXTRA_UNUSED_LIFETIMES,
 +    lifetimes::NEEDLESS_LIFETIMES,
 +    literal_representation::DECIMAL_LITERAL_REPRESENTATION,
 +    literal_representation::INCONSISTENT_DIGIT_GROUPING,
 +    literal_representation::LARGE_DIGIT_GROUPS,
 +    literal_representation::MISTYPED_LITERAL_SUFFIXES,
 +    literal_representation::UNREADABLE_LITERAL,
 +    literal_representation::UNUSUAL_BYTE_GROUPINGS,
 +    loops::EMPTY_LOOP,
 +    loops::EXPLICIT_COUNTER_LOOP,
 +    loops::EXPLICIT_INTO_ITER_LOOP,
 +    loops::EXPLICIT_ITER_LOOP,
 +    loops::FOR_KV_MAP,
 +    loops::FOR_LOOPS_OVER_FALLIBLES,
 +    loops::ITER_NEXT_LOOP,
 +    loops::MANUAL_FIND,
 +    loops::MANUAL_FLATTEN,
 +    loops::MANUAL_MEMCPY,
 +    loops::MISSING_SPIN_LOOP,
 +    loops::MUT_RANGE_BOUND,
 +    loops::NEEDLESS_COLLECT,
 +    loops::NEEDLESS_RANGE_LOOP,
 +    loops::NEVER_LOOP,
 +    loops::SAME_ITEM_PUSH,
 +    loops::SINGLE_ELEMENT_LOOP,
 +    loops::WHILE_IMMUTABLE_CONDITION,
 +    loops::WHILE_LET_LOOP,
 +    loops::WHILE_LET_ON_ITERATOR,
 +    macro_use::MACRO_USE_IMPORTS,
 +    main_recursion::MAIN_RECURSION,
 +    manual_assert::MANUAL_ASSERT,
 +    manual_async_fn::MANUAL_ASYNC_FN,
 +    manual_bits::MANUAL_BITS,
 +    manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
 +    manual_ok_or::MANUAL_OK_OR,
 +    manual_rem_euclid::MANUAL_REM_EUCLID,
 +    manual_retain::MANUAL_RETAIN,
 +    manual_strip::MANUAL_STRIP,
 +    map_clone::MAP_CLONE,
 +    map_err_ignore::MAP_ERR_IGNORE,
 +    map_unit_fn::OPTION_MAP_UNIT_FN,
 +    map_unit_fn::RESULT_MAP_UNIT_FN,
 +    match_result_ok::MATCH_RESULT_OK,
 +    matches::COLLAPSIBLE_MATCH,
 +    matches::INFALLIBLE_DESTRUCTURING_MATCH,
 +    matches::MANUAL_MAP,
 +    matches::MANUAL_UNWRAP_OR,
 +    matches::MATCH_AS_REF,
 +    matches::MATCH_BOOL,
 +    matches::MATCH_LIKE_MATCHES_MACRO,
 +    matches::MATCH_ON_VEC_ITEMS,
 +    matches::MATCH_OVERLAPPING_ARM,
 +    matches::MATCH_REF_PATS,
 +    matches::MATCH_SAME_ARMS,
 +    matches::MATCH_SINGLE_BINDING,
 +    matches::MATCH_STR_CASE_MISMATCH,
 +    matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +    matches::MATCH_WILD_ERR_ARM,
 +    matches::NEEDLESS_MATCH,
 +    matches::REDUNDANT_PATTERN_MATCHING,
 +    matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +    matches::SIGNIFICANT_DROP_IN_SCRUTINEE,
 +    matches::SINGLE_MATCH,
 +    matches::SINGLE_MATCH_ELSE,
 +    matches::TRY_ERR,
 +    matches::WILDCARD_ENUM_MATCH_ARM,
 +    matches::WILDCARD_IN_OR_PATTERNS,
 +    mem_forget::MEM_FORGET,
 +    mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
 +    mem_replace::MEM_REPLACE_WITH_DEFAULT,
 +    mem_replace::MEM_REPLACE_WITH_UNINIT,
 +    methods::BIND_INSTEAD_OF_MAP,
 +    methods::BYTES_NTH,
 +    methods::CHARS_LAST_CMP,
 +    methods::CHARS_NEXT_CMP,
 +    methods::CLONED_INSTEAD_OF_COPIED,
 +    methods::CLONE_DOUBLE_REF,
 +    methods::CLONE_ON_COPY,
 +    methods::CLONE_ON_REF_PTR,
 +    methods::ERR_EXPECT,
 +    methods::EXPECT_FUN_CALL,
 +    methods::EXPECT_USED,
 +    methods::EXTEND_WITH_DRAIN,
 +    methods::FILETYPE_IS_FILE,
 +    methods::FILTER_MAP_IDENTITY,
 +    methods::FILTER_MAP_NEXT,
 +    methods::FILTER_NEXT,
 +    methods::FLAT_MAP_IDENTITY,
 +    methods::FLAT_MAP_OPTION,
 +    methods::FROM_ITER_INSTEAD_OF_COLLECT,
 +    methods::GET_LAST_WITH_LEN,
 +    methods::GET_UNWRAP,
 +    methods::IMPLICIT_CLONE,
 +    methods::INEFFICIENT_TO_STRING,
 +    methods::INSPECT_FOR_EACH,
 +    methods::INTO_ITER_ON_REF,
 +    methods::IS_DIGIT_ASCII_RADIX,
 +    methods::ITERATOR_STEP_BY_ZERO,
 +    methods::ITER_CLONED_COLLECT,
 +    methods::ITER_COUNT,
 +    methods::ITER_NEXT_SLICE,
 +    methods::ITER_NTH,
 +    methods::ITER_NTH_ZERO,
 +    methods::ITER_OVEREAGER_CLONED,
 +    methods::ITER_SKIP_NEXT,
 +    methods::ITER_WITH_DRAIN,
 +    methods::MANUAL_FILTER_MAP,
 +    methods::MANUAL_FIND_MAP,
 +    methods::MANUAL_SATURATING_ARITHMETIC,
 +    methods::MANUAL_SPLIT_ONCE,
 +    methods::MANUAL_STR_REPEAT,
 +    methods::MAP_COLLECT_RESULT_UNIT,
 +    methods::MAP_FLATTEN,
 +    methods::MAP_IDENTITY,
 +    methods::MAP_UNWRAP_OR,
 +    methods::NEEDLESS_OPTION_AS_DEREF,
 +    methods::NEEDLESS_OPTION_TAKE,
 +    methods::NEEDLESS_SPLITN,
 +    methods::NEW_RET_NO_SELF,
 +    methods::NO_EFFECT_REPLACE,
 +    methods::OK_EXPECT,
 +    methods::OPTION_AS_REF_DEREF,
 +    methods::OPTION_FILTER_MAP,
 +    methods::OPTION_MAP_OR_NONE,
 +    methods::OR_FUN_CALL,
 +    methods::OR_THEN_UNWRAP,
 +    methods::RESULT_MAP_OR_INTO_OPTION,
 +    methods::SEARCH_IS_SOME,
 +    methods::SHOULD_IMPLEMENT_TRAIT,
 +    methods::SINGLE_CHAR_ADD_STR,
 +    methods::SINGLE_CHAR_PATTERN,
 +    methods::SKIP_WHILE_NEXT,
 +    methods::STRING_EXTEND_CHARS,
 +    methods::SUSPICIOUS_MAP,
 +    methods::SUSPICIOUS_SPLITN,
 +    methods::UNINIT_ASSUMED_INIT,
 +    methods::UNNECESSARY_FILTER_MAP,
 +    methods::UNNECESSARY_FIND_MAP,
 +    methods::UNNECESSARY_FOLD,
 +    methods::UNNECESSARY_JOIN,
 +    methods::UNNECESSARY_LAZY_EVALUATIONS,
 +    methods::UNNECESSARY_TO_OWNED,
 +    methods::UNWRAP_OR_ELSE_DEFAULT,
 +    methods::UNWRAP_USED,
 +    methods::USELESS_ASREF,
 +    methods::WRONG_SELF_CONVENTION,
 +    methods::ZST_OFFSET,
 +    minmax::MIN_MAX,
 +    misc::SHORT_CIRCUIT_STATEMENT,
 +    misc::TOPLEVEL_REF_ARG,
 +    misc::USED_UNDERSCORE_BINDING,
 +    misc::ZERO_PTR,
 +    misc_early::BUILTIN_TYPE_SHADOW,
 +    misc_early::DOUBLE_NEG,
 +    misc_early::DUPLICATE_UNDERSCORE_ARGUMENT,
 +    misc_early::MIXED_CASE_HEX_LITERALS,
 +    misc_early::REDUNDANT_PATTERN,
 +    misc_early::SEPARATED_LITERAL_SUFFIX,
 +    misc_early::UNNEEDED_FIELD_PATTERN,
 +    misc_early::UNNEEDED_WILDCARD_PATTERN,
 +    misc_early::UNSEPARATED_LITERAL_SUFFIX,
 +    misc_early::ZERO_PREFIXED_LITERAL,
 +    mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER,
 +    missing_const_for_fn::MISSING_CONST_FOR_FN,
 +    missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
 +    missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
 +    missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
 +    mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION,
 +    mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION,
 +    module_style::MOD_MODULE_FILES,
 +    module_style::SELF_NAMED_MODULE_FILES,
 +    mut_key::MUTABLE_KEY_TYPE,
 +    mut_mut::MUT_MUT,
 +    mut_mutex_lock::MUT_MUTEX_LOCK,
 +    mut_reference::UNNECESSARY_MUT_PASSED,
 +    mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
 +    mutex_atomic::MUTEX_ATOMIC,
 +    mutex_atomic::MUTEX_INTEGER,
 +    needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
 +    needless_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,
 +    open_options::NONSENSICAL_OPEN_OPTIONS,
 +    operators::ABSURD_EXTREME_COMPARISONS,
 +    operators::ASSIGN_OP_PATTERN,
 +    operators::BAD_BIT_MASK,
 +    operators::CMP_NAN,
 +    operators::CMP_OWNED,
 +    operators::DOUBLE_COMPARISONS,
 +    operators::DURATION_SUBSEC,
 +    operators::EQ_OP,
 +    operators::ERASING_OP,
 +    operators::FLOAT_ARITHMETIC,
 +    operators::FLOAT_CMP,
 +    operators::FLOAT_CMP_CONST,
 +    operators::FLOAT_EQUALITY_WITHOUT_ABS,
 +    operators::IDENTITY_OP,
 +    operators::INEFFECTIVE_BIT_MASK,
 +    operators::INTEGER_ARITHMETIC,
 +    operators::INTEGER_DIVISION,
 +    operators::MISREFACTORED_ASSIGN_OP,
 +    operators::MODULO_ARITHMETIC,
 +    operators::MODULO_ONE,
 +    operators::NEEDLESS_BITWISE_BOOL,
 +    operators::OP_REF,
 +    operators::PTR_EQ,
 +    operators::SELF_ASSIGNMENT,
 +    operators::VERBOSE_BIT_MASK,
 +    option_env_unwrap::OPTION_ENV_UNWRAP,
 +    option_if_let_else::OPTION_IF_LET_ELSE,
 +    overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
 +    panic_in_result_fn::PANIC_IN_RESULT_FN,
 +    panic_unimplemented::PANIC,
 +    panic_unimplemented::TODO,
 +    panic_unimplemented::UNIMPLEMENTED,
 +    panic_unimplemented::UNREACHABLE,
 +    partialeq_ne_impl::PARTIALEQ_NE_IMPL,
 +    pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
 +    pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
 +    path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
 +    pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
 +    precedence::PRECEDENCE,
 +    ptr::CMP_NULL,
 +    ptr::INVALID_NULL_PTR_USAGE,
 +    ptr::MUT_FROM_REF,
 +    ptr::PTR_ARG,
 +    ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
 +    pub_use::PUB_USE,
 +    question_mark::QUESTION_MARK,
 +    ranges::MANUAL_RANGE_CONTAINS,
 +    ranges::RANGE_MINUS_ONE,
 +    ranges::RANGE_PLUS_ONE,
 +    ranges::RANGE_ZIP_WITH_LEN,
 +    ranges::REVERSED_EMPTY_RANGES,
 +    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,
 +    repeat_once::REPEAT_ONCE,
 +    return_self_not_must_use::RETURN_SELF_NOT_MUST_USE,
 +    returns::LET_AND_RETURN,
 +    returns::NEEDLESS_RETURN,
 +    same_name_method::SAME_NAME_METHOD,
 +    self_named_constructors::SELF_NAMED_CONSTRUCTORS,
 +    semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
 +    serde_api::SERDE_API_MISUSE,
 +    shadow::SHADOW_REUSE,
 +    shadow::SHADOW_SAME,
 +    shadow::SHADOW_UNRELATED,
 +    single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES,
 +    single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
 +    size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
 +    slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
 +    stable_sort_primitive::STABLE_SORT_PRIMITIVE,
++    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::UNSOUND_COLLECTION_TRANSMUTE,
 +    transmute::USELESS_TRANSMUTE,
 +    transmute::WRONG_TRANSMUTE,
 +    transmuting_null::TRANSMUTING_NULL,
 +    types::BORROWED_BOX,
 +    types::BOX_COLLECTION,
 +    types::LINKEDLIST,
 +    types::OPTION_OPTION,
 +    types::RC_BUFFER,
 +    types::RC_MUTEX,
 +    types::REDUNDANT_ALLOCATION,
 +    types::TYPE_COMPLEXITY,
 +    types::VEC_BOX,
 +    undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS,
 +    unicode::INVISIBLE_CHARACTERS,
 +    unicode::NON_ASCII_LITERAL,
 +    unicode::UNICODE_NOT_NFC,
 +    uninit_vec::UNINIT_VEC,
 +    unit_hash::UNIT_HASH,
 +    unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
 +    unit_types::LET_UNIT_VALUE,
 +    unit_types::UNIT_ARG,
 +    unit_types::UNIT_CMP,
 +    unnamed_address::FN_ADDRESS_COMPARISONS,
 +    unnamed_address::VTABLE_ADDRESS_COMPARISONS,
 +    unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
 +    unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
 +    unnecessary_sort_by::UNNECESSARY_SORT_BY,
 +    unnecessary_wraps::UNNECESSARY_WRAPS,
 +    unnested_or_patterns::UNNESTED_OR_PATTERNS,
 +    unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
 +    unused_async::UNUSED_ASYNC,
 +    unused_io_amount::UNUSED_IO_AMOUNT,
 +    unused_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,
 +    vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
 +    verbose_file_reads::VERBOSE_FILE_READS,
 +    wildcard_imports::ENUM_GLOB_USE,
 +    wildcard_imports::WILDCARD_IMPORTS,
 +    write::PRINTLN_EMPTY_STRING,
 +    write::PRINT_LITERAL,
 +    write::PRINT_STDERR,
 +    write::PRINT_STDOUT,
 +    write::PRINT_WITH_NEWLINE,
 +    write::USE_DEBUG,
 +    write::WRITELN_EMPTY_STRING,
 +    write::WRITE_LITERAL,
 +    write::WRITE_WITH_NEWLINE,
 +    zero_div_zero::ZERO_DIVIDED_BY_ZERO,
 +    zero_sized_map_values::ZERO_SIZED_MAP_VALUES,
 +])
index 6bf519c24e848ad2272d6f11e48b1e5166a32596,0000000000000000000000000000000000000000..e1b90acb93c2e9ae78baa4778790830996eb5065
mode 100644,000000..100644
--- /dev/null
@@@ -1,32 -1,0 +1,31 @@@
-     LintId::of(format_push_string::FORMAT_PUSH_STRING),
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
 +    LintId::of(entry::MAP_ENTRY),
 +    LintId::of(escape::BOXED_LOCAL),
 +    LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
 +    LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
 +    LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
 +    LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
 +    LintId::of(loops::MANUAL_MEMCPY),
 +    LintId::of(loops::MISSING_SPIN_LOOP),
 +    LintId::of(loops::NEEDLESS_COLLECT),
 +    LintId::of(manual_retain::MANUAL_RETAIN),
 +    LintId::of(methods::EXPECT_FUN_CALL),
 +    LintId::of(methods::EXTEND_WITH_DRAIN),
 +    LintId::of(methods::ITER_NTH),
 +    LintId::of(methods::ITER_OVEREAGER_CLONED),
 +    LintId::of(methods::MANUAL_STR_REPEAT),
 +    LintId::of(methods::OR_FUN_CALL),
 +    LintId::of(methods::SINGLE_CHAR_PATTERN),
 +    LintId::of(methods::UNNECESSARY_TO_OWNED),
 +    LintId::of(operators::CMP_OWNED),
 +    LintId::of(redundant_clone::REDUNDANT_CLONE),
 +    LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
 +    LintId::of(types::BOX_COLLECTION),
 +    LintId::of(types::REDUNDANT_ALLOCATION),
 +    LintId::of(vec::USELESS_VEC),
 +    LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
 +])
index 970e9db4772cb192eaf80ba336d213f92061f6e7,0000000000000000000000000000000000000000..43f1c892eb9b9c396db40f9deb83d481d380c7da
mode 100644,000000..100644
--- /dev/null
@@@ -1,82 -1,0 +1,86 @@@
 +// 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(as_underscore::AS_UNDERSCORE),
 +    LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
 +    LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
 +    LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
 +    LintId::of(create_dir::CREATE_DIR),
 +    LintId::of(dbg_macro::DBG_MACRO),
 +    LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),
 +    LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION),
 +    LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
 +    LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
 +    LintId::of(empty_drop::EMPTY_DROP),
 +    LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS),
 +    LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
 +    LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
 +    LintId::of(exit::EXIT),
 +    LintId::of(float_literal::LOSSY_FLOAT_LITERAL),
++    LintId::of(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(map_err_ignore::MAP_ERR_IGNORE),
 +    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::UNWRAP_USED),
 +    LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
 +    LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
 +    LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
 +    LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
 +    LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES),
 +    LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
 +    LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION),
 +    LintId::of(module_style::MOD_MODULE_FILES),
 +    LintId::of(module_style::SELF_NAMED_MODULE_FILES),
 +    LintId::of(operators::FLOAT_ARITHMETIC),
 +    LintId::of(operators::FLOAT_CMP_CONST),
 +    LintId::of(operators::INTEGER_ARITHMETIC),
 +    LintId::of(operators::INTEGER_DIVISION),
 +    LintId::of(operators::MODULO_ARITHMETIC),
 +    LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
 +    LintId::of(panic_unimplemented::PANIC),
 +    LintId::of(panic_unimplemented::TODO),
 +    LintId::of(panic_unimplemented::UNIMPLEMENTED),
 +    LintId::of(panic_unimplemented::UNREACHABLE),
 +    LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
 +    LintId::of(pub_use::PUB_USE),
 +    LintId::of(redundant_slicing::DEREF_BY_SLICING),
 +    LintId::of(same_name_method::SAME_NAME_METHOD),
 +    LintId::of(shadow::SHADOW_REUSE),
 +    LintId::of(shadow::SHADOW_SAME),
 +    LintId::of(shadow::SHADOW_UNRELATED),
 +    LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES),
++    LintId::of(std_instead_of_core::ALLOC_INSTEAD_OF_CORE),
++    LintId::of(std_instead_of_core::STD_INSTEAD_OF_ALLOC),
++    LintId::of(std_instead_of_core::STD_INSTEAD_OF_CORE),
 +    LintId::of(strings::STRING_ADD),
 +    LintId::of(strings::STRING_SLICE),
 +    LintId::of(strings::STRING_TO_STRING),
 +    LintId::of(strings::STR_TO_STRING),
 +    LintId::of(types::RC_BUFFER),
 +    LintId::of(types::RC_MUTEX),
 +    LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS),
 +    LintId::of(unicode::NON_ASCII_LITERAL),
 +    LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
 +    LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
 +    LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
 +    LintId::of(write::PRINT_STDERR),
 +    LintId::of(write::PRINT_STDOUT),
 +    LintId::of(write::USE_DEBUG),
 +])
index 884baaea0292693193dbaa12edcaada6968824c6,0000000000000000000000000000000000000000..1988c24578e0b781ea57caf881d987f4ef568d62
mode 100644,000000..100644
--- /dev/null
@@@ -1,977 -1,0 +1,981 @@@
 +#![feature(array_windows)]
 +#![feature(binary_heap_into_iter_sorted)]
 +#![feature(box_patterns)]
 +#![feature(control_flow_enum)]
 +#![feature(drain_filter)]
 +#![feature(iter_intersperse)]
 +#![cfg_attr(bootstrap, feature(let_chains))]
 +#![feature(let_else)]
 +#![feature(lint_reasons)]
 +#![feature(never_type)]
 +#![feature(once_cell)]
 +#![feature(rustc_private)]
 +#![feature(stmt_expr_attributes)]
 +#![recursion_limit = "512"]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
 +#![warn(trivial_casts, trivial_numeric_casts)]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +// Disable this rustc lint for now, as it was also done in rustc
 +#![allow(rustc::potential_query_instability)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_arena;
 +extern crate rustc_ast;
 +extern crate rustc_ast_pretty;
 +extern crate rustc_attr;
 +extern crate rustc_data_structures;
 +extern crate rustc_driver;
 +extern crate rustc_errors;
 +extern crate rustc_hir;
 +extern crate rustc_hir_pretty;
 +extern crate rustc_index;
 +extern crate rustc_infer;
 +extern crate rustc_lexer;
 +extern crate rustc_lint;
 +extern crate rustc_middle;
 +extern crate rustc_mir_dataflow;
 +extern crate rustc_parse;
 +extern crate rustc_parse_format;
 +extern crate rustc_session;
 +extern crate rustc_span;
 +extern crate rustc_target;
 +extern crate rustc_trait_selection;
 +extern crate rustc_typeck;
 +
 +#[macro_use]
 +extern crate clippy_utils;
 +
 +use clippy_utils::parse_msrv;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_lint::LintId;
 +use rustc_semver::RustcVersion;
 +use rustc_session::Session;
 +
 +/// Macro used to declare a Clippy lint.
 +///
 +/// Every lint declaration consists of 4 parts:
 +///
 +/// 1. The documentation, which is used for the website
 +/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions.
 +/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or
 +///    `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of.
 +/// 4. The `description` that contains a short explanation on what's wrong with code where the
 +///    lint is triggered.
 +///
 +/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are
 +/// enabled by default. As said in the README.md of this repository, if the lint level mapping
 +/// changes, please update README.md.
 +///
 +/// # Example
 +///
 +/// ```
 +/// #![feature(rustc_private)]
 +/// extern crate rustc_session;
 +/// use rustc_session::declare_tool_lint;
 +/// use clippy_lints::declare_clippy_lint;
 +///
 +/// declare_clippy_lint! {
 +///     /// ### What it does
 +///     /// Checks for ... (describe what the lint matches).
 +///     ///
 +///     /// ### Why is this bad?
 +///     /// Supply the reason for linting the code.
 +///     ///
 +///     /// ### Example
 +///     /// ```rust
 +///     /// Insert a short example of code that triggers the lint
 +///     /// ```
 +///     ///
 +///     /// Use instead:
 +///     /// ```rust
 +///     /// Insert a short example of improved code that doesn't trigger the lint
 +///     /// ```
 +///     pub LINT_NAME,
 +///     pedantic,
 +///     "description"
 +/// }
 +/// ```
 +/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
 +#[macro_export]
 +macro_rules! declare_clippy_lint {
 +    { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, suspicious, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +}
 +
 +#[cfg(feature = "internal")]
 +pub mod deprecated_lints;
 +#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
 +mod utils;
 +
 +mod renamed_lints;
 +
 +// begin lints modules, do not remove this comment, it’s used in `update_lints`
 +mod almost_complete_letter_range;
 +mod approx_const;
 +mod as_conversions;
 +mod as_underscore;
 +mod asm_syntax;
 +mod assertions_on_constants;
 +mod async_yields_async;
 +mod attrs;
 +mod await_holding_invalid;
 +mod blacklisted_name;
 +mod blocks_in_if_conditions;
 +mod bool_assert_comparison;
 +mod booleans;
 +mod borrow_as_ptr;
 +mod borrow_deref_ref;
 +mod bytecount;
 +mod bytes_count_to_len;
 +mod cargo;
 +mod case_sensitive_file_extension_comparisons;
 +mod casts;
 +mod checked_conversions;
 +mod cognitive_complexity;
 +mod collapsible_if;
 +mod comparison_chain;
 +mod copies;
 +mod copy_iterator;
 +mod crate_in_macro_def;
 +mod create_dir;
 +mod dbg_macro;
 +mod default;
 +mod default_instead_of_iter_empty;
 +mod default_numeric_fallback;
 +mod default_union_representation;
 +mod dereference;
 +mod derivable_impls;
 +mod derive;
 +mod disallowed_methods;
 +mod disallowed_script_idents;
 +mod disallowed_types;
 +mod doc;
 +mod doc_link_with_quotes;
 +mod double_parens;
 +mod drop_forget_ref;
 +mod duplicate_mod;
 +mod else_if_without_else;
 +mod empty_drop;
 +mod empty_enum;
 +mod empty_structs_with_brackets;
 +mod entry;
 +mod enum_clike;
 +mod enum_variants;
 +mod equatable_if_let;
 +mod escape;
 +mod eta_reduction;
 +mod excessive_bools;
 +mod exhaustive_items;
 +mod exit;
 +mod explicit_write;
 +mod fallible_impl_from;
 +mod float_literal;
 +mod floating_point_arithmetic;
 +mod format;
 +mod format_args;
 +mod format_impl;
 +mod format_push_string;
 +mod formatting;
 +mod from_over_into;
 +mod from_str_radix_10;
 +mod functions;
 +mod future_not_send;
 +mod get_first;
 +mod if_let_mutex;
 +mod if_not_else;
 +mod if_then_some_else_none;
 +mod implicit_hasher;
 +mod implicit_return;
 +mod implicit_saturating_sub;
 +mod inconsistent_struct_constructor;
 +mod index_refutable_slice;
 +mod indexing_slicing;
 +mod infinite_iter;
 +mod inherent_impl;
 +mod inherent_to_string;
 +mod init_numbered_fields;
 +mod inline_fn_without_body;
 +mod int_plus_one;
 +mod invalid_upcast_comparisons;
++mod invalid_utf8_in_unchecked;
 +mod items_after_statements;
 +mod iter_not_returning_iterator;
 +mod large_const_arrays;
 +mod large_enum_variant;
 +mod large_include_file;
 +mod large_stack_arrays;
 +mod len_zero;
 +mod let_if_seq;
 +mod let_underscore;
 +mod lifetimes;
 +mod literal_representation;
 +mod loops;
 +mod macro_use;
 +mod main_recursion;
 +mod manual_assert;
 +mod manual_async_fn;
 +mod manual_bits;
 +mod manual_non_exhaustive;
 +mod manual_ok_or;
 +mod manual_rem_euclid;
 +mod manual_retain;
 +mod manual_strip;
 +mod map_clone;
 +mod map_err_ignore;
 +mod map_unit_fn;
 +mod match_result_ok;
 +mod matches;
 +mod mem_forget;
 +mod mem_replace;
 +mod methods;
 +mod minmax;
 +mod misc;
 +mod misc_early;
 +mod mismatching_type_param_order;
 +mod missing_const_for_fn;
 +mod missing_doc;
 +mod missing_enforced_import_rename;
 +mod missing_inline;
 +mod mixed_read_write_in_expression;
 +mod module_style;
 +mod mut_key;
 +mod mut_mut;
 +mod mut_mutex_lock;
 +mod mut_reference;
 +mod mutable_debug_assertion;
 +mod mutex_atomic;
 +mod needless_arbitrary_self_type;
 +mod needless_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 open_options;
 +mod operators;
 +mod option_env_unwrap;
 +mod option_if_let_else;
 +mod overflow_check_conditional;
 +mod panic_in_result_fn;
 +mod panic_unimplemented;
 +mod partialeq_ne_impl;
 +mod pass_by_ref_or_value;
 +mod path_buf_push_overwrite;
 +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 repeat_once;
 +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 stable_sort_primitive;
++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 transmuting_null;
 +mod types;
 +mod undocumented_unsafe_blocks;
 +mod unicode;
 +mod uninit_vec;
 +mod unit_hash;
 +mod unit_return_expecting_ord;
 +mod unit_types;
 +mod unnamed_address;
 +mod unnecessary_owned_empty_strings;
 +mod unnecessary_self_imports;
 +mod unnecessary_sort_by;
 +mod unnecessary_wraps;
 +mod unnested_or_patterns;
 +mod unsafe_removed_from_name;
 +mod unused_async;
 +mod unused_io_amount;
 +mod unused_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 vec_resize_to_zero;
 +mod verbose_file_reads;
 +mod wildcard_imports;
 +mod write;
 +mod zero_div_zero;
 +mod zero_sized_map_values;
 +// end lints modules, do not remove this comment, it’s used in `update_lints`
 +
 +pub use crate::utils::conf::Conf;
 +use crate::utils::conf::{format_error, TryConf};
 +
 +/// Register all pre expansion lints
 +///
 +/// Pre-expansion lints run before any macro expansion has happened.
 +///
 +/// Note that due to the architecture of the compiler, currently `cfg_attr` attributes on crate
 +/// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
 +///
 +/// Used in `./src/driver.rs`.
 +pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
 +    // NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
 +
 +    let msrv = conf.msrv.as_ref().and_then(|s| {
 +        parse_msrv(s, None, None).or_else(|| {
 +            sess.err(&format!(
 +                "error reading Clippy's configuration file. `{}` is not a valid Rust version",
 +                s
 +            ));
 +            None
 +        })
 +    });
 +
 +    store.register_pre_expansion_pass(|| Box::new(write::Write::default()));
 +    store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv }));
 +}
 +
 +fn read_msrv(conf: &Conf, sess: &Session) -> Option<RustcVersion> {
 +    let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
 +        .ok()
 +        .and_then(|v| parse_msrv(&v, None, None));
 +    let clippy_msrv = conf.msrv.as_ref().and_then(|s| {
 +        parse_msrv(s, None, None).or_else(|| {
 +            sess.err(&format!(
 +                "error reading Clippy's configuration file. `{}` is not a valid Rust version",
 +                s
 +            ));
 +            None
 +        })
 +    });
 +
 +    if let Some(cargo_msrv) = cargo_msrv {
 +        if let Some(clippy_msrv) = clippy_msrv {
 +            // if both files have an msrv, let's compare them and emit a warning if they differ
 +            if clippy_msrv != cargo_msrv {
 +                sess.warn(&format!(
 +                    "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{}` from `clippy.toml`",
 +                    clippy_msrv
 +                ));
 +            }
 +
 +            Some(clippy_msrv)
 +        } else {
 +            Some(cargo_msrv)
 +        }
 +    } else {
 +        clippy_msrv
 +    }
 +}
 +
 +#[doc(hidden)]
 +pub fn read_conf(sess: &Session) -> Conf {
 +    let file_name = match utils::conf::lookup_conf_file() {
 +        Ok(Some(path)) => path,
 +        Ok(None) => return Conf::default(),
 +        Err(error) => {
 +            sess.struct_err(&format!("error finding Clippy's configuration file: {}", error))
 +                .emit();
 +            return Conf::default();
 +        },
 +    };
 +
 +    let TryConf { conf, errors } = utils::conf::read(&file_name);
 +    // all conf errors are non-fatal, we just use the default conf in case of error
 +    for error in errors {
 +        sess.err(&format!(
 +            "error reading Clippy's configuration file `{}`: {}",
 +            file_name.display(),
 +            format_error(error)
 +        ));
 +    }
 +
 +    conf
 +}
 +
 +/// Register all lints and lint groups with the rustc plugin registry
 +///
 +/// Used in `./src/driver.rs`.
 +#[expect(clippy::too_many_lines)]
 +pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
 +    register_removed_non_tool_lints(store);
 +
 +    include!("lib.deprecated.rs");
 +
 +    include!("lib.register_lints.rs");
 +    include!("lib.register_restriction.rs");
 +    include!("lib.register_pedantic.rs");
 +
 +    #[cfg(feature = "internal")]
 +    include!("lib.register_internal.rs");
 +
 +    include!("lib.register_all.rs");
 +    include!("lib.register_style.rs");
 +    include!("lib.register_complexity.rs");
 +    include!("lib.register_correctness.rs");
 +    include!("lib.register_suspicious.rs");
 +    include!("lib.register_perf.rs");
 +    include!("lib.register_cargo.rs");
 +    include!("lib.register_nursery.rs");
 +
 +    #[cfg(feature = "internal")]
 +    {
 +        if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
 +            store.register_late_pass(|| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
 +            return;
 +        }
 +    }
 +
 +    // all the internal lints
 +    #[cfg(feature = "internal")]
 +    {
 +        store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal));
 +        store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::CollapsibleCalls));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::CompilerLintFunctions::new()));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::IfChainStyle));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::InvalidPaths));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::InterningDefinedSymbol::default()));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::LintWithoutLintPass::default()));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::MatchTypeOnDiagItem));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::OuterExpnDataPass));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl));
 +    }
 +
 +    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_hash::UnitHash));
 +    store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
 +    store.register_late_pass(|| Box::new(strings::StringAdd));
 +    store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn));
 +    store.register_late_pass(|| Box::new(implicit_saturating_sub::ImplicitSaturatingSub));
 +    store.register_late_pass(|| Box::new(default_numeric_fallback::DefaultNumericFallback));
 +    store.register_late_pass(|| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor));
 +    store.register_late_pass(|| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
 +    store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
 +
 +    let msrv = 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(move || Box::new(map_clone::MapClone::new(msrv)));
 +
 +    store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
 +    store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
 +    let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
 +    store.register_late_pass(move || {
 +        Box::new(index_refutable_slice::IndexRefutableSlice::new(
 +            max_suggested_slice_pattern_length,
 +            msrv,
 +        ))
 +    });
 +    store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
 +    store.register_late_pass(|| Box::new(shadow::Shadow::default()));
 +    store.register_late_pass(|| Box::new(unit_types::UnitTypes));
 +    store.register_late_pass(|| Box::new(loops::Loops));
 +    store.register_late_pass(|| Box::new(main_recursion::MainRecursion::default()));
 +    store.register_late_pass(|| Box::new(lifetimes::Lifetimes));
 +    store.register_late_pass(|| Box::new(entry::HashMapPass));
 +    store.register_late_pass(|| Box::new(minmax::MinMaxPass));
 +    store.register_late_pass(|| Box::new(open_options::OpenOptions));
 +    store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv));
 +    store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
 +    store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate));
 +    store.register_late_pass(|| Box::new(needless_borrowed_ref::NeedlessBorrowedRef));
 +    store.register_late_pass(|| Box::new(borrow_deref_ref::BorrowDerefRef));
 +    store.register_late_pass(|| Box::new(no_effect::NoEffect));
 +    store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment));
 +    store.register_late_pass(move || Box::new(transmute::Transmute::new(msrv)));
 +    let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
 +    store.register_late_pass(move || {
 +        Box::new(cognitive_complexity::CognitiveComplexity::new(
 +            cognitive_complexity_threshold,
 +        ))
 +    });
 +    let too_large_for_stack = conf.too_large_for_stack;
 +    store.register_late_pass(move || Box::new(escape::BoxedLocal { too_large_for_stack }));
 +    store.register_late_pass(move || Box::new(vec::UselessVec { too_large_for_stack }));
 +    store.register_late_pass(|| Box::new(panic_unimplemented::PanicUnimplemented));
 +    store.register_late_pass(|| Box::new(strings::StringLitAsBytes));
 +    store.register_late_pass(|| Box::new(derive::Derive));
 +    store.register_late_pass(|| Box::new(derivable_impls::DerivableImpls));
 +    store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef));
 +    store.register_late_pass(|| Box::new(empty_enum::EmptyEnum));
 +    store.register_late_pass(|| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
 +    store.register_late_pass(|| Box::new(regex::Regex));
 +    store.register_late_pass(|| Box::new(copies::CopyAndPaste));
 +    store.register_late_pass(|| Box::new(copy_iterator::CopyIterator));
 +    store.register_late_pass(|| Box::new(format::UselessFormat));
 +    store.register_late_pass(|| Box::new(swap::Swap));
 +    store.register_late_pass(|| Box::new(overflow_check_conditional::OverflowCheckConditional));
 +    store.register_late_pass(|| Box::new(new_without_default::NewWithoutDefault::default()));
 +    let blacklisted_names = conf.blacklisted_names.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_late_pass(move || Box::new(blacklisted_name::BlacklistedName::new(blacklisted_names.clone())));
 +    let too_many_arguments_threshold = conf.too_many_arguments_threshold;
 +    let too_many_lines_threshold = conf.too_many_lines_threshold;
 +    store.register_late_pass(move || {
 +        Box::new(functions::Functions::new(
 +            too_many_arguments_threshold,
 +            too_many_lines_threshold,
 +        ))
 +    });
 +    let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_late_pass(move || Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
 +    store.register_late_pass(|| Box::new(neg_multiply::NegMultiply));
 +    store.register_late_pass(|| Box::new(mem_forget::MemForget));
 +    store.register_late_pass(|| Box::new(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(bytecount::ByteCount));
 +    store.register_late_pass(|| Box::new(infinite_iter::InfiniteIter));
 +    store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody));
 +    store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default()));
 +    store.register_late_pass(|| Box::new(implicit_hasher::ImplicitHasher));
 +    store.register_late_pass(|| Box::new(fallible_impl_from::FallibleImplFrom));
 +    store.register_late_pass(|| Box::new(question_mark::QuestionMark));
 +    store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
 +    store.register_late_pass(|| Box::new(suspicious_trait_impl::SuspiciousImpl));
 +    store.register_late_pass(|| Box::new(map_unit_fn::MapUnit));
 +    store.register_late_pass(|| Box::new(inherent_impl::MultipleInherentImpl));
 +    store.register_late_pass(|| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd));
 +    store.register_late_pass(|| Box::new(unwrap::Unwrap));
 +    store.register_late_pass(|| Box::new(indexing_slicing::IndexingSlicing));
 +    store.register_late_pass(|| Box::new(non_copy_const::NonCopyConst));
 +    store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
 +    store.register_late_pass(|| Box::new(redundant_clone::RedundantClone));
 +    store.register_late_pass(|| Box::new(slow_vector_initialization::SlowVectorInit));
 +    store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
 +    store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
 +    store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
 +    store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
 +    store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
 +    store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
 +    let max_trait_bounds = conf.max_trait_bounds;
 +    store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
 +    store.register_late_pass(|| Box::new(comparison_chain::ComparisonChain));
 +    store.register_late_pass(|| Box::new(mut_key::MutableKeyType));
 +    store.register_early_pass(|| Box::new(reference::DerefAddrOf));
 +    store.register_early_pass(|| Box::new(double_parens::DoubleParens));
 +    store.register_late_pass(|| Box::new(format_impl::FormatImpl::new()));
 +    store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
 +    store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse));
 +    store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne));
 +    store.register_early_pass(|| Box::new(formatting::Formatting));
 +    store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints));
 +    store.register_early_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
 +    store.register_late_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
 +    store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
 +    store.register_late_pass(|| Box::new(returns::Return));
 +    store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf));
 +    store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements));
 +    store.register_early_pass(|| Box::new(precedence::Precedence));
 +    store.register_late_pass(|| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals));
 +    store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue));
 +    store.register_early_pass(|| Box::new(redundant_else::RedundantElse));
 +    store.register_late_pass(|| Box::new(create_dir::CreateDir));
 +    store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType));
 +    let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions;
 +    store.register_early_pass(move || {
 +        Box::new(literal_representation::LiteralDigitGrouping::new(
 +            literal_representation_lint_fraction_readability,
 +        ))
 +    });
 +    let literal_representation_threshold = conf.literal_representation_threshold;
 +    store.register_early_pass(move || {
 +        Box::new(literal_representation::DecimalLiteralRepresentation::new(
 +            literal_representation_threshold,
 +        ))
 +    });
 +    let enum_variant_name_threshold = conf.enum_variant_name_threshold;
 +    store.register_late_pass(move || {
 +        Box::new(enum_variants::EnumVariantNames::new(
 +            enum_variant_name_threshold,
 +            avoid_breaking_exported_api,
 +        ))
 +    });
 +    store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
 +    let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive;
 +    store.register_late_pass(move || {
 +        Box::new(upper_case_acronyms::UpperCaseAcronyms::new(
 +            avoid_breaking_exported_api,
 +            upper_case_acronyms_aggressive,
 +        ))
 +    });
 +    store.register_late_pass(|| Box::new(default::Default::default()));
 +    store.register_late_pass(|| Box::new(unused_self::UnusedSelf));
 +    store.register_late_pass(|| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
 +    store.register_late_pass(|| Box::new(exit::Exit));
 +    store.register_late_pass(|| Box::new(to_digit_is_some::ToDigitIsSome));
 +    let array_size_threshold = conf.array_size_threshold;
 +    store.register_late_pass(move || Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold)));
 +    store.register_late_pass(move || Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold)));
 +    store.register_late_pass(|| Box::new(floating_point_arithmetic::FloatingPointArithmetic));
 +    store.register_early_pass(|| Box::new(as_conversions::AsConversions));
 +    store.register_late_pass(|| Box::new(let_underscore::LetUnderscore));
 +    store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports));
 +    let max_fn_params_bools = conf.max_fn_params_bools;
 +    let max_struct_bools = conf.max_struct_bools;
 +    store.register_early_pass(move || {
 +        Box::new(excessive_bools::ExcessiveBools::new(
 +            max_struct_bools,
 +            max_fn_params_bools,
 +        ))
 +    });
 +    store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
 +    let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
 +    store.register_late_pass(move || Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
 +    store.register_late_pass(|| Box::new(verbose_file_reads::VerboseFileReads));
 +    store.register_late_pass(|| Box::new(redundant_pub_crate::RedundantPubCrate::default()));
 +    store.register_late_pass(|| Box::new(unnamed_address::UnnamedAddress));
 +    store.register_late_pass(|| Box::new(dereference::Dereferencing::default()));
 +    store.register_late_pass(|| Box::new(option_if_let_else::OptionIfLetElse));
 +    store.register_late_pass(|| Box::new(future_not_send::FutureNotSend));
 +    store.register_late_pass(|| Box::new(if_let_mutex::IfLetMutex));
 +    store.register_late_pass(|| Box::new(if_not_else::IfNotElse));
 +    store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality));
 +    store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock));
 +    store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn));
 +    store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
 +    store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn));
 +    let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
 +    store.register_early_pass(move || {
 +        Box::new(non_expressive_names::NonExpressiveNames {
 +            single_char_binding_names_threshold,
 +        })
 +    });
 +    let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(&macro_matcher)));
 +    store.register_late_pass(|| Box::new(macro_use::MacroUseImports::default()));
 +    store.register_late_pass(|| Box::new(pattern_type_mismatch::PatternTypeMismatch));
 +    store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive));
 +    store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
 +    store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
 +    store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
 +    store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
 +    store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
 +    let disallowed_methods = conf.disallowed_methods.clone();
 +    store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone())));
 +    store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
 +    store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
 +    store.register_late_pass(|| Box::new(empty_drop::EmptyDrop));
 +    store.register_late_pass(|| Box::new(strings::StrToString));
 +    store.register_late_pass(|| Box::new(strings::StringToString));
 +    store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
 +    store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default()));
 +    store.register_late_pass(|| {
 +        Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons)
 +    });
 +    store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
 +    store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
 +    store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
 +    store.register_late_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison));
 +    store.register_early_pass(move || Box::new(module_style::ModStyle));
 +    store.register_late_pass(|| Box::new(unused_async::UnusedAsync));
 +    let disallowed_types = conf.disallowed_types.clone();
 +    store.register_late_pass(move || Box::new(disallowed_types::DisallowedTypes::new(disallowed_types.clone())));
 +    let import_renames = conf.enforced_import_renames.clone();
 +    store.register_late_pass(move || {
 +        Box::new(missing_enforced_import_rename::ImportRename::new(
 +            import_renames.clone(),
 +        ))
 +    });
 +    let scripts = conf.allowed_scripts.clone();
 +    store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(&scripts)));
 +    store.register_late_pass(|| Box::new(strlen_on_c_strings::StrlenOnCStrings));
 +    store.register_late_pass(move || Box::new(self_named_constructors::SelfNamedConstructors));
 +    store.register_late_pass(move || Box::new(iter_not_returning_iterator::IterNotReturningIterator));
 +    store.register_late_pass(move || Box::new(manual_assert::ManualAssert));
 +    let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send;
 +    store.register_late_pass(move || {
 +        Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(
 +            enable_raw_pointer_heuristic_for_send,
 +        ))
 +    });
 +    store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks));
 +    store.register_late_pass(move || Box::new(format_args::FormatArgs));
 +    store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray));
 +    store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
 +    store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit));
 +    store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
 +    store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields));
 +    store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
 +    store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv)));
 +    store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
 +    store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
 +    store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes));
 +    store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion));
 +    let allow_dbg_in_tests = conf.allow_dbg_in_tests;
 +    store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
 +    let cargo_ignore_publish = conf.cargo_ignore_publish;
 +    store.register_late_pass(move || {
 +        Box::new(cargo::Cargo {
 +            ignore_publish: cargo_ignore_publish,
 +        })
 +    });
 +    store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
 +    store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
 +    store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
 +    store.register_early_pass(|| Box::new(pub_use::PubUse));
 +    store.register_late_pass(|| Box::new(format_push_string::FormatPushString));
 +    store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen));
 +    let max_include_file_size = conf.max_include_file_size;
 +    store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
 +    store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace));
 +    store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
 +    store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default()));
 +    store.register_late_pass(|| Box::new(get_first::GetFirst));
 +    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(as_underscore::AsUnderscore));
 +    store.register_late_pass(|| Box::new(read_zero_byte_vec::ReadZeroByteVec));
 +    store.register_late_pass(|| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
 +    store.register_late_pass(move || Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
 +    store.register_late_pass(move || Box::new(manual_retain::ManualRetain::new(msrv)));
 +    let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
 +    store.register_late_pass(move || Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
++    store.register_late_pass(|| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
++    store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports));
 +    // 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 fc50e8addccec8f84e87c03fffedd10a92daa3dd,0000000000000000000000000000000000000000..8e3ab26a947f1c9413e1e06233696a2c075448c3
mode 100644,000000..100644
--- /dev/null
@@@ -1,92 -1,0 +1,93 @@@
-                     let mut applicability = Applicability::MachineApplicable;
 +use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP};
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{get_enclosing_block, is_integer_const};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_block, walk_expr};
 +use rustc_hir::{Expr, Pat};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, Ty, UintTy};
 +
 +// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
 +// incremented exactly once in the loop body, and initialized to zero
 +// at the start of the loop.
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    pat: &'tcx Pat<'_>,
 +    arg: &'tcx Expr<'_>,
 +    body: &'tcx Expr<'_>,
 +    expr: &'tcx Expr<'_>,
 +) {
 +    // Look for variables that are incremented once per loop iteration.
 +    let mut increment_visitor = IncrementVisitor::new(cx);
 +    walk_expr(&mut increment_visitor, body);
 +
 +    // For each candidate, check the parent block to see if
 +    // it's initialized to zero at the start of the loop.
 +    if let Some(block) = get_enclosing_block(cx, expr.hir_id) {
 +        for id in increment_visitor.into_results() {
 +            let mut initialize_visitor = InitializeVisitor::new(cx, expr, id);
 +            walk_block(&mut initialize_visitor, block);
 +
 +            if_chain! {
 +                if let Some((name, ty, initializer)) = initialize_visitor.get_result();
 +                if is_integer_const(cx, initializer, 0);
 +                then {
-                                 expr.span.with_hi(arg.span.hi()),
++                    let mut applicability = Applicability::MaybeIncorrect;
++                    let span = expr.span.with_hi(arg.span.hi());
 +
 +                    let int_name = match ty.map(Ty::kind) {
 +                        // usize or inferred
 +                        Some(ty::Uint(UintTy::Usize)) | None => {
 +                            span_lint_and_sugg(
 +                                cx,
 +                                EXPLICIT_COUNTER_LOOP,
-                         expr.span.with_hi(arg.span.hi()),
++                                span,
 +                                &format!("the variable `{}` is used as a loop counter", name),
 +                                "consider using",
 +                                format!(
 +                                    "for ({}, {}) in {}.enumerate()",
 +                                    name,
 +                                    snippet_with_applicability(cx, pat.span, "item", &mut applicability),
 +                                    make_iterator_snippet(cx, arg, &mut applicability),
 +                                ),
 +                                applicability,
 +                            );
 +                            return;
 +                        }
 +                        Some(ty::Int(int_ty)) => int_ty.name_str(),
 +                        Some(ty::Uint(uint_ty)) => uint_ty.name_str(),
 +                        _ => return,
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        EXPLICIT_COUNTER_LOOP,
-                                 expr.span.with_hi(arg.span.hi()),
++                        span,
 +                        &format!("the variable `{}` is used as a loop counter", name),
 +                        |diag| {
 +                            diag.span_suggestion(
++                                span,
 +                                "consider using",
 +                                format!(
 +                                    "for ({}, {}) in (0_{}..).zip({})",
 +                                    name,
 +                                    snippet_with_applicability(cx, pat.span, "item", &mut applicability),
 +                                    int_name,
 +                                    make_iterator_snippet(cx, arg, &mut applicability),
 +                                ),
 +                                applicability,
 +                            );
 +
 +                            diag.note(&format!(
 +                                "`{}` is of type `{}`, making it ineligible for `Iterator::enumerate`",
 +                                name, int_name
 +                            ));
 +                        },
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
index 33736d6d4e650691e6f9a394b2d2f7b8def212e0,0000000000000000000000000000000000000000..215c83a7edf6e9f91d86ecc5ce962894ad71ffa6
mode 100644,000000..100644
--- /dev/null
@@@ -1,158 -1,0 +1,158 @@@
-         // This should be the funciton body
 +use super::utils::make_iterator_snippet;
 +use super::MANUAL_FIND;
 +use clippy_utils::{
 +    diagnostics::span_lint_and_then, higher, is_lang_ctor, path_res, peel_blocks_with_stmt,
 +    source::snippet_with_applicability, ty::implements_trait,
 +};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    def::Res, lang_items::LangItem, BindingAnnotation, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind,
 +};
 +use rustc_lint::LateContext;
 +use rustc_span::source_map::Span;
 +
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    pat: &'tcx Pat<'_>,
 +    arg: &'tcx Expr<'_>,
 +    body: &'tcx Expr<'_>,
 +    span: Span,
 +    expr: &'tcx Expr<'_>,
 +) {
 +    let inner_expr = peel_blocks_with_stmt(body);
 +    // Check for the specific case that the result is returned and optimize suggestion for that (more
 +    // cases can be added later)
 +    if_chain! {
 +        if let Some(higher::If { cond, then, r#else: None, }) = higher::If::hir(inner_expr);
 +        if let Some(binding_id) = get_binding(pat);
 +        if let ExprKind::Block(block, _) = then.kind;
 +        if let [stmt] = block.stmts;
 +        if let StmtKind::Semi(semi) = stmt.kind;
 +        if let ExprKind::Ret(Some(ret_value)) = semi.kind;
 +        if let ExprKind::Call(Expr { kind: ExprKind::Path(ctor), .. }, [inner_ret]) = ret_value.kind;
 +        if is_lang_ctor(cx, ctor, LangItem::OptionSome);
 +        if path_res(cx, inner_ret) == Res::Local(binding_id);
 +        if let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr);
 +        then {
 +            let mut applicability = Applicability::MachineApplicable;
 +            let mut snippet = make_iterator_snippet(cx, arg, &mut applicability);
 +            // Checks if `pat` is a single reference to a binding (`&x`)
 +            let is_ref_to_binding =
 +                matches!(pat.kind, PatKind::Ref(inner, _) if matches!(inner.kind, PatKind::Binding(..)));
 +            // If `pat` is not a binding or a reference to a binding (`x` or `&x`)
 +            // we need to map it to the binding returned by the function (i.e. `.map(|(x, _)| x)`)
 +            if !(matches!(pat.kind, PatKind::Binding(..)) || is_ref_to_binding) {
 +                snippet.push_str(
 +                    &format!(
 +                        ".map(|{}| {})",
 +                        snippet_with_applicability(cx, pat.span, "..", &mut applicability),
 +                        snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability),
 +                    )[..],
 +                );
 +            }
 +            let ty = cx.typeck_results().expr_ty(inner_ret);
 +            if cx.tcx.lang_items().copy_trait().map_or(false, |id| implements_trait(cx, ty, id, &[])) {
 +                snippet.push_str(
 +                    &format!(
 +                        ".find(|{}{}| {})",
 +                        "&".repeat(1 + usize::from(is_ref_to_binding)),
 +                        snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability),
 +                        snippet_with_applicability(cx, cond.span, "..", &mut applicability),
 +                    )[..],
 +                );
 +                if is_ref_to_binding {
 +                    snippet.push_str(".copied()");
 +                }
 +            } else {
 +                applicability = Applicability::MaybeIncorrect;
 +                snippet.push_str(
 +                    &format!(
 +                        ".find(|{}| {})",
 +                        snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability),
 +                        snippet_with_applicability(cx, cond.span, "..", &mut applicability),
 +                    )[..],
 +                );
 +            }
 +            // Extends to `last_stmt` to include semicolon in case of `return None;`
 +            let lint_span = span.to(last_stmt.span).to(last_ret.span);
 +            span_lint_and_then(
 +                cx,
 +                MANUAL_FIND,
 +                lint_span,
 +                "manual implementation of `Iterator::find`",
 +                |diag| {
 +                    if applicability == Applicability::MaybeIncorrect {
 +                        diag.note("you may need to dereference some variables");
 +                    }
 +                    diag.span_suggestion(
 +                        lint_span,
 +                        "replace with an iterator",
 +                        snippet,
 +                        applicability,
 +                    );
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +fn get_binding(pat: &Pat<'_>) -> Option<HirId> {
 +    let mut hir_id = None;
 +    let mut count = 0;
 +    pat.each_binding(|annotation, id, _, _| {
 +        count += 1;
 +        if count > 1 {
 +            hir_id = None;
 +            return;
 +        }
 +        if let BindingAnnotation::Unannotated = annotation {
 +            hir_id = Some(id);
 +        }
 +    });
 +    hir_id
 +}
 +
 +// Returns the last statement and last return if function fits format for lint
 +fn last_stmt_and_ret<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +) -> Option<(&'tcx Stmt<'tcx>, &'tcx Expr<'tcx>)> {
 +    // Returns last non-return statement and the last return
 +    fn extract<'tcx>(block: &Block<'tcx>) -> Option<(&'tcx Stmt<'tcx>, &'tcx Expr<'tcx>)> {
 +        if let [.., last_stmt] = block.stmts {
 +            if let Some(ret) = block.expr {
 +                return Some((last_stmt, ret));
 +            }
 +            if_chain! {
 +                if let [.., snd_last, _] = block.stmts;
 +                if let StmtKind::Semi(last_expr) = last_stmt.kind;
 +                if let ExprKind::Ret(Some(ret)) = last_expr.kind;
 +                then {
 +                    return Some((snd_last, ret));
 +                }
 +            }
 +        }
 +        None
 +    }
 +    let mut parent_iter = cx.tcx.hir().parent_iter(expr.hir_id);
 +    if_chain! {
 +        // This should be the loop
 +        if let Some((node_hir, Node::Stmt(..))) = parent_iter.next();
++        // This should be the function body
 +        if let Some((_, Node::Block(block))) = parent_iter.next();
 +        if let Some((last_stmt, last_ret)) = extract(block);
 +        if last_stmt.hir_id == node_hir;
 +        if let ExprKind::Path(path) = &last_ret.kind;
 +        if is_lang_ctor(cx, path, LangItem::OptionNone);
 +        if let Some((_, Node::Expr(_block))) = parent_iter.next();
 +        // This includes the function header
 +        if let Some((_, func)) = parent_iter.next();
 +        if func.fn_kind().is_some();
 +        then {
 +            Some((block.stmts.last().unwrap(), last_ret))
 +        } else {
 +            None
 +        }
 +    }
 +}
index d276c901059974a54c38d51601f26c3a6d070638,0000000000000000000000000000000000000000..1d6ddf4b99f7b6835717672b238eb660547a4e45
mode 100644,000000..100644
--- /dev/null
@@@ -1,75 -1,0 +1,85 @@@
-                     let sugg = format!("{}{}.flatten()", arg_snippet, copied);
 +use super::utils::make_iterator_snippet;
 +use super::MANUAL_FLATTEN;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::higher;
 +use clippy_utils::visitors::is_local_used;
 +use clippy_utils::{is_lang_ctor, path_to_local_id, peel_blocks_with_stmt};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::LangItem::{OptionSome, ResultOk};
 +use rustc_hir::{Expr, Pat, PatKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +use rustc_span::source_map::Span;
 +
 +/// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the
 +/// iterator element is used.
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    pat: &'tcx Pat<'_>,
 +    arg: &'tcx Expr<'_>,
 +    body: &'tcx Expr<'_>,
 +    span: Span,
 +) {
 +    let inner_expr = peel_blocks_with_stmt(body);
 +    if_chain! {
 +        if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None })
 +            = higher::IfLet::hir(cx, inner_expr);
 +        // Ensure match_expr in `if let` statement is the same as the pat from the for-loop
 +        if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
 +        if path_to_local_id(let_expr, pat_hir_id);
 +        // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
 +        if let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind;
 +        let some_ctor = is_lang_ctor(cx, qpath, OptionSome);
 +        let ok_ctor = is_lang_ctor(cx, qpath, ResultOk);
 +        if some_ctor || ok_ctor;
 +        // Ensure expr in `if let` is not used afterwards
 +        if !is_local_used(cx, if_then, pat_hir_id);
 +        then {
 +            let if_let_type = if some_ctor { "Some" } else { "Ok" };
 +            // Prepare the error message
 +            let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type);
 +
 +            // Prepare the help message
 +            let mut applicability = Applicability::MaybeIncorrect;
 +            let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
 +            let copied = match cx.typeck_results().expr_ty(let_expr).kind() {
 +                ty::Ref(_, inner, _) => match inner.kind() {
 +                    ty::Ref(..) => ".copied()",
 +                    _ => ""
 +                }
 +                _ => ""
 +            };
 +
++            let sugg = format!("{arg_snippet}{copied}.flatten()");
++
++            // If suggestion is not a one-liner, it won't be shown inline within the error message. In that case,
++            // it will be shown in the extra `help` message at the end, which is why the first `help_msg` needs
++            // to refer to the correct relative position of the suggestion.
++            let help_msg = if sugg.contains('\n') {
++                "remove the `if let` statement in the for loop and then..."
++            } else {
++                "...and remove the `if let` statement in the for loop"
++            };
++
 +            span_lint_and_then(
 +                cx,
 +                MANUAL_FLATTEN,
 +                span,
 +                &msg,
 +                |diag| {
-                         Applicability::MaybeIncorrect,
 +                    diag.span_suggestion(
 +                        arg.span,
 +                        "try",
 +                        sugg,
-                         "...and remove the `if let` statement in the for loop",
++                        applicability,
 +                    );
 +                    diag.span_help(
 +                        inner_expr.span,
++                        help_msg,
 +                    );
 +                }
 +            );
 +        }
 +    }
 +}
index b94bbd2bd417de88fe6293b832cc79183cda7130,0000000000000000000000000000000000000000..e9e215e662f1939c6d799d5e0cdd84aee65fdea5
mode 100644,000000..100644
--- /dev/null
@@@ -1,353 -1,0 +1,362 @@@
-     get_enclosing_loop_or_closure, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used,
 +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::{
- use rustc_hir::{Closure, def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
++    get_enclosing_loop_or_multi_call_closure, is_refutable, is_trait_method, match_def_path, paths,
++    visitors::is_res_used,
 +};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_expr, Visitor};
-     if let Some(e) = get_enclosing_loop_or_closure(cx.tcx, loop_expr) {
-         // The iterator expression will be used on the next iteration (for loops), or on the next call (for
-         // closures) unless it is declared within the enclosing expression. TODO: Check for closures
-         // used where an `FnOnce` type is expected.
++use rustc_hir::{def::Res, Closure, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, 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(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind;
 +        if let Res::Def(_, pat_did) = pat_path.res;
 +        if match_def_path(cx, pat_did, &paths::OPTION_SOME);
 +        // 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 {} in {}{}", loop_var, 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 local_id = match iter_expr.path {
 +            Res::Local(id) => id,
 +            _ => 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 93a34f452f6d67d8ee79c25568ce62484e4f733c,0000000000000000000000000000000000000000..945880d21471b2a31af15ee8f53ae573174967d7
mode 100644,000000..100644
--- /dev/null
@@@ -1,202 -1,0 +1,202 @@@
-     AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, HirId,
-     IsAsync, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind,
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::match_function_call;
 +use clippy_utils::paths::FUTURE_FROM_GENERATOR;
 +use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::FnKind;
 +use rustc_hir::{
++    AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound,
++    HirId, IsAsync, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// It checks for manual implementations of `async` functions.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's more idiomatic to use the dedicated syntax.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::future::Future;
 +    ///
 +    /// fn foo() -> impl Future<Output = i32> { async { 42 } }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// async fn foo() -> i32 { 42 }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub MANUAL_ASYNC_FN,
 +    style,
 +    "manual implementations of `async` functions can be simplified using the dedicated syntax"
 +}
 +
 +declare_lint_pass!(ManualAsyncFn => [MANUAL_ASYNC_FN]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: FnKind<'tcx>,
 +        decl: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        span: Span,
 +        _: HirId,
 +    ) {
 +        if_chain! {
 +            if let Some(header) = kind.header();
 +            if header.asyncness == IsAsync::NotAsync;
 +            // Check that this function returns `impl Future`
 +            if let FnRetTy::Return(ret_ty) = decl.output;
 +            if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty);
 +            if let Some(output) = future_output_ty(trait_ref);
 +            if captures_all_lifetimes(decl.inputs, &output_lifetimes);
 +            // Check that the body of the function consists of one async block
 +            if let ExprKind::Block(block, _) = body.value.kind;
 +            if block.stmts.is_empty();
 +            if let Some(closure_body) = desugared_async_block(cx, block);
 +            then {
 +                let header_span = span.with_hi(ret_ty.span.hi());
 +
 +                span_lint_and_then(
 +                    cx,
 +                    MANUAL_ASYNC_FN,
 +                    header_span,
 +                    "this function can be simplified using the `async fn` syntax",
 +                    |diag| {
 +                        if_chain! {
 +                            if let Some(header_snip) = snippet_opt(cx, header_span);
 +                            if let Some(ret_pos) = position_before_rarrow(&header_snip);
 +                            if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output);
 +                            then {
 +                                let help = format!("make the function `async` and {}", ret_sugg);
 +                                diag.span_suggestion(
 +                                    header_span,
 +                                    &help,
 +                                    format!("async {}{}", &header_snip[..ret_pos], ret_snip),
 +                                    Applicability::MachineApplicable
 +                                );
 +
 +                                let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span));
 +                                diag.span_suggestion(
 +                                    block.span,
 +                                    "move the body of the async block to the enclosing function",
 +                                    body_snip,
 +                                    Applicability::MachineApplicable
 +                                );
 +                            }
 +                        }
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn future_trait_ref<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: &'tcx Ty<'tcx>,
 +) -> Option<(&'tcx TraitRef<'tcx>, Vec<LifetimeName>)> {
 +    if_chain! {
 +        if let TyKind::OpaqueDef(item_id, bounds) = ty.kind;
 +        let item = cx.tcx.hir().item(item_id);
 +        if let ItemKind::OpaqueTy(opaque) = &item.kind;
 +        if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| {
 +            if let GenericBound::Trait(poly, _) = bound {
 +                Some(&poly.trait_ref)
 +            } else {
 +                None
 +            }
 +        });
 +        if trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait();
 +        then {
 +            let output_lifetimes = bounds
 +                .iter()
 +                .filter_map(|bound| {
 +                    if let GenericArg::Lifetime(lt) = bound {
 +                        Some(lt.name)
 +                    } else {
 +                        None
 +                    }
 +                })
 +                .collect();
 +
 +            return Some((trait_ref, output_lifetimes));
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'tcx>> {
 +    if_chain! {
 +        if let Some(segment) = trait_ref.path.segments.last();
 +        if let Some(args) = segment.args;
 +        if args.bindings.len() == 1;
 +        let binding = &args.bindings[0];
 +        if binding.ident.name == sym::Output;
 +        if let TypeBindingKind::Equality{term: Term::Ty(output)} = binding.kind;
 +        then {
 +            return Some(output)
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) -> bool {
 +    let input_lifetimes: Vec<LifetimeName> = inputs
 +        .iter()
 +        .filter_map(|ty| {
 +            if let TyKind::Rptr(lt, _) = ty.kind {
 +                Some(lt.name)
 +            } else {
 +                None
 +            }
 +        })
 +        .collect();
 +
 +    // The lint should trigger in one of these cases:
 +    // - There are no input lifetimes
 +    // - There's only one output lifetime bound using `+ '_`
 +    // - All input lifetimes are explicitly bound to the output
 +    input_lifetimes.is_empty()
 +        || (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Underscore))
 +        || input_lifetimes
 +            .iter()
 +            .all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt))
 +}
 +
 +fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> {
 +    if_chain! {
 +        if let Some(block_expr) = block.expr;
 +        if let Some(args) = match_function_call(cx, block_expr, &FUTURE_FROM_GENERATOR);
 +        if args.len() == 1;
 +        if let Expr{kind: ExprKind::Closure(&Closure { body, .. }), ..} = args[0];
 +        let closure_body = cx.tcx.hir().body(body);
 +        if closure_body.generator_kind == Some(GeneratorKind::Async(AsyncGeneratorKind::Block));
 +        then {
 +            return Some(closure_body);
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str, String)> {
 +    match output.kind {
 +        TyKind::Tup(tys) if tys.is_empty() => {
 +            let sugg = "remove the return type";
 +            Some((sugg, "".into()))
 +        },
 +        _ => {
 +            let sugg = "return the output of the future directly";
 +            snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {}", snip)))
 +        },
 +    }
 +}
index 4278e98dc91facd694f9ad7d64d01d12a639ad4a,0000000000000000000000000000000000000000..2b04475c7a9debd962534a28343034ad1a5af815
mode 100644,000000..100644
--- /dev/null
@@@ -1,221 -1,0 +1,221 @@@
-                 .then(|| (id, v.span))
 +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::{is_doc_hidden, meets_msrv, msrvs};
 +use rustc_ast::ast::{self, VisibilityKind};
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 +use rustc_hir::{self as hir, Expr, ExprKind, QPath};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 +use rustc_middle::ty::DefIdTree;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::def_id::{DefId, LocalDefId};
 +use rustc_span::{sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for manual implementations of the non-exhaustive pattern.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the #[non_exhaustive] attribute expresses better the intent
 +    /// and allows possible optimizations when applied to enums.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct S {
 +    ///     pub a: i32,
 +    ///     pub b: i32,
 +    ///     _c: (),
 +    /// }
 +    ///
 +    /// enum E {
 +    ///     A,
 +    ///     B,
 +    ///     #[doc(hidden)]
 +    ///     _C,
 +    /// }
 +    ///
 +    /// struct T(pub i32, pub i32, ());
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// #[non_exhaustive]
 +    /// struct S {
 +    ///     pub a: i32,
 +    ///     pub b: i32,
 +    /// }
 +    ///
 +    /// #[non_exhaustive]
 +    /// enum E {
 +    ///     A,
 +    ///     B,
 +    /// }
 +    ///
 +    /// #[non_exhaustive]
 +    /// struct T(pub i32, pub i32);
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub MANUAL_NON_EXHAUSTIVE,
 +    style,
 +    "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
 +}
 +
 +#[expect(clippy::module_name_repetitions)]
 +pub struct ManualNonExhaustiveStruct {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl ManualNonExhaustiveStruct {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]);
 +
 +#[expect(clippy::module_name_repetitions)]
 +pub struct ManualNonExhaustiveEnum {
 +    msrv: Option<RustcVersion>,
 +    constructed_enum_variants: FxHashSet<(DefId, DefId)>,
 +    potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>,
 +}
 +
 +impl ManualNonExhaustiveEnum {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self {
 +            msrv,
 +            constructed_enum_variants: FxHashSet::default(),
 +            potential_enums: Vec::new(),
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]);
 +
 +impl EarlyLintPass for ManualNonExhaustiveStruct {
 +    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
 +        if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) {
 +            return;
 +        }
 +
 +        if let ast::ItemKind::Struct(variant_data, _) = &item.kind {
 +            let (fields, delimiter) = match variant_data {
 +                ast::VariantData::Struct(fields, _) => (&**fields, '{'),
 +                ast::VariantData::Tuple(fields, _) => (&**fields, '('),
 +                ast::VariantData::Unit(_) => return,
 +            };
 +            if fields.len() <= 1 {
 +                return;
 +            }
 +            let mut iter = fields.iter().filter_map(|f| match f.vis.kind {
 +                VisibilityKind::Public => None,
 +                VisibilityKind::Inherited => Some(Ok(f)),
 +                VisibilityKind::Restricted { .. } => Some(Err(())),
 +            });
 +            if let Some(Ok(field)) = iter.next()
 +                && iter.next().is_none()
 +                && field.ty.kind.is_unit()
 +                && field.ident.map_or(true, |name| name.as_str().starts_with('_'))
 +            {
 +                span_lint_and_then(
 +                    cx,
 +                    MANUAL_NON_EXHAUSTIVE,
 +                    item.span,
 +                    "this seems like a manual implementation of the non-exhaustive pattern",
 +                    |diag| {
 +                        if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive))
 +                            && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter)
 +                            && let Some(snippet) = snippet_opt(cx, header_span)
 +                        {
 +                            diag.span_suggestion(
 +                                header_span,
 +                                "add the attribute",
 +                                format!("#[non_exhaustive] {}", snippet),
 +                                Applicability::Unspecified,
 +                            );
 +                        }
 +                        diag.span_help(field.span, "remove this field");
 +                    }
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(EarlyContext);
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) {
 +            return;
 +        }
 +
 +        if let hir::ItemKind::Enum(def, _) = &item.kind
 +            && def.variants.len() > 1
 +        {
 +            let mut iter = def.variants.iter().filter_map(|v| {
 +                let id = cx.tcx.hir().local_def_id(v.id);
 +                (matches!(v.data, hir::VariantData::Unit(_))
 +                    && v.ident.as_str().starts_with('_')
 +                    && is_doc_hidden(cx.tcx.hir().attrs(v.id)))
++                .then_some((id, v.span))
 +            });
 +            if let Some((id, span)) = iter.next()
 +                && iter.next().is_none()
 +            {
 +                self.potential_enums.push((item.def_id, id, item.span, span));
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if let ExprKind::Path(QPath::Resolved(None, p)) = &e.kind
 +            && let [.., name] = p.segments
 +            && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res
 +            && name.ident.as_str().starts_with('_')
 +        {
 +            let variant_id = cx.tcx.parent(id);
 +            let enum_id = cx.tcx.parent(variant_id);
 +
 +            self.constructed_enum_variants.insert((enum_id, variant_id));
 +        }
 +    }
 +
 +    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
 +        for &(enum_id, _, enum_span, variant_span) in
 +            self.potential_enums.iter().filter(|&&(enum_id, variant_id, _, _)| {
 +                !self
 +                    .constructed_enum_variants
 +                    .contains(&(enum_id.to_def_id(), variant_id.to_def_id()))
 +            })
 +        {
 +            let hir_id = cx.tcx.hir().local_def_id_to_hir_id(enum_id);
 +            span_lint_hir_and_then(
 +                cx,
 +                MANUAL_NON_EXHAUSTIVE,
 +                hir_id,
 +                enum_span,
 +                "this seems like a manual implementation of the non-exhaustive pattern",
 +                |diag| {
 +                    if !cx.tcx.adt_def(enum_id).is_variant_list_non_exhaustive()
 +                        && let header_span = cx.sess().source_map().span_until_char(enum_span, '{')
 +                        && let Some(snippet) = snippet_opt(cx, header_span)
 +                    {
 +                            diag.span_suggestion(
 +                                header_span,
 +                                "add the attribute",
 +                                format!("#[non_exhaustive] {}", snippet),
 +                                Applicability::Unspecified,
 +                            );
 +                    }
 +                    diag.span_help(variant_span, "remove this variant");
 +                },
 +            );
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
index 2ce9d0e77c1fd167f093fd0cb2613bfaf9bab27b,0000000000000000000000000000000000000000..95cc6bdbd8ba7bf0b748b7d9559bb9dc7e8af8f2
mode 100644,000000..100644
--- /dev/null
@@@ -1,122 -1,0 +1,123 @@@
-             && let Some(hir_id) = path_to_local(expr3) {
 +use clippy_utils::consts::{constant_full_int, FullInt};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{in_constant, meets_msrv, msrvs, path_to_local};
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
 +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 an expression like `((x % 4) + 4) % 4` which is a common manual reimplementation
 +    /// of `x.rem_euclid(4)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's simpler and more readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: i32 = 24;
 +    /// let rem = ((x % 4) + 4) % 4;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x: i32 = 24;
 +    /// let rem = x.rem_euclid(4);
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub MANUAL_REM_EUCLID,
 +    complexity,
 +    "manually reimplementing `rem_euclid`"
 +}
 +
 +pub struct ManualRemEuclid {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl ManualRemEuclid {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(ManualRemEuclid => [MANUAL_REM_EUCLID]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if !meets_msrv(self.msrv, msrvs::REM_EUCLID) {
 +            return;
 +        }
 +
 +        if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::REM_EUCLID_CONST) {
 +            return;
 +        }
 +
 +        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
 +
 +        if let ExprKind::Binary(op1, expr1, right) = expr.kind
 +            && op1.node == BinOpKind::Rem
 +            && let Some(const1) = check_for_unsigned_int_constant(cx, right)
 +            && let ExprKind::Binary(op2, left, right) = expr1.kind
 +            && op2.node == BinOpKind::Add
 +            && let Some((const2, expr2)) = check_for_either_unsigned_int_constant(cx, left, right)
 +            && let ExprKind::Binary(op3, expr3, right) = expr2.kind
 +            && op3.node == BinOpKind::Rem
 +            && let Some(const3) = check_for_unsigned_int_constant(cx, right)
 +            // Also ensures the const is nonzero since zero can't be a divisor
 +            && const1 == const2 && const2 == const3
++            && let Some(hir_id) = path_to_local(expr3)
++            && let Some(Node::Pat(_)) = cx.tcx.hir().find(hir_id) {
 +                // Apply only to params or locals with annotated types
 +                match cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
 +                    Some(Node::Param(..)) => (),
 +                    Some(Node::Local(local)) => {
 +                        let Some(ty) = local.ty else { return };
 +                        if matches!(ty.kind, TyKind::Infer) {
 +                            return;
 +                        }
 +                    }
 +                    _ => return,
 +                };
 +
 +                let mut app = Applicability::MachineApplicable;
 +                let rem_of = snippet_with_applicability(cx, expr3.span, "_", &mut app);
 +                span_lint_and_sugg(
 +                    cx,
 +                    MANUAL_REM_EUCLID,
 +                    expr.span,
 +                    "manual `rem_euclid` implementation",
 +                    "consider using",
 +                    format!("{rem_of}.rem_euclid({const1})"),
 +                    app,
 +                );
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +// Checks if either the left or right expressions can be an unsigned int constant and returns that
 +// constant along with the other expression unchanged if so
 +fn check_for_either_unsigned_int_constant<'a>(
 +    cx: &'a LateContext<'_>,
 +    left: &'a Expr<'_>,
 +    right: &'a Expr<'_>,
 +) -> Option<(u128, &'a Expr<'a>)> {
 +    check_for_unsigned_int_constant(cx, left)
 +        .map(|int_const| (int_const, right))
 +        .or_else(|| check_for_unsigned_int_constant(cx, right).map(|int_const| (int_const, left)))
 +}
 +
 +fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<u128> {
 +    let Some(int_const) = constant_full_int(cx, cx.typeck_results(), expr) else { return None };
 +    match int_const {
 +        FullInt::S(s) => s.try_into().ok(),
 +        FullInt::U(u) => Some(u),
 +    }
 +}
index 542905a2d763cffa4879b4f75d7acefa3e4aa7a3,0000000000000000000000000000000000000000..8f98b43b9e5c8d240327b269a2961468d0e28275
mode 100644,000000..100644
--- /dev/null
@@@ -1,306 -1,0 +1,306 @@@
-     let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability));
 +use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
 +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_lang_ctor, is_lint_allowed, 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;
 +
 +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)),
 +    ) {
 +        (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()
 +            } 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::Mutable) {
 +                    "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!("|{}{}| unsafe {{ {} }}", annotation, some_binding, 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!("|{}| unsafe {{ {} }}", pat_snip, 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!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str)
 +        } else {
 +            format!("{}{}.map({})", scrutinee_str, as_ref_str, 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_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None),
 +            PatKind::TupleStruct(ref qpath, [pattern], _)
 +                if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt =>
 +            {
 +                Some(OptionPat::Some { pattern, ref_count })
 +            },
 +            _ => None,
 +        }
 +    }
 +    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>,
 +    expr: &'tcx Expr<'_>,
 +    needs_unsafe_block: bool,
 +    ctxt: SyntaxContext,
 +) -> Option<SomeExpr<'tcx>> {
 +    // TODO: Allow more complex expressions.
 +    match expr.kind {
 +        ExprKind::Call(
 +            Expr {
 +                kind: ExprKind::Path(ref qpath),
 +                ..
 +            },
 +            [arg],
 +        ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(SomeExpr {
 +            expr: arg,
 +            needs_unsafe_block,
 +        }),
 +        ExprKind::Block(
 +            Block {
 +                stmts: [],
 +                expr: Some(expr),
 +                rules,
 +                ..
 +            },
 +            _,
 +        ) => get_some_expr(
 +            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 {
 +    matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
 +}
index a68eec842abc51a08418e793c9b670e93e5b45a6,0000000000000000000000000000000000000000..0da4833f1dfe0c9dfc5694874ab0cad257c30487
mode 100644,000000..100644
--- /dev/null
@@@ -1,171 -1,0 +1,171 @@@
-         if let Some(b0) = find_bool_lit(&first_expr.kind, is_if_let);
-         if let Some(b1) = find_bool_lit(&last_expr.kind, is_if_let);
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::is_wild;
 +use clippy_utils::source::snippet_with_applicability;
 +use rustc_ast::{Attribute, LitKind};
 +use rustc_errors::Applicability;
 +use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +use rustc_span::source_map::Spanned;
 +
 +use super::MATCH_LIKE_MATCHES_MACRO;
 +
 +/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
 +pub(crate) fn check_if_let<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    let_pat: &'tcx Pat<'_>,
 +    let_expr: &'tcx Expr<'_>,
 +    then_expr: &'tcx Expr<'_>,
 +    else_expr: &'tcx Expr<'_>,
 +) {
 +    find_matches_sugg(
 +        cx,
 +        let_expr,
 +        IntoIterator::into_iter([
 +            (&[][..], Some(let_pat), then_expr, None),
 +            (&[][..], None, else_expr, None),
 +        ]),
 +        expr,
 +        true,
 +    );
 +}
 +
 +pub(super) fn check_match<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'_>,
 +    scrutinee: &'tcx Expr<'_>,
 +    arms: &'tcx [Arm<'tcx>],
 +) -> bool {
 +    find_matches_sugg(
 +        cx,
 +        scrutinee,
 +        arms.iter().map(|arm| {
 +            (
 +                cx.tcx.hir().attrs(arm.hir_id),
 +                Some(arm.pat),
 +                arm.body,
 +                arm.guard.as_ref(),
 +            )
 +        }),
 +        e,
 +        false,
 +    )
 +}
 +
 +/// Lint a `match` or `if let` for replacement by `matches!`
 +fn find_matches_sugg<'a, 'b, I>(
 +    cx: &LateContext<'_>,
 +    ex: &Expr<'_>,
 +    mut iter: I,
 +    expr: &Expr<'_>,
 +    is_if_let: bool,
 +) -> bool
 +where
 +    'b: 'a,
 +    I: Clone
 +        + DoubleEndedIterator
 +        + ExactSizeIterator
 +        + Iterator<
 +            Item = (
 +                &'a [Attribute],
 +                Option<&'a Pat<'b>>,
 +                &'a Expr<'b>,
 +                Option<&'a Guard<'b>>,
 +            ),
 +        >,
 +{
 +    if_chain! {
 +        if iter.len() >= 2;
 +        if cx.typeck_results().expr_ty(expr).is_bool();
 +        if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
 +        let iter_without_last = iter.clone();
 +        if let Some((first_attrs, _, first_expr, first_guard)) = iter.next();
-                 find_bool_lit(&arm.2.kind, is_if_let).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
++        if let Some(b0) = find_bool_lit(&first_expr.kind);
++        if let Some(b1) = find_bool_lit(&last_expr.kind);
 +        if b0 != b1;
 +        if first_guard.is_none() || iter.len() == 0;
 +        if first_attrs.is_empty();
 +        if iter
 +            .all(|arm| {
- fn find_bool_lit(ex: &ExprKind<'_>, is_if_let: bool) -> Option<bool> {
++                find_bool_lit(&arm.2.kind).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
 +            });
 +        then {
 +            if let Some(last_pat) = last_pat_opt {
 +                if !is_wild(last_pat) {
 +                    return false;
 +                }
 +            }
 +
 +            // The suggestion may be incorrect, because some arms can have `cfg` attributes
 +            // evaluated into `false` and so such arms will be stripped before.
 +            let mut applicability = Applicability::MaybeIncorrect;
 +            let pat = {
 +                use itertools::Itertools as _;
 +                iter_without_last
 +                    .filter_map(|arm| {
 +                        let pat_span = arm.1?.span;
 +                        Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability))
 +                    })
 +                    .join(" | ")
 +            };
 +            let pat_and_guard = if let Some(Guard::If(g)) = first_guard {
 +                format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability))
 +            } else {
 +                pat
 +            };
 +
 +            // strip potential borrows (#6503), but only if the type is a reference
 +            let mut ex_new = ex;
 +            if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind {
 +                if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() {
 +                    ex_new = ex_inner;
 +                }
 +            };
 +            span_lint_and_sugg(
 +                cx,
 +                MATCH_LIKE_MATCHES_MACRO,
 +                expr.span,
 +                &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }),
 +                "try this",
 +                format!(
 +                    "{}matches!({}, {})",
 +                    if b0 { "" } else { "!" },
 +                    snippet_with_applicability(cx, ex_new.span, "..", &mut applicability),
 +                    pat_and_guard,
 +                ),
 +                applicability,
 +            );
 +            true
 +        } else {
 +            false
 +        }
 +    }
 +}
 +
 +/// Extract a `bool` or `{ bool }`
-         ) if is_if_let => {
++fn find_bool_lit(ex: &ExprKind<'_>) -> Option<bool> {
 +    match ex {
 +        ExprKind::Lit(Spanned {
 +            node: LitKind::Bool(b), ..
 +        }) => Some(*b),
 +        ExprKind::Block(
 +            rustc_hir::Block {
 +                stmts: &[],
 +                expr: Some(exp),
 +                ..
 +            },
 +            _,
++        ) => {
 +            if let ExprKind::Lit(Spanned {
 +                node: LitKind::Bool(b), ..
 +            }) = exp.kind
 +            {
 +                Some(b)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
index 15513de7d860d90dda22a8b7acaf4fcd5c8ec0be,0000000000000000000000000000000000000000..582782f245fcc5382ef06a3695b4d73c0316f579
mode 100644,000000..100644
--- /dev/null
@@@ -1,414 -1,0 +1,414 @@@
-                 .find_map(|(j, other)| pat.has_overlapping_values(other).then(|| i + 1 + j))
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet;
 +use clippy_utils::{path_to_local, search_same, SpanlessEq, SpanlessHash};
 +use core::cmp::Ordering;
 +use core::iter;
 +use core::slice;
 +use rustc_arena::DroplessArena;
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdSet, Pat, PatKind, RangeEnd};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +use rustc_span::Symbol;
 +use std::collections::hash_map::Entry;
 +
 +use super::MATCH_SAME_ARMS;
 +
 +#[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()
-                     (forward_block == i || pat.has_overlapping_values(other)).then(|| j)
++                .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)| {
-                 // then the minium length required will be greater than the length of `pats`.
++                    (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) {
 +                    Entry::Vacant(entry) => entry,
 +                    // check if using the same bindings as before
 +                    Entry::Occupied(entry) => return *entry.get() == b_id,
 +                };
 +                // the names technically don't have to match; this makes the lint more conservative
 +                if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id);
 +                if cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b);
 +                if pat_contains_local(lhs.pat, a_id);
 +                if pat_contains_local(rhs.pat, b_id);
 +                then {
 +                    entry.insert(b_id);
 +                    true
 +                } else {
 +                    false
 +                }
 +            }
 +        };
 +        // Arms with a guard are ignored, those can’t always be merged together
 +        // If both arms overlap with an arm in between then these can't be merged either.
 +        !(backwards_blocking_idxs[max_index] > min_index && forwards_blocking_idxs[min_index] < max_index)
 +                && lhs.guard.is_none()
 +                && rhs.guard.is_none()
 +                && SpanlessEq::new(cx)
 +                    .expr_fallback(eq_fallback)
 +                    .eq_expr(lhs.body, rhs.body)
 +                // these checks could be removed to allow unused bindings
 +                && bindings_eq(lhs.pat, local_map.keys().copied().collect())
 +                && bindings_eq(rhs.pat, local_map.values().copied().collect())
 +    };
 +
 +    let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
 +    for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) {
 +        if matches!(arm2.pat.kind, PatKind::Wild) {
 +            span_lint_and_then(
 +                cx,
 +                MATCH_SAME_ARMS,
 +                arm1.span,
 +                "this match arm has an identical body to the `_` wildcard arm",
 +                |diag| {
 +                    diag.span_suggestion(arm1.span, "try removing the arm", "", 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> {
 +    #[expect(clippy::too_many_lines)]
 +    fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self {
 +        match pat.kind {
 +            PatKind::Wild | PatKind::Binding(.., None) => Self::Wild,
 +            PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Ref(pat, _) => {
 +                Self::from_pat(cx, arena, pat)
 +            },
 +            PatKind::Struct(ref path, fields, _) => {
 +                let fields =
 +                    arena.alloc_from_iter(fields.iter().map(|f| (f.ident.name, Self::from_pat(cx, arena, f.pat))));
 +                fields.sort_by_key(|&(name, _)| name);
 +                Self::Struct(cx.qpath_res(path, pat.hir_id).opt_def_id(), fields)
 +            },
 +            PatKind::TupleStruct(ref path, pats, wild_idx) => {
 +                let adt = match cx.typeck_results().pat_ty(pat).ty_adt_def() {
 +                    Some(x) => x,
 +                    None => return Self::Wild,
 +                };
 +                let (var_id, variant) = if adt.is_enum() {
 +                    match cx.qpath_res(path, pat.hir_id).opt_def_id() {
 +                        Some(x) => (Some(x), adt.variant_with_ctor_id(x)),
 +                        None => return Self::Wild,
 +                    }
 +                } else {
 +                    (None, adt.non_enum_variant())
 +                };
 +                let (front, back) = match wild_idx {
 +                    Some(i) => pats.split_at(i),
 +                    None => (pats, [].as_slice()),
 +                };
 +                let pats = arena.alloc_from_iter(
 +                    front
 +                        .iter()
 +                        .map(|pat| Self::from_pat(cx, arena, pat))
 +                        .chain(iter::repeat_with(|| Self::Wild).take(variant.fields.len() - pats.len()))
 +                        .chain(back.iter().map(|pat| Self::from_pat(cx, arena, pat))),
 +                );
 +                Self::Tuple(var_id, pats)
 +            },
 +            PatKind::Or(pats) => Self::Or(arena.alloc_from_iter(pats.iter().map(|pat| Self::from_pat(cx, arena, pat)))),
 +            PatKind::Path(ref path) => Self::Path(cx.qpath_res(path, pat.hir_id).opt_def_id()),
 +            PatKind::Tuple(pats, wild_idx) => {
 +                let field_count = match cx.typeck_results().pat_ty(pat).kind() {
 +                    ty::Tuple(subs) => subs.len(),
 +                    _ => return Self::Wild,
 +                };
 +                let (front, back) = match wild_idx {
 +                    Some(i) => pats.split_at(i),
 +                    None => (pats, [].as_slice()),
 +                };
 +                let pats = arena.alloc_from_iter(
 +                    front
 +                        .iter()
 +                        .map(|pat| Self::from_pat(cx, arena, pat))
 +                        .chain(iter::repeat_with(|| Self::Wild).take(field_count - pats.len()))
 +                        .chain(back.iter().map(|pat| Self::from_pat(cx, arena, pat))),
 +                );
 +                Self::Tuple(None, pats)
 +            },
 +            PatKind::Lit(e) => match &e.kind {
 +                // TODO: Handle negative integers. They're currently treated as a wild match.
 +                ExprKind::Lit(lit) => match lit.node {
 +                    LitKind::Str(sym, _) => Self::LitStr(sym),
 +                    LitKind::ByteStr(ref bytes) => Self::LitBytes(bytes),
 +                    LitKind::Byte(val) => Self::LitInt(val.into()),
 +                    LitKind::Char(val) => Self::LitInt(val.into()),
 +                    LitKind::Int(val, _) => Self::LitInt(val),
 +                    LitKind::Bool(val) => Self::LitBool(val),
 +                    LitKind::Float(..) | LitKind::Err(_) => Self::Wild,
 +                },
 +                _ => Self::Wild,
 +            },
 +            PatKind::Range(start, end, bounds) => {
 +                // TODO: Handle negative integers. They're currently treated as a wild match.
 +                let start = match start {
 +                    None => 0,
 +                    Some(e) => match &e.kind {
 +                        ExprKind::Lit(lit) => match lit.node {
 +                            LitKind::Int(val, _) => val,
 +                            LitKind::Char(val) => val.into(),
 +                            LitKind::Byte(val) => val.into(),
 +                            _ => return Self::Wild,
 +                        },
 +                        _ => return Self::Wild,
 +                    },
 +                };
 +                let (end, bounds) = match end {
 +                    None => (u128::MAX, RangeEnd::Included),
 +                    Some(e) => match &e.kind {
 +                        ExprKind::Lit(lit) => match lit.node {
 +                            LitKind::Int(val, _) => (val, bounds),
 +                            LitKind::Char(val) => (val.into(), bounds),
 +                            LitKind::Byte(val) => (val.into(), bounds),
 +                            _ => return Self::Wild,
 +                        },
 +                        _ => return Self::Wild,
 +                    },
 +                };
 +                Self::Range(PatRange { start, end, bounds })
 +            },
 +            PatKind::Slice(front, wild_pat, back) => Self::Slice(
 +                arena.alloc_from_iter(front.iter().map(|pat| Self::from_pat(cx, arena, pat))),
 +                wild_pat.map(|_| &*arena.alloc_from_iter(back.iter().map(|pat| Self::from_pat(cx, arena, pat)))),
 +            ),
 +        }
 +    }
 +
 +    /// Checks if two patterns overlap in the values they can match assuming they are for the same
 +    /// type.
 +    fn has_overlapping_values(&self, other: &Self) -> bool {
 +        match (*self, *other) {
 +            (Self::Wild, _) | (_, Self::Wild) => true,
 +            (Self::Or(pats), ref other) | (ref other, Self::Or(pats)) => {
 +                pats.iter().any(|pat| pat.has_overlapping_values(other))
 +            },
 +            (Self::Struct(lpath, lfields), Self::Struct(rpath, rfields)) => {
 +                if lpath != rpath {
 +                    return false;
 +                }
 +                iter_matching_struct_fields(lfields, rfields).all(|(lpat, rpat)| lpat.has_overlapping_values(rpat))
 +            },
 +            (Self::Tuple(lpath, lpats), Self::Tuple(rpath, rpats)) => {
 +                if lpath != rpath {
 +                    return false;
 +                }
 +                lpats
 +                    .iter()
 +                    .zip(rpats.iter())
 +                    .all(|(lpat, rpat)| lpat.has_overlapping_values(rpat))
 +            },
 +            (Self::Path(x), Self::Path(y)) => x == y,
 +            (Self::LitStr(x), Self::LitStr(y)) => x == y,
 +            (Self::LitBytes(x), Self::LitBytes(y)) => x == y,
 +            (Self::LitInt(x), Self::LitInt(y)) => x == y,
 +            (Self::LitBool(x), Self::LitBool(y)) => x == y,
 +            (Self::Range(ref x), Self::Range(ref y)) => x.overlaps(y),
 +            (Self::Range(ref range), Self::LitInt(x)) | (Self::LitInt(x), Self::Range(ref range)) => range.contains(x),
 +            (Self::Slice(lpats, None), Self::Slice(rpats, None)) => {
 +                lpats.len() == rpats.len() && lpats.iter().zip(rpats.iter()).all(|(x, y)| x.has_overlapping_values(y))
 +            },
 +            (Self::Slice(pats, None), Self::Slice(front, Some(back)))
 +            | (Self::Slice(front, Some(back)), Self::Slice(pats, None)) => {
 +                // Here `pats` is an exact size match. If the combined lengths of `front` and `back` are greater
++                // then the 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 3077b999f4ee445f72716412911a1238eb2d4276,0000000000000000000000000000000000000000..d55082c66dc864a4fa4a1254a082c1fbd639512a
mode 100644,000000..100644
--- /dev/null
@@@ -1,1144 -1,0 +1,1144 @@@
-         (data.ctxt == SyntaxContext::root()).then(|| (data.lo, data.hi))
 +use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context};
 +use clippy_utils::{higher, in_constant, 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};
 +
 +mod collapsible_match;
 +mod infallible_destructuring_match;
 +mod manual_map;
 +mod manual_unwrap_or;
 +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;
 +
 +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,
 +    suspicious,
 +    "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`"
 +}
 +
 +#[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,
 +]);
 +
 +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 && !span_starts_with(cx, expr.span, "match") {
 +                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);
 +                    }
 +
 +                    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);
 +                    }
 +                }
 +                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();
-         (!span_contains_cfg(cx, span)).then(|| next_start).ok_or(())
++        (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;
 +        (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 0704a5af52595ce158e7760b7ec8975722de8d2c,0000000000000000000000000000000000000000..b0b15b3f54cd8b35f253218596c49c61aebc27be
mode 100644,000000..100644
--- /dev/null
@@@ -1,396 -1,0 +1,400 @@@
 +use crate::FxHashSet;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::{indent_of, snippet};
 +use clippy_utils::{get_attr, is_lint_allowed};
 +use rustc_errors::{Applicability, Diagnostic};
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::ty::subst::GenericArgKind;
 +use rustc_middle::ty::{Ty, TypeAndMut};
 +use rustc_span::Span;
 +
 +use super::SIGNIFICANT_DROP_IN_SCRUTINEE;
 +
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    scrutinee: &'tcx Expr<'_>,
 +    arms: &'tcx [Arm<'_>],
 +    source: MatchSource,
 +) {
 +    if is_lint_allowed(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, expr.hir_id) {
 +        return;
 +    }
 +
 +    if let Some((suggestions, message)) = has_significant_drop_in_scrutinee(cx, scrutinee, source) {
 +        for found in suggestions {
 +            span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| {
 +                set_diagnostic(diag, cx, expr, found);
 +                let s = Span::new(expr.span.hi(), expr.span.hi(), expr.span.ctxt(), None);
 +                diag.span_label(s, "temporary lives until here");
 +                for span in has_significant_drop_in_arms(cx, arms) {
 +                    diag.span_label(span, "another value with significant `Drop` created here");
 +                }
 +                diag.note("this might lead to deadlocks or other unexpected behavior");
 +            });
 +        }
 +    }
 +}
 +
 +fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) {
 +    if found.lint_suggestion == LintSuggestion::MoveAndClone {
 +        // If our suggestion is to move and clone, then we want to leave it to the user to
 +        // decide how to address this lint, since it may be that cloning is inappropriate.
 +        // Therefore, we won't to emit a suggestion.
 +        return;
 +    }
 +
 +    let original = snippet(cx, found.found_span, "..");
 +    let trailing_indent = " ".repeat(indent_of(cx, found.found_span).unwrap_or(0));
 +
 +    let replacement = if found.lint_suggestion == LintSuggestion::MoveAndDerefToCopy {
 +        format!("let value = *{};\n{}", original, trailing_indent)
 +    } else if found.is_unit_return_val {
 +        // If the return value of the expression to be moved is unit, then we don't need to
 +        // capture the result in a temporary -- we can just replace it completely with `()`.
 +        format!("{};\n{}", original, trailing_indent)
 +    } else {
 +        format!("let value = {};\n{}", original, trailing_indent)
 +    };
 +
 +    let suggestion_message = if found.lint_suggestion == LintSuggestion::MoveOnly {
 +        "try moving the temporary above the match"
 +    } else {
 +        "try moving the temporary above the match and create a copy"
 +    };
 +
 +    let scrutinee_replacement = if found.is_unit_return_val {
 +        "()".to_owned()
 +    } else {
 +        "value".to_owned()
 +    };
 +
 +    diag.multipart_suggestion(
 +        suggestion_message,
 +        vec![
 +            (expr.span.shrink_to_lo(), replacement),
 +            (found.found_span, scrutinee_replacement),
 +        ],
 +        Applicability::MaybeIncorrect,
 +    );
 +}
 +
 +/// If the expression is an `ExprKind::Match`, check if the scrutinee has a significant drop that
 +/// may have a surprising lifetime.
 +fn has_significant_drop_in_scrutinee<'tcx, 'a>(
 +    cx: &'a LateContext<'tcx>,
 +    scrutinee: &'tcx Expr<'tcx>,
 +    source: MatchSource,
 +) -> Option<(Vec<FoundSigDrop>, &'static str)> {
 +    let mut helper = SigDropHelper::new(cx);
++    let scrutinee = match (source, &scrutinee.kind) {
++        (MatchSource::ForLoopDesugar, ExprKind::Call(_, [e])) => e,
++        _ => scrutinee,
++    };
 +    helper.find_sig_drop(scrutinee).map(|drops| {
 +        let message = if source == MatchSource::Normal {
 +            "temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression"
 +        } else {
 +            "temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression"
 +        };
 +        (drops, message)
 +    })
 +}
 +
 +struct SigDropChecker<'a, 'tcx> {
 +    seen_types: FxHashSet<Ty<'tcx>>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> SigDropChecker<'a, 'tcx> {
 +        SigDropChecker {
 +            seen_types: FxHashSet::default(),
 +            cx,
 +        }
 +    }
 +
 +    fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> {
 +        self.cx.typeck_results().expr_ty(ex)
 +    }
 +
 +    fn has_seen_type(&mut self, ty: Ty<'tcx>) -> bool {
 +        !self.seen_types.insert(ty)
 +    }
 +
 +    fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +        if let Some(adt) = ty.ty_adt_def() {
 +            if get_attr(cx.sess(), cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop").count() > 0 {
 +                return true;
 +            }
 +        }
 +
 +        match ty.kind() {
 +            rustc_middle::ty::Adt(a, b) => {
 +                for f in a.all_fields() {
 +                    let ty = f.ty(cx.tcx, b);
 +                    if !self.has_seen_type(ty) && self.has_sig_drop_attr(cx, ty) {
 +                        return true;
 +                    }
 +                }
 +
 +                for generic_arg in b.iter() {
 +                    if let GenericArgKind::Type(ty) = generic_arg.unpack() {
 +                        if self.has_sig_drop_attr(cx, ty) {
 +                            return true;
 +                        }
 +                    }
 +                }
 +                false
 +            },
 +            rustc_middle::ty::Array(ty, _)
 +            | rustc_middle::ty::RawPtr(TypeAndMut { ty, .. })
 +            | rustc_middle::ty::Ref(_, ty, _)
 +            | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty),
 +            _ => false,
 +        }
 +    }
 +}
 +
 +struct SigDropHelper<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    is_chain_end: bool,
 +    has_significant_drop: bool,
 +    current_sig_drop: Option<FoundSigDrop>,
 +    sig_drop_spans: Option<Vec<FoundSigDrop>>,
 +    special_handling_for_binary_op: bool,
 +    sig_drop_checker: SigDropChecker<'a, 'tcx>,
 +}
 +
 +#[expect(clippy::enum_variant_names)]
 +#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 +enum LintSuggestion {
 +    MoveOnly,
 +    MoveAndDerefToCopy,
 +    MoveAndClone,
 +}
 +
 +#[derive(Clone, Copy)]
 +struct FoundSigDrop {
 +    found_span: Span,
 +    is_unit_return_val: bool,
 +    lint_suggestion: LintSuggestion,
 +}
 +
 +impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> SigDropHelper<'a, 'tcx> {
 +        SigDropHelper {
 +            cx,
 +            is_chain_end: true,
 +            has_significant_drop: false,
 +            current_sig_drop: None,
 +            sig_drop_spans: None,
 +            special_handling_for_binary_op: false,
 +            sig_drop_checker: SigDropChecker::new(cx),
 +        }
 +    }
 +
 +    fn find_sig_drop(&mut self, match_expr: &'tcx Expr<'_>) -> Option<Vec<FoundSigDrop>> {
 +        self.visit_expr(match_expr);
 +
 +        // If sig drop spans is empty but we found a significant drop, it means that we didn't find
 +        // a type that was trivially copyable as we moved up the chain after finding a significant
 +        // drop, so move the entire scrutinee.
 +        if self.has_significant_drop && self.sig_drop_spans.is_none() {
 +            self.try_setting_current_suggestion(match_expr, true);
 +            self.move_current_suggestion();
 +        }
 +
 +        self.sig_drop_spans.take()
 +    }
 +
 +    fn replace_current_sig_drop(
 +        &mut self,
 +        found_span: Span,
 +        is_unit_return_val: bool,
 +        lint_suggestion: LintSuggestion,
 +    ) {
 +        self.current_sig_drop.replace(FoundSigDrop {
 +            found_span,
 +            is_unit_return_val,
 +            lint_suggestion,
 +        });
 +    }
 +
 +    /// This will try to set the current suggestion (so it can be moved into the suggestions vec
 +    /// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us
 +    /// an opportunity to look for another type in the chain that will be trivially copyable.
 +    /// However, if we are at the the end of the chain, we want to accept whatever is there. (The
 +    /// suggestion won't actually be output, but the diagnostic message will be output, so the user
 +    /// can determine the best way to handle the lint.)
 +    fn try_setting_current_suggestion(&mut self, expr: &'tcx Expr<'_>, allow_move_and_clone: bool) {
 +        if self.current_sig_drop.is_some() {
 +            return;
 +        }
 +        let ty = self.sig_drop_checker.get_type(expr);
 +        if ty.is_ref() {
 +            // We checked that the type was ref, so builtin_deref will return Some TypeAndMut,
 +            // but let's avoid any chance of an ICE
 +            if let Some(TypeAndMut { ty, .. }) = ty.builtin_deref(true) {
 +                if ty.is_trivially_pure_clone_copy() {
 +                    self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndDerefToCopy);
 +                } else if allow_move_and_clone {
 +                    self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone);
 +                }
 +            }
 +        } else if ty.is_trivially_pure_clone_copy() {
 +            self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveOnly);
 +        } else if allow_move_and_clone {
 +            self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone);
 +        }
 +    }
 +
 +    fn move_current_suggestion(&mut self) {
 +        if let Some(current) = self.current_sig_drop.take() {
 +            self.sig_drop_spans.get_or_insert_with(Vec::new).push(current);
 +        }
 +    }
 +
 +    fn visit_exprs_for_binary_ops(
 +        &mut self,
 +        left: &'tcx Expr<'_>,
 +        right: &'tcx Expr<'_>,
 +        is_unit_return_val: bool,
 +        span: Span,
 +    ) {
 +        self.special_handling_for_binary_op = true;
 +        self.visit_expr(left);
 +        self.visit_expr(right);
 +
 +        // If either side had a significant drop, suggest moving the entire scrutinee to avoid
 +        // unnecessary copies and to simplify cases where both sides have significant drops.
 +        if self.has_significant_drop {
 +            self.replace_current_sig_drop(span, is_unit_return_val, LintSuggestion::MoveOnly);
 +        }
 +
 +        self.special_handling_for_binary_op = false;
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
 +    fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
 +        if !self.is_chain_end
 +            && self
 +                .sig_drop_checker
 +                .has_sig_drop_attr(self.cx, self.sig_drop_checker.get_type(ex))
 +        {
 +            self.has_significant_drop = true;
 +            return;
 +        }
 +        self.is_chain_end = false;
 +
 +        match ex.kind {
 +            ExprKind::MethodCall(_, [ref expr, ..], _) => {
 +                self.visit_expr(expr);
 +            }
 +            ExprKind::Binary(_, left, right) => {
 +                self.visit_exprs_for_binary_ops(left, right, false, ex.span);
 +            }
 +            ExprKind::Assign(left, right, _) | ExprKind::AssignOp(_, left, right) => {
 +                self.visit_exprs_for_binary_ops(left, right, true, ex.span);
 +            }
 +            ExprKind::Tup(exprs) => {
 +                for expr in exprs {
 +                    self.visit_expr(expr);
 +                    if self.has_significant_drop {
 +                        // We may have not have set current_sig_drop if all the suggestions were
 +                        // MoveAndClone, so add this tuple item's full expression in that case.
 +                        if self.current_sig_drop.is_none() {
 +                            self.try_setting_current_suggestion(expr, true);
 +                        }
 +
 +                        // Now we are guaranteed to have something, so add it to the final vec.
 +                        self.move_current_suggestion();
 +                    }
 +                    // Reset `has_significant_drop` after each tuple expression so we can look for
 +                    // additional cases.
 +                    self.has_significant_drop = false;
 +                }
 +                if self.sig_drop_spans.is_some() {
 +                    self.has_significant_drop = true;
 +                }
 +            }
 +            ExprKind::Box(..) |
 +            ExprKind::Array(..) |
 +            ExprKind::Call(..) |
 +            ExprKind::Unary(..) |
 +            ExprKind::If(..) |
 +            ExprKind::Match(..) |
 +            ExprKind::Field(..) |
 +            ExprKind::Index(..) |
 +            ExprKind::Ret(..) |
 +            ExprKind::Repeat(..) |
 +            ExprKind::Yield(..) |
 +            ExprKind::MethodCall(..) => walk_expr(self, ex),
 +            ExprKind::AddrOf(_, _, _) |
 +            ExprKind::Block(_, _) |
 +            ExprKind::Break(_, _) |
 +            ExprKind::Cast(_, _) |
 +            // Don't want to check the closure itself, only invocation, which is covered by MethodCall
 +            ExprKind::Closure { .. } |
 +            ExprKind::ConstBlock(_) |
 +            ExprKind::Continue(_) |
 +            ExprKind::DropTemps(_) |
 +            ExprKind::Err |
 +            ExprKind::InlineAsm(_) |
 +            ExprKind::Let(_) |
 +            ExprKind::Lit(_) |
 +            ExprKind::Loop(_, _, _, _) |
 +            ExprKind::Path(_) |
 +            ExprKind::Struct(_, _, _) |
 +            ExprKind::Type(_, _) => {
 +                return;
 +            }
 +        }
 +
 +        // Once a significant temporary has been found, we need to go back up at least 1 level to
 +        // find the span to extract for replacement, so the temporary gets dropped. However, for
 +        // binary ops, we want to move the whole scrutinee so we avoid unnecessary copies and to
 +        // simplify cases where both sides have significant drops.
 +        if self.has_significant_drop && !self.special_handling_for_binary_op {
 +            self.try_setting_current_suggestion(ex, false);
 +        }
 +    }
 +}
 +
 +struct ArmSigDropHelper<'a, 'tcx> {
 +    sig_drop_checker: SigDropChecker<'a, 'tcx>,
 +    found_sig_drop_spans: FxHashSet<Span>,
 +}
 +
 +impl<'a, 'tcx> ArmSigDropHelper<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> ArmSigDropHelper<'a, 'tcx> {
 +        ArmSigDropHelper {
 +            sig_drop_checker: SigDropChecker::new(cx),
 +            found_sig_drop_spans: FxHashSet::<Span>::default(),
 +        }
 +    }
 +}
 +
 +fn has_significant_drop_in_arms<'tcx, 'a>(cx: &'a LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> FxHashSet<Span> {
 +    let mut helper = ArmSigDropHelper::new(cx);
 +    for arm in arms {
 +        helper.visit_expr(arm.body);
 +    }
 +    helper.found_sig_drop_spans
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for ArmSigDropHelper<'a, 'tcx> {
 +    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
 +        if self
 +            .sig_drop_checker
 +            .has_sig_drop_attr(self.sig_drop_checker.cx, self.sig_drop_checker.get_type(ex))
 +        {
 +            self.found_sig_drop_spans.insert(ex.span);
 +            return;
 +        }
 +        walk_expr(self, ex);
 +    }
 +}
index 7dbfd95c50db40d1e0215690e6eff7d1f7ca942f,0000000000000000000000000000000000000000..692e22a7c5cf7138dbbc0f7cf6df0f06228721b3
mode 100644,000000..100644
--- /dev/null
@@@ -1,196 -1,0 +1,197 @@@
- /// lint use of `filter().map()` for `Iterators`
- fn lint_filter_some_map_unwrap(
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::{indent_of, reindent_multiline, snippet};
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, SpanlessEq};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::def::Res;
 +use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp};
 +use rustc_lint::LateContext;
++use rustc_middle::ty::adjustment::Adjust;
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::{sym, Symbol};
 +use std::borrow::Cow;
 +
 +use super::MANUAL_FILTER_MAP;
 +use super::MANUAL_FIND_MAP;
 +use super::OPTION_FILTER_MAP;
 +
 +fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool {
 +    match &expr.kind {
 +        hir::ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name,
 +        hir::ExprKind::Path(QPath::Resolved(_, segments)) => {
 +            segments.segments.last().unwrap().ident.name == method_name
 +        },
 +        hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
 +            let body = cx.tcx.hir().body(body);
 +            let closure_expr = peel_blocks(&body.value);
 +            let arg_id = body.params[0].pat.hir_id;
 +            match closure_expr.kind {
 +                hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, args, _) => {
 +                    if_chain! {
 +                    if ident.name == method_name;
 +                    if let hir::ExprKind::Path(path) = &args[0].kind;
 +                    if let Res::Local(ref local) = cx.qpath_res(path, args[0].hir_id);
 +                    then {
 +                        return arg_id == *local
 +                    }
 +                    }
 +                    false
 +                },
 +                _ => false,
 +            }
 +        },
 +        _ => false,
 +    }
 +}
 +
 +fn is_option_filter_map<'tcx>(cx: &LateContext<'tcx>, filter_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) -> bool {
 +    is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some))
 +}
 +
-     target_span: Span,
-     methods_span: Span,
- ) {
++/// is `filter(|x| x.is_some()).map(|x| x.unwrap())`
++fn is_filter_some_map_unwrap(
 +    cx: &LateContext<'_>,
 +    expr: &hir::Expr<'_>,
 +    filter_recv: &hir::Expr<'_>,
 +    filter_arg: &hir::Expr<'_>,
 +    map_arg: &hir::Expr<'_>,
-     if (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg) {
-         let msg = "`filter` for `Some` followed by `unwrap`";
-         let help = "consider using `flatten` instead";
-         let sugg = format!(
-             "{}",
-             reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, target_span),)
-         );
-         span_lint_and_sugg(
-             cx,
-             OPTION_FILTER_MAP,
-             methods_span,
-             msg,
-             help,
-             sugg,
-             Applicability::MachineApplicable,
-         );
-     }
++) -> bool {
 +    let iterator = is_trait_method(cx, expr, sym::Iterator);
 +    let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv), sym::Option);
-     lint_filter_some_map_unwrap(
-         cx,
-         expr,
-         filter_recv,
-         filter_arg,
-         map_arg,
-         map_span,
-         filter_span.with_hi(expr.span.hi()),
-     );
++
++    (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg)
 +}
 +
 +/// lint use of `filter().map()` or `find().map()` for `Iterators`
 +#[allow(clippy::too_many_arguments)]
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &hir::Expr<'_>,
 +    filter_recv: &hir::Expr<'_>,
 +    filter_arg: &hir::Expr<'_>,
 +    filter_span: Span,
 +    map_recv: &hir::Expr<'_>,
 +    map_arg: &hir::Expr<'_>,
 +    map_span: Span,
 +    is_find: bool,
 +) {
-             if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def();
++    if is_filter_some_map_unwrap(cx, expr, filter_recv, filter_arg, map_arg) {
++        span_lint_and_sugg(
++            cx,
++            OPTION_FILTER_MAP,
++            filter_span.with_hi(expr.span.hi()),
++            "`filter` for `Some` followed by `unwrap`",
++            "consider using `flatten` instead",
++            reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, map_span)).into_owned(),
++            Applicability::MachineApplicable,
++        );
++
++        return;
++    }
++
 +    if_chain! {
 +            if is_trait_method(cx, map_recv, sym::Iterator);
 +
 +            // filter(|x| ...is_some())...
 +            if let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind;
 +            let filter_body = cx.tcx.hir().body(filter_body_id);
 +            if let [filter_param] = filter_body.params;
 +            // optional ref pattern: `filter(|&x| ..)`
 +            let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind {
 +                (ref_pat, true)
 +            } else {
 +                (filter_param.pat, false)
 +            };
 +            // closure ends with is_some() or is_ok()
 +            if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind;
 +            if let ExprKind::MethodCall(path, [filter_arg], _) = filter_body.value.kind;
-                 if_chain! {
-                     if path_to_local_id(a_path, filter_param_id);
-                     if path_to_local_id(b, map_param_id);
-                     if cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b);
-                     then {
-                         return true;
-                     }
-                 }
-                 false
-             };
-             if match map_arg.kind {
-                 ExprKind::MethodCall(method, [original_arg], _) => {
-                     acceptable_methods(method)
-                         && SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, original_arg)
-                 },
-                 _ => SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg)
++            if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).peel_refs().ty_adt_def();
 +            if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::Option, opt_ty.did()) {
 +                Some(false)
 +            } else if cx.tcx.is_diagnostic_item(sym::Result, opt_ty.did()) {
 +                Some(true)
 +            } else {
 +                None
 +            };
 +            if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" };
 +
 +            // ...map(|x| ...unwrap())
 +            if let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind;
 +            let map_body = cx.tcx.hir().body(map_body_id);
 +            if let [map_param] = map_body.params;
 +            if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind;
 +            // closure ends with expect() or unwrap()
 +            if let ExprKind::MethodCall(seg, [map_arg, ..], _) = map_body.value.kind;
 +            if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or);
 +
++            // .filter(..).map(|y| f(y).copied().unwrap())
++            //                     ~~~~
++            let map_arg_peeled = match map_arg.kind {
++                ExprKind::MethodCall(method, [original_arg], _) if acceptable_methods(method) => {
++                    original_arg
++                },
++                _ => map_arg,
++            };
++
++            // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap())
++            let simple_equal = path_to_local_id(filter_arg, filter_param_id)
++                && path_to_local_id(map_arg_peeled, map_param_id);
++
 +            let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
 +                // in `filter(|x| ..)`, replace `*x` with `x`
 +                let a_path = if_chain! {
 +                    if !is_filter_param_ref;
 +                    if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind;
 +                    then { expr_path } else { a }
 +                };
 +                // let the filter closure arg and the map closure arg be equal
-                 let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name);
-                 let to_opt = if is_result { ".ok()" } else { "" };
-                 let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident,
-                     snippet(cx, map_arg.span, ".."), to_opt);
++                path_to_local_id(a_path, filter_param_id)
++                    && path_to_local_id(b, map_param_id)
++                    && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b)
 +            };
 +
++            if simple_equal || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg_peeled);
 +            then {
 +                let span = filter_span.with_hi(expr.span.hi());
 +                let (filter_name, lint) = if is_find {
 +                    ("find", MANUAL_FIND_MAP)
 +                } else {
 +                    ("filter", MANUAL_FILTER_MAP)
 +                };
++                let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`");
++                let (to_opt, deref) = if is_result {
++                    (".ok()", String::new())
++                } else {
++                    let derefs = cx.typeck_results()
++                        .expr_adjustments(map_arg)
++                        .iter()
++                        .filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
++                        .count();
++
++                    ("", "*".repeat(derefs))
++                };
++                let sugg = format!(
++                    "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})",
++                    snippet(cx, map_arg.span, ".."),
++                );
 +                span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable);
 +            }
 +    }
 +}
 +
 +fn acceptable_methods(method: &PathSegment<'_>) -> bool {
 +    let methods: [Symbol; 8] = [
 +        sym::clone,
 +        sym::as_ref,
 +        sym!(copied),
 +        sym!(cloned),
 +        sym!(as_deref),
 +        sym!(as_mut),
 +        sym!(as_deref_mut),
 +        sym!(to_owned),
 +    ];
 +
 +    methods.contains(&method.ident.name)
 +}
index 68a75667914aa0f8f6ab34c80d408ba3a812741a,0000000000000000000000000000000000000000..46d2fc493f81e19a5f6ba149a21ff150a75c0d8e
mode 100644,000000..100644
--- /dev/null
@@@ -1,99 -1,0 +1,99 @@@
-             (ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)).then(|| RepeatKind::String)
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type};
 +use clippy_utils::{is_expr_path_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, LangItem};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_span::symbol::sym;
 +use std::borrow::Cow;
 +
 +use super::MANUAL_STR_REPEAT;
 +
 +enum RepeatKind {
 +    String,
 +    Char(char),
 +}
 +
 +fn get_ty_param(ty: Ty<'_>) -> Option<Ty<'_>> {
 +    if let ty::Adt(_, subs) = ty.kind() {
 +        subs.types().next()
 +    } else {
 +        None
 +    }
 +}
 +
 +fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<RepeatKind> {
 +    if let ExprKind::Lit(lit) = &e.kind {
 +        match lit.node {
 +            LitKind::Str(..) => Some(RepeatKind::String),
 +            LitKind::Char(c) => Some(RepeatKind::Char(c)),
 +            _ => None,
 +        }
 +    } else {
 +        let ty = cx.typeck_results().expr_ty(e);
 +        if is_type_diagnostic_item(cx, ty, sym::String)
 +            || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, Ty::is_str))
 +            || (match_type(cx, ty, &paths::COW) && get_ty_param(ty).map_or(false, Ty::is_str))
 +        {
 +            Some(RepeatKind::String)
 +        } else {
 +            let ty = ty.peel_refs();
++            (ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)).then_some(RepeatKind::String)
 +        }
 +    }
 +}
 +
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    collect_expr: &Expr<'_>,
 +    take_expr: &Expr<'_>,
 +    take_self_arg: &Expr<'_>,
 +    take_arg: &Expr<'_>,
 +) {
 +    if_chain! {
 +        if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind;
 +        if is_expr_path_def_path(cx, repeat_fn, &paths::ITER_REPEAT);
 +        if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(collect_expr), sym::String);
 +        if let Some(collect_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id);
 +        if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id);
 +        if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
 +        if cx.tcx.trait_of_item(collect_id) == Some(iter_trait_id);
 +        if cx.tcx.trait_of_item(take_id) == Some(iter_trait_id);
 +        if let Some(repeat_kind) = parse_repeat_arg(cx, repeat_arg);
 +        let ctxt = collect_expr.span.ctxt();
 +        if ctxt == take_expr.span.ctxt();
 +        if ctxt == take_self_arg.span.ctxt();
 +        then {
 +            let mut app = Applicability::MachineApplicable;
 +            let count_snip = snippet_with_context(cx, take_arg.span, ctxt, "..", &mut app).0;
 +
 +            let val_str = match repeat_kind {
 +                RepeatKind::Char(_) if repeat_arg.span.ctxt() != ctxt => return,
 +                RepeatKind::Char('\'') => r#""'""#.into(),
 +                RepeatKind::Char('"') => r#""\"""#.into(),
 +                RepeatKind::Char(_) =>
 +                    match snippet_with_applicability(cx, repeat_arg.span, "..", &mut app) {
 +                        Cow::Owned(s) => Cow::Owned(format!("\"{}\"", &s[1..s.len() - 1])),
 +                        s @ Cow::Borrowed(_) => s,
 +                    },
 +                RepeatKind::String =>
 +                    Sugg::hir_with_context(cx, repeat_arg, ctxt, "..", &mut app).maybe_par().to_string().into(),
 +            };
 +
 +            span_lint_and_sugg(
 +                cx,
 +                MANUAL_STR_REPEAT,
 +                collect_expr.span,
 +                "manual implementation of `str::repeat` using iterators",
 +                "try this",
 +                format!("{}.repeat({})", val_str, count_snip),
 +                app
 +            )
 +        }
 +    }
 +}
index 9bb7bb7a7aba468e53abd72274fa654340965bc3,0000000000000000000000000000000000000000..6981b4a66318ed1f88de71bb6365ae9e82a5499c
mode 100644,000000..100644
--- /dev/null
@@@ -1,3011 -1,0 +1,3018 @@@
-     #[clippy::version = "1.61.0"]
 +mod bind_instead_of_map;
 +mod bytes_nth;
 +mod chars_cmp;
 +mod chars_cmp_with_unwrap;
 +mod chars_last_cmp;
 +mod chars_last_cmp_with_unwrap;
 +mod chars_next_cmp;
 +mod chars_next_cmp_with_unwrap;
 +mod clone_on_copy;
 +mod clone_on_ref_ptr;
 +mod cloned_instead_of_copied;
 +mod err_expect;
 +mod expect_fun_call;
 +mod expect_used;
 +mod extend_with_drain;
 +mod filetype_is_file;
 +mod filter_map;
 +mod filter_map_identity;
 +mod filter_map_next;
 +mod filter_next;
 +mod flat_map_identity;
 +mod flat_map_option;
 +mod from_iter_instead_of_collect;
 +mod get_last_with_len;
 +mod get_unwrap;
 +mod implicit_clone;
 +mod inefficient_to_string;
 +mod inspect_for_each;
 +mod into_iter_on_ref;
 +mod is_digit_ascii_radix;
 +mod iter_cloned_collect;
 +mod iter_count;
 +mod iter_next_slice;
 +mod iter_nth;
 +mod iter_nth_zero;
 +mod iter_overeager_cloned;
 +mod iter_skip_next;
 +mod iter_with_drain;
 +mod iterator_step_by_zero;
 +mod manual_saturating_arithmetic;
 +mod manual_str_repeat;
 +mod map_collect_result_unit;
 +mod map_flatten;
 +mod map_identity;
 +mod map_unwrap_or;
 +mod needless_option_as_deref;
 +mod needless_option_take;
 +mod no_effect_replace;
 +mod ok_expect;
 +mod option_as_ref_deref;
 +mod option_map_or_none;
 +mod option_map_unwrap_or;
 +mod or_fun_call;
 +mod or_then_unwrap;
 +mod search_is_some;
 +mod single_char_add_str;
 +mod single_char_insert_string;
 +mod single_char_pattern;
 +mod single_char_push_string;
 +mod skip_while_next;
 +mod str_splitn;
 +mod string_extend_chars;
 +mod suspicious_map;
 +mod suspicious_splitn;
 +mod uninit_assumed_init;
 +mod unnecessary_filter_map;
 +mod unnecessary_fold;
 +mod unnecessary_iter_cloned;
 +mod unnecessary_join;
 +mod unnecessary_lazy_eval;
 +mod unnecessary_to_owned;
 +mod unwrap_or_else_default;
 +mod unwrap_used;
 +mod useless_asref;
 +mod utils;
 +mod wrong_self_convention;
 +mod zst_offset;
 +
 +use bind_instead_of_map::BindInsteadOfMap;
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 +use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
 +use clippy_utils::{contains_return, get_trait_def_id, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_hir::def::Res;
 +use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{self, TraitRef, Ty};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{sym, Span};
 +use rustc_typeck::hir_ty_to_ty;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `cloned()` on an `Iterator` or `Option` where
 +    /// `copied()` could be used instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// `copied()` is better because it guarantees that the type being cloned
 +    /// implements `Copy`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// [1, 2, 3].iter().cloned();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// [1, 2, 3].iter().copied();
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub CLONED_INSTEAD_OF_COPIED,
 +    pedantic,
 +    "used `cloned` where `copied` could be used instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's often inefficient to clone all elements of an iterator, when eventually, only some
 +    /// of them will be consumed.
 +    ///
 +    /// ### 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()` calls on `Option`s and on `Result`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is better to handle the `None` or `Err` case,
 +    /// or at least call `.expect(_)` with a more helpful message. Still, for a lot of
 +    /// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is
 +    /// `Allow` by default.
 +    ///
 +    /// `result.unwrap()` will let the thread panic on `Err` values.
 +    /// Normally, you want to implement more sophisticated error handling,
 +    /// and propagate errors upwards with `?` operator.
 +    ///
 +    /// Even if you want to panic on errors, not all `Error`s implement good
 +    /// messages on display. Therefore, it may be beneficial to look at the places
 +    /// where they may get displayed. Activate this lint to do just that.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let 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");
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub UNWRAP_USED,
 +    restriction,
 +    "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.expect()` calls on `Option`s and `Result`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// Usually it is better to handle the `None` or `Err` case.
 +    /// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why
 +    /// this lint is `Allow` by default.
 +    ///
 +    /// `result.expect()` will let the thread panic on `Err`
 +    /// values. Normally, you want to implement more sophisticated error handling,
 +    /// and propagate errors upwards with `?` operator.
 +    ///
 +    /// ### Examples
 +    /// ```rust,ignore
 +    /// # let 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");
 +    /// ```
-     /// 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).
++    #[clippy::version = "1.62.0"]
 +    pub ERR_EXPECT,
 +    style,
 +    r#"using `.err().expect("")` when `.expect_err("")` can be used"#
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and
 +    /// `Result` values.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, these can be written as `_.unwrap_or_default`, which is
 +    /// simpler and more concise.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let x = Some(1);
 +    /// x.unwrap_or_else(Default::default);
 +    /// x.unwrap_or_else(u32::default);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = Some(1);
 +    /// x.unwrap_or_default();
 +    /// ```
 +    #[clippy::version = "1.56.0"]
 +    pub UNWRAP_OR_ELSE_DEFAULT,
 +    style,
 +    "using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or
 +    /// `result.map(_).unwrap_or_else(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, these can be written more concisely (resp.) as
 +    /// `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.
 +    ///
 +    /// ### Known problems
 +    /// The order of the arguments is not in execution order
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// # fn some_function(foo: ()) -> usize { 1 }
 +    /// option.map(|a| a + 1).unwrap_or(0);
 +    /// result.map(|a| a + 1).unwrap_or_else(some_function);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// # fn some_function(foo: ()) -> usize { 1 }
 +    /// option.map_or(0, |a| a + 1);
 +    /// result.map_or_else(some_function, |a| a + 1);
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub MAP_UNWRAP_OR,
 +    pedantic,
 +    "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map_or(None, _)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.and_then(_)`.
 +    ///
 +    /// ### Known problems
 +    /// The order of the arguments is not in execution order.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    /// opt.map_or(None, |a| Some(a + 1));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    /// opt.and_then(|a| Some(a + 1));
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OPTION_MAP_OR_NONE,
 +    style,
 +    "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map_or(None, Some)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.ok()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let r: Result<u32, &str> = Ok(1);
 +    /// assert_eq!(Some(1), r.map_or(None, Some));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let r: Result<u32, &str> = Ok(1);
 +    /// assert_eq!(Some(1), r.ok());
 +    /// ```
 +    #[clippy::version = "1.44.0"]
 +    pub RESULT_MAP_OR_INTO_OPTION,
 +    style,
 +    "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or
 +    /// `_.or_else(|x| Err(y))`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.map(|x| y)` or `_.map_err(|x| y)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn opt() -> Option<&'static str> { Some("42") }
 +    /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
 +    /// let _ = opt().and_then(|s| Some(s.len()));
 +    /// let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });
 +    /// let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });
 +    /// ```
 +    ///
 +    /// The correct use would be:
 +    ///
 +    /// ```rust
 +    /// # fn opt() -> Option<&'static str> { Some("42") }
 +    /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
 +    /// let _ = opt().map(|s| s.len());
 +    /// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });
 +    /// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub BIND_INSTEAD_OF_MAP,
 +    complexity,
 +    "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter(_).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().filter(|x| **x == 0).next();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().find(|x| **x == 0);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FILTER_NEXT,
 +    complexity,
 +    "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.skip_while(condition).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find(!condition)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().skip_while(|x| **x == 0).next();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().find(|x| **x != 0);
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub SKIP_WHILE_NEXT,
 +    complexity,
 +    "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map(_).flatten(_)` on `Iterator` and `Option`
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.flat_map(_)` for `Iterator` or `_.and_then(_)` for `Option`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec = vec![vec![1]];
 +    /// let opt = Some(5);
 +    ///
 +    /// vec.iter().map(|x| x.iter()).flatten();
 +    /// opt.map(|x| Some(x * 2)).flatten();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![vec![1]];
 +    /// # let opt = Some(5);
 +    /// vec.iter().flat_map(|x| x.iter());
 +    /// opt.and_then(|x| Some(x * 2));
 +    /// ```
 +    #[clippy::version = "1.31.0"]
 +    pub MAP_FLATTEN,
 +    complexity,
 +    "using combinations of `flatten` and `map` which can usually be written as a single method call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter(_).map(_)` that can be written more simply
 +    /// as `filter_map(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code in the `filter` and `map` operations is poor style and
 +    /// less performant.
 +    ///
 +     /// ### Example
 +    /// ```rust
 +    /// # #![allow(unused)]
 +    /// (0_i32..10)
 +    ///     .filter(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// (0_i32..10).filter_map(|n| n.checked_add(1));
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub MANUAL_FILTER_MAP,
 +    complexity,
 +    "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.find(_).map(_)` that can be written more simply
 +    /// as `find_map(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code in the `find` and `map` operations is poor style and
 +    /// less performant.
 +    ///
 +     /// ### Example
 +    /// ```rust
 +    /// (0_i32..10)
 +    ///     .find(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// (0_i32..10).find_map(|n| n.checked_add(1));
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub MANUAL_FIND_MAP,
 +    complexity,
 +    "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter_map(_).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find_map(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    ///  (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();
 +    /// ```
 +    /// Can be written as
 +    ///
 +    /// ```rust
 +    ///  (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
 +    /// ```
 +    #[clippy::version = "1.36.0"]
 +    pub FILTER_MAP_NEXT,
 +    pedantic,
 +    "using combination of `filter_map` and `next` which can usually be written as a single method call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `flat_map(|x| x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely by using `flatten`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iter = vec![vec![0]].into_iter();
 +    /// iter.flat_map(|x| x);
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// # let iter = vec![vec![0]].into_iter();
 +    /// iter.flatten();
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub FLAT_MAP_IDENTITY,
 +    complexity,
 +    "call to `flat_map` where `flatten` is sufficient"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for an iterator or string search (such as `find()`,
 +    /// `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as:
 +    /// * `_.any(_)`, or `_.contains(_)` for `is_some()`,
 +    /// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #![allow(unused)]
 +    /// let vec = vec![1];
 +    /// vec.iter().find(|x| **x == 0).is_some();
 +    ///
 +    /// "hello world".find("world").is_none();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let vec = vec![1];
 +    /// vec.iter().any(|x| *x == 0);
 +    ///
 +    /// # #[allow(unused)]
 +    /// !"hello world".contains("world");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SEARCH_IS_SOME,
 +    complexity,
 +    "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.chars().next()` on a `str` to check
 +    /// if it starts with a given char.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.starts_with(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let name = "foo";
 +    /// if name.chars().next() == Some('_') {};
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let name = "foo";
 +    /// if name.starts_with('_') {};
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CHARS_NEXT_CMP,
 +    style,
 +    "using `.chars().next()` to check if a string starts with a char"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,
 +    /// etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or
 +    /// `unwrap_or_default` instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// The function will always be called and potentially
 +    /// allocate an object acting as the default.
 +    ///
 +    /// ### Known problems
 +    /// If the function has side-effects, not calling it will
 +    /// change the semantic of the program, but you shouldn't rely on that anyway.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or(String::new());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or_else(String::new);
 +    ///
 +    /// // or
 +    ///
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or_default();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OR_FUN_CALL,
 +    perf,
 +    "using any `*or` method with a function call, which suggests `*or_else`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.or(…).unwrap()` calls to Options and Results.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `.unwrap_or(…)` instead for clarity.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let fallback = "fallback";
 +    /// // Result
 +    /// # type Error = &'static str;
 +    /// # let result: Result<&str, Error> = Err("error");
 +    /// let value = result.or::<Error>(Ok(fallback)).unwrap();
 +    ///
 +    /// // Option
 +    /// # let option: Option<&str> = None;
 +    /// let value = option.or(Some(fallback)).unwrap();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let fallback = "fallback";
 +    /// // Result
 +    /// # let result: Result<&str, &str> = Err("error");
 +    /// let value = result.unwrap_or(fallback);
 +    ///
 +    /// // Option
 +    /// # let option: Option<&str> = None;
 +    /// let value = option.unwrap_or(fallback);
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub OR_THEN_UNWRAP,
 +    complexity,
 +    "checks for `.or(…).unwrap()` calls to Options and Results."
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,
 +    /// etc., and suggests to use `unwrap_or_else` instead
 +    ///
 +    /// ### Why is this bad?
 +    /// The function will always be called.
 +    ///
 +    /// ### Known problems
 +    /// If the function has side-effects, not calling it will
 +    /// change the semantics of the program, but you shouldn't rely on that anyway.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # let err_msg = "I'm a teapot";
 +    /// foo.expect(&format!("Err {}: {}", err_code, err_msg));
 +    ///
 +    /// // or
 +    ///
 +    /// # let foo = Some(String::new());
 +    /// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # let err_msg = "I'm a teapot";
 +    /// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXPECT_FUN_CALL,
 +    perf,
 +    "using any `expect` method with a function call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on a `Copy` type.
 +    ///
 +    /// ### Why is this bad?
 +    /// The only reason `Copy` types implement `Clone` is for
 +    /// generics, not for using the `clone` method on a concrete type.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// 42u64.clone();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CLONE_ON_COPY,
 +    complexity,
 +    "using `clone` on a `Copy` type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on a ref-counted pointer,
 +    /// (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified
 +    /// function syntax instead (e.g., `Rc::clone(foo)`).
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling '.clone()' on an Rc, Arc, or Weak
 +    /// can obscure the fact that only the pointer is being cloned, not the underlying
 +    /// data.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// let x = Rc::new(1);
 +    ///
 +    /// x.clone();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// # let x = Rc::new(1);
 +    /// Rc::clone(&x);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CLONE_ON_REF_PTR,
 +    restriction,
 +    "using 'clone' on a ref-counted pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on an `&&T`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Cloning an `&&T` copies the inner `&T`, instead of
 +    /// cloning the underlying `T`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn main() {
 +    ///     let x = vec![1];
 +    ///     let y = &&x;
 +    ///     let z = y.clone();
 +    ///     println!("{:p} {:p}", *y, z); // prints out the same pointer
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CLONE_DOUBLE_REF,
 +    correctness,
 +    "using `clone` on `&&T`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.to_string()` on an `&&T` where
 +    /// `T` implements `ToString` directly (like `&&str` or `&&String`).
 +    ///
 +    /// ### Why is this bad?
 +    /// This bypasses the specialized implementation of
 +    /// `ToString` and instead goes through the more expensive string formatting
 +    /// facilities.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Generic implementation for `T: Display` is used (slow)
 +    /// ["foo", "bar"].iter().map(|s| s.to_string());
 +    ///
 +    /// // OK, the specialized impl is used
 +    /// ["foo", "bar"].iter().map(|&s| s.to_string());
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub INEFFICIENT_TO_STRING,
 +    pedantic,
 +    "using `to_string` on `&&T` where `T: ToString`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `new` not returning a type that contains `Self`.
 +    ///
 +    /// ### Why is this bad?
 +    /// As a convention, `new` methods are used to make a new
 +    /// instance of a type.
 +    ///
 +    /// ### Example
 +    /// In an impl block:
 +    /// ```rust
 +    /// # struct Foo;
 +    /// # struct NotAFoo;
 +    /// impl Foo {
 +    ///     fn new() -> NotAFoo {
 +    /// # NotAFoo
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # struct Foo;
 +    /// struct Bar(Foo);
 +    /// impl Foo {
 +    ///     // Bad. The type name must contain `Self`
 +    ///     fn new() -> Bar {
 +    /// # Bar(Foo)
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # struct Foo;
 +    /// # struct FooError;
 +    /// impl Foo {
 +    ///     // Good. Return type contains `Self`
 +    ///     fn new() -> Result<Foo, FooError> {
 +    /// # Ok(Foo)
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Or in a trait definition:
 +    /// ```rust
 +    /// pub trait Trait {
 +    ///     // Bad. The type name must contain `Self`
 +    ///     fn new();
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// pub trait Trait {
 +    ///     // Good. Return type contains `Self`
 +    ///     fn new() -> Self;
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEW_RET_NO_SELF,
 +    style,
 +    "not returning type containing `Self` in a `new` method"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for string methods that receive a single-character
 +    /// `str` as an argument, e.g., `_.split("x")`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Performing these methods using a `char` is faster than
 +    /// using a `str`.
 +    ///
 +    /// ### Known problems
 +    /// Does not catch multi-byte unicode characters.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// _.split("x");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// _.split('x');
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SINGLE_CHAR_PATTERN,
 +    perf,
 +    "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calling `.step_by(0)` on iterators which panics.
 +    ///
 +    /// ### Why is this bad?
 +    /// This very much looks like an oversight. Use `panic!()` instead if you
 +    /// actually intend to panic.
 +    ///
 +    /// ### Example
 +    /// ```rust,should_panic
 +    /// for x in (0..100).step_by(0) {
 +    ///     //..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITERATOR_STEP_BY_ZERO,
 +    correctness,
 +    "using `Iterator::step_by(0)`, which will panic at runtime"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for indirect collection of populated `Option`
 +    ///
 +    /// ### Why is this bad?
 +    /// `Option` is like a collection of 0-1 things, so `flatten`
 +    /// automatically does this without suspicious-looking `unwrap` calls.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = std::iter::empty::<Option<i32>>().filter(Option::is_some).map(Option::unwrap);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let _ = std::iter::empty::<Option<i32>>().flatten();
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub OPTION_FILTER_MAP,
 +    complexity,
 +    "filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `iter.nth(0)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `iter.next()` is equivalent to
 +    /// `iter.nth(0)`, as they both consume the next element,
 +    ///  but is more readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// # let mut s = HashSet::new();
 +    /// # s.insert(1);
 +    /// let x = s.iter().nth(0);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// # let mut s = HashSet::new();
 +    /// # s.insert(1);
 +    /// let x = s.iter().next();
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub ITER_NTH_ZERO,
 +    style,
 +    "replace `iter.nth(0)` with `iter.next()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.iter().nth()` (and the related
 +    /// `.iter_mut().nth()`) on standard library types with *O*(1) element access.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.get()` and `.get_mut()` are more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().nth(3);
 +    /// let bad_slice = &some_vec[..].iter().nth(3);
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.get(3);
 +    /// let bad_slice = &some_vec[..].get(3);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITER_NTH,
 +    perf,
 +    "using `.iter().nth()` on a standard library type with O(1) element access"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.skip(x).next()` on iterators.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.nth(x)` is cleaner
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().skip(3).next();
 +    /// let bad_slice = &some_vec[..].iter().skip(3).next();
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().nth(3);
 +    /// let bad_slice = &some_vec[..].iter().nth(3);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITER_SKIP_NEXT,
 +    style,
 +    "using `.skip(x).next()` on an iterator"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.drain(..)` on `Vec` and `VecDeque` for iteration.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.into_iter()` is simpler with better performance.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// let mut foo = vec![0, 1, 2, 3];
 +    /// let bar: HashSet<usize> = foo.drain(..).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// let foo = vec![0, 1, 2, 3];
 +    /// let bar: HashSet<usize> = foo.into_iter().collect();
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub ITER_WITH_DRAIN,
 +    nursery,
 +    "replace `.drain(..)` with `.into_iter()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for using `x.get(x.len() - 1)` instead of
 +    /// `x.last()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `x.last()` is easier to read and has the same
 +    /// result.
 +    ///
 +    /// Note that using `x[x.len() - 1]` is semantically different from
 +    /// `x.last()`.  Indexing into the array will panic on out-of-bounds
 +    /// accesses, while `x.get()` and `x.last()` will return `None`.
 +    ///
 +    /// There is another lint (get_unwrap) that covers the case of using
 +    /// `x.get(index).unwrap()` instead of `x[index]`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = vec![2, 3, 5];
 +    /// let last_element = x.get(x.len() - 1);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = vec![2, 3, 5];
 +    /// let last_element = x.last();
 +    /// ```
 +    #[clippy::version = "1.37.0"]
 +    pub GET_LAST_WITH_LEN,
 +    complexity,
 +    "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.get().unwrap()` (or
 +    /// `.get_mut().unwrap`) on a standard library type which implements `Index`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the Index trait (`[]`) is more clear and more
 +    /// concise.
 +    ///
 +    /// ### Known problems
 +    /// Not a replacement for error handling: Using either
 +    /// `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`
 +    /// if the value being accessed is `None`. If the use of `.get().unwrap()` is a
 +    /// temporary placeholder for dealing with the `Option` type, then this does
 +    /// not mitigate the need for error handling. If there is a chance that `.get()`
 +    /// will be `None` in your program, then it is advisable that the `None` case
 +    /// is handled in a future refactor instead of using `.unwrap()` or the Index
 +    /// trait.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut some_vec = vec![0, 1, 2, 3];
 +    /// let last = some_vec.get(3).unwrap();
 +    /// *some_vec.get_mut(0).unwrap() = 1;
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let mut some_vec = vec![0, 1, 2, 3];
 +    /// let last = some_vec[3];
 +    /// some_vec[0] = 1;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub GET_UNWRAP,
 +    restriction,
 +    "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for occurrences where one vector gets extended instead of append
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `append` instead of `extend` is more concise and faster
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut a = vec![1, 2, 3];
 +    /// let mut b = vec![4, 5, 6];
 +    ///
 +    /// a.extend(b.drain(..));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let mut a = vec![1, 2, 3];
 +    /// let mut b = vec![4, 5, 6];
 +    ///
 +    /// a.append(&mut b);
 +    /// ```
 +    #[clippy::version = "1.55.0"]
 +    pub EXTEND_WITH_DRAIN,
 +    perf,
 +    "using vec.append(&mut vec) to move the full range of a vector to another"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.extend(s.chars())` where s is a
 +    /// `&str` or `String`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.push_str(s)` is clearer
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let abc = "abc";
 +    /// let def = String::from("def");
 +    /// let mut s = String::new();
 +    /// s.extend(abc.chars());
 +    /// s.extend(def.chars());
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let abc = "abc";
 +    /// let def = String::from("def");
 +    /// let mut s = String::new();
 +    /// s.push_str(abc);
 +    /// s.push_str(&def);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub STRING_EXTEND_CHARS,
 +    style,
 +    "using `x.extend(s.chars())` where s is a `&str` or `String`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.cloned().collect()` on slice to
 +    /// create a `Vec`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.to_vec()` is clearer
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let s = [1, 2, 3, 4, 5];
 +    /// let s2: Vec<isize> = s[..].iter().cloned().collect();
 +    /// ```
 +    /// The better use would be:
 +    /// ```rust
 +    /// let s = [1, 2, 3, 4, 5];
 +    /// let s2: Vec<isize> = s.to_vec();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITER_CLONED_COLLECT,
 +    style,
 +    "using `.cloned().collect()` on slice to create a `Vec`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.chars().last()` or
 +    /// `_.chars().next_back()` on a `str` to check if it ends with a given char.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.ends_with(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let name = "_";
 +    /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-');
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let name = "_";
 +    /// name.ends_with('_') || name.ends_with('-');
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CHARS_LAST_CMP,
 +    style,
 +    "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.as_ref()` or `.as_mut()` where the
 +    /// types before and after the call are the same.
 +    ///
 +    /// ### Why is this bad?
 +    /// The call is unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn do_stuff(x: &[i32]) {}
 +    /// let x: &[i32] = &[1, 2, 3, 4, 5];
 +    /// do_stuff(x.as_ref());
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// # fn do_stuff(x: &[i32]) {}
 +    /// let x: &[i32] = &[1, 2, 3, 4, 5];
 +    /// do_stuff(x);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USELESS_ASREF,
 +    complexity,
 +    "using `as_ref` where the types before and after the call are the same"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for using `fold` when a more succinct alternative exists.
 +    /// Specifically, this checks for `fold`s which could be replaced by `any`, `all`,
 +    /// `sum` or `product`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// (0..3).fold(false, |acc, x| acc || x > 2);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// (0..3).any(|x| x > 2);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNNECESSARY_FOLD,
 +    style,
 +    "using `fold` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `filter_map` calls that could be replaced by `filter` or `map`.
 +    /// More specifically it checks if the closure provided is only performing one of the
 +    /// filter or map operations and suggests the appropriate option.
 +    ///
 +    /// ### Why is this bad?
 +    /// Complexity. The intent is also clearer if only a single
 +    /// operation is being performed.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });
 +    ///
 +    /// // As there is no transformation of the argument this could be written as:
 +    /// let _ = (0..3).filter(|&x| x > 2);
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// let _ = (0..4).filter_map(|x| Some(x + 1));
 +    ///
 +    /// // As there is no conditional check on the argument this could be written as:
 +    /// let _ = (0..4).map(|x| x + 1);
 +    /// ```
 +    #[clippy::version = "1.31.0"]
 +    pub UNNECESSARY_FILTER_MAP,
 +    complexity,
 +    "using `filter_map` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `find_map` calls that could be replaced by `find` or `map`. More
 +    /// specifically it checks if the closure provided is only performing one of the
 +    /// find or map operations and suggests the appropriate option.
 +    ///
 +    /// ### Why is this bad?
 +    /// Complexity. The intent is also clearer if only a single
 +    /// operation is being performed.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).find_map(|x| if x > 2 { Some(x) } else { None });
 +    ///
 +    /// // As there is no transformation of the argument this could be written as:
 +    /// let _ = (0..3).find(|&x| x > 2);
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// let _ = (0..4).find_map(|x| Some(x + 1));
 +    ///
 +    /// // As there is no conditional check on the argument this could be written as:
 +    /// let _ = (0..4).map(|x| x + 1).next();
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub UNNECESSARY_FIND_MAP,
 +    complexity,
 +    "using `find_map` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `into_iter` calls on references which should be replaced by `iter`
 +    /// or `iter_mut`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. Calling `into_iter` on a reference will not move out its
 +    /// content into the resulting iterator, which is confusing. It is better just call `iter` or
 +    /// `iter_mut` directly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![3, 4, 5];
 +    /// (&vec).into_iter();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![3, 4, 5];
 +    /// (&vec).iter();
 +    /// ```
 +    #[clippy::version = "1.32.0"]
 +    pub INTO_ITER_ON_REF,
 +    style,
 +    "using `.into_iter()` on a reference"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `map` followed by a `count`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It looks suspicious. Maybe `map` was confused with `filter`.
 +    /// If the `map` call is intentional, this should be rewritten
 +    /// using `inspect`. Or, if you intend to drive the iterator to
 +    /// completion, you can just use `for_each` instead.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).map(|x| x + 2).count();
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub SUSPICIOUS_MAP,
 +    suspicious,
 +    "suspicious usage of map"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `MaybeUninit::uninit().assume_init()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// For most types, this is undefined behavior.
 +    ///
 +    /// ### Known problems
 +    /// For now, we accept empty tuples and tuples / arrays
 +    /// of `MaybeUninit`. There may be other types that allow uninitialized
 +    /// data, but those are not yet rigorously defined.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Beware the UB
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
 +    /// ```
 +    ///
 +    /// Note that the following is OK:
 +    ///
 +    /// ```rust
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: [MaybeUninit<bool>; 5] = unsafe {
 +    ///     MaybeUninit::uninit().assume_init()
 +    /// };
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub UNINIT_ASSUMED_INIT,
 +    correctness,
 +    "`MaybeUninit::uninit().assume_init()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be written simply with `saturating_add/sub` methods.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.checked_add(y).unwrap_or(u32::MAX);
 +    /// let sub = x.checked_sub(y).unwrap_or(u32::MIN);
 +    /// ```
 +    ///
 +    /// can be written using dedicated methods for saturating addition/subtraction as:
 +    ///
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.saturating_add(y);
 +    /// let sub = x.saturating_sub(y);
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub MANUAL_SATURATING_ARITHMETIC,
 +    style,
 +    "`.checked_add/sub(x).unwrap_or(MAX/MIN)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
 +    /// zero-sized types
 +    ///
 +    /// ### Why is this bad?
 +    /// This is a no-op, and likely unintended
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe { (&() as *const ()).offset(1) };
 +    /// ```
 +    #[clippy::version = "1.41.0"]
 +    pub ZST_OFFSET,
 +    correctness,
 +    "Check for offset calculations on raw pointers to zero-sized types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `FileType::is_file()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// When people testing a file type with `FileType::is_file`
 +    /// they are testing whether a path is something they can get bytes from. But
 +    /// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover
 +    /// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if filetype.is_file() {
 +    ///     // read file
 +    /// }
 +    /// # Ok::<_, std::io::Error>(())
 +    /// # };
 +    /// ```
 +    ///
 +    /// should be written as:
 +    ///
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if !filetype.is_dir() {
 +    ///     // read file
 +    /// }
 +    /// # Ok::<_, std::io::Error>(())
 +    /// # };
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub FILETYPE_IS_FILE,
 +    restriction,
 +    "`FileType::is_file` is not recommended to test for readable file type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.as_deref()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some("".to_string());
 +    /// opt.as_ref().map(String::as_str)
 +    /// # ;
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// # let opt = Some("".to_string());
 +    /// opt.as_deref()
 +    /// # ;
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub OPTION_AS_REF_DEREF,
 +    complexity,
 +    "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `iter().next()` on a Slice or an Array
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be shortened into `.get()`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let a = [1, 2, 3];
 +    /// # let b = vec![1, 2, 3];
 +    /// a[2..].iter().next();
 +    /// b.iter().next();
 +    /// ```
 +    /// should be written as:
 +    /// ```rust
 +    /// # let a = [1, 2, 3];
 +    /// # let b = vec![1, 2, 3];
 +    /// a.get(2);
 +    /// b.get(0);
 +    /// ```
 +    #[clippy::version = "1.46.0"]
 +    pub ITER_NEXT_SLICE,
 +    style,
 +    "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns when using `push_str`/`insert_str` with a single-character string literal
 +    /// where `push`/`insert` with a `char` would work fine.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's less clear that we are pushing a single character.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let mut string = String::new();
 +    /// string.insert_str(0, "R");
 +    /// string.push_str("R");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let mut string = String::new();
 +    /// string.insert(0, 'R');
 +    /// string.push('R');
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub SINGLE_CHAR_ADD_STR,
 +    style,
 +    "`push_str()` or `insert_str()` used with a single-character string literal as parameter"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// As the counterpart to `or_fun_call`, this lint looks for unnecessary
 +    /// lazily evaluated closures on `Option` and `Result`.
 +    ///
 +    /// This lint suggests changing the following functions, when eager evaluation results in
 +    /// simpler code:
 +    ///  - `unwrap_or_else` to `unwrap_or`
 +    ///  - `and_then` to `and`
 +    ///  - `or_else` to `or`
 +    ///  - `get_or_insert_with` to `get_or_insert`
 +    ///  - `ok_or_else` to `ok_or`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using eager evaluation is shorter and simpler in some cases.
 +    ///
 +    /// ### Known problems
 +    /// It is possible, but not recommended for `Deref` and `Index` to have
 +    /// side effects. Eagerly evaluating them can change the semantics of the program.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // example code where clippy issues a warning
 +    /// let opt: Option<u32> = None;
 +    ///
 +    /// opt.unwrap_or_else(|| 42);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let opt: Option<u32> = None;
 +    ///
 +    /// opt.unwrap_or(42);
 +    /// ```
 +    #[clippy::version = "1.48.0"]
 +    pub UNNECESSARY_LAZY_EVALUATIONS,
 +    style,
 +    "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map(_).collect::<Result<(), _>()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `try_for_each` instead is more readable and idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// (0..3).try_for_each(|t| Err(t));
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub MAP_COLLECT_RESULT_UNIT,
 +    style,
 +    "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `from_iter()` function calls on types that implement the `FromIterator`
 +    /// trait.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is recommended style to use collect. See
 +    /// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let five_fives = std::iter::repeat(5).take(5);
 +    ///
 +    /// let v = Vec::from_iter(five_fives);
 +    ///
 +    /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let five_fives = std::iter::repeat(5).take(5);
 +    ///
 +    /// let v: Vec<i32> = five_fives.collect();
 +    ///
 +    /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub FROM_ITER_INSTEAD_OF_COLLECT,
 +    pedantic,
 +    "use `.collect()` instead of `::from_iter()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `inspect().for_each()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is the same as performing the computation
 +    /// inside `inspect` at the beginning of the closure in `for_each`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// [1,2,3,4,5].iter()
 +    /// .inspect(|&x| println!("inspect the number: {}", x))
 +    /// .for_each(|&x| {
 +    ///     assert!(x >= 0);
 +    /// });
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// [1,2,3,4,5].iter()
 +    /// .for_each(|&x| {
 +    ///     println!("inspect the number: {}", x);
 +    ///     assert!(x >= 0);
 +    /// });
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub INSPECT_FOR_EACH,
 +    complexity,
 +    "using `.inspect().for_each()`, which can be replaced with `.for_each()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `filter_map(|x| x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely by using `flatten`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iter = vec![Some(1)].into_iter();
 +    /// iter.filter_map(|x| x);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let iter = vec![Some(1)].into_iter();
 +    /// iter.flatten();
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub FILTER_MAP_IDENTITY,
 +    complexity,
 +    "call to `filter_map` where `flatten` is sufficient"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for instances of `map(f)` where `f` is the identity function.
 +    ///
 +    /// ### Why is this bad?
 +    /// It can be written more concisely without the call to `map`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = [1, 2, 3];
 +    /// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = [1, 2, 3];
 +    /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub MAP_IDENTITY,
 +    complexity,
 +    "using iterator.map(|x| x)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.bytes().nth()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.as_bytes().get()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// "Hello".bytes().nth(3);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// "Hello".as_bytes().get(3);
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub BYTES_NTH,
 +    style,
 +    "replace `.bytes().nth()` with `.as_bytes().get()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.
 +    ///
 +    /// ### Why is this bad?
 +    /// These methods do the same thing as `_.clone()` but may be confusing as
 +    /// to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = vec![1, 2, 3];
 +    /// let b = a.to_vec();
 +    /// let c = a.to_owned();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = vec![1, 2, 3];
 +    /// let b = a.clone();
 +    /// let c = a.clone();
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub IMPLICIT_CLONE,
 +    pedantic,
 +    "implicitly cloning a value by invoking a function on its dereferenced type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.iter().count()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.len()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #![allow(unused)]
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    ///
 +    /// some_vec.iter().count();
 +    /// &some_vec[..].iter().count();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    ///
 +    /// some_vec.len();
 +    /// &some_vec[..].len();
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub ITER_COUNT,
 +    complexity,
 +    "replace `.iter().count()` with `.len()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for 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
-     #[clippy::version = "1.61.0"]
++    /// Finds usages of [`char::is_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that
++    /// can be replaced with [`is_ascii_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or
++    /// [`is_ascii_hexdigit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit).
 +    ///
 +    /// ### Why is this bad?
 +    /// `is_digit(..)` is slower and requires specifying the radix.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let c: char = '6';
 +    /// c.is_digit(10);
 +    /// c.is_digit(16);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let c: char = '6';
 +    /// c.is_ascii_digit();
 +    /// c.is_ascii_hexdigit();
 +    /// ```
-     #[clippy::version = "1.61.0"]
++    #[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.62.0"]
 +    pub NO_EFFECT_REPLACE,
 +    suspicious,
 +    "replace with no effect"
 +}
 +
 +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,
 +    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_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,
 +]);
 +
 +/// Extracts a method call name, args, and `Span` of the method name.
 +fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx str, &'tcx [hir::Expr<'tcx>], Span)> {
 +    if let ExprKind::MethodCall(path, args, _) = recv.kind {
 +        if !args.iter().any(|e| e.span.from_expansion()) {
 +            let name = path.ident.name.as_str();
 +            return Some((name, args, path.ident.span));
 +        }
 +    }
 +    None
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for Methods {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        self.check_methods(cx, expr);
 +
 +        match expr.kind {
 +            hir::ExprKind::Call(func, args) => {
 +                from_iter_instead_of_collect::check(cx, expr, args, func);
 +            },
 +            hir::ExprKind::MethodCall(method_call, args, _) => {
 +                let method_span = method_call.ident.span;
 +                or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), args);
 +                expect_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), args);
 +                clone_on_copy::check(cx, expr, method_call.ident.name, args);
 +                clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args);
 +                inefficient_to_string::check(cx, expr, method_call.ident.name, args);
 +                single_char_add_str::check(cx, expr, args);
 +                into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args);
 +                single_char_pattern::check(cx, expr, method_call.ident.name, args);
 +                unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv);
 +            },
 +            hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
 +                let mut info = BinaryExprInfo {
 +                    expr,
 +                    chain: lhs,
 +                    other: rhs,
 +                    eq: op.node == hir::BinOpKind::Eq,
 +                };
 +                lint_binary_expr_with_method_call(cx, &mut info);
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    #[allow(clippy::too_many_lines)]
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
 +        if in_external_macro(cx.sess(), impl_item.span) {
 +            return;
 +        }
 +        let name = impl_item.ident.name.as_str();
 +        let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id());
 +        let item = cx.tcx.hir().expect_item(parent);
 +        let self_ty = cx.tcx.type_of(item.def_id);
 +
 +        let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }));
 +        if_chain! {
 +            if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind;
 +            if let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next();
 +
 +            let method_sig = cx.tcx.fn_sig(impl_item.def_id);
 +            let method_sig = cx.tcx.erase_late_bound_regions(method_sig);
 +
 +            let first_arg_ty = method_sig.inputs().iter().next();
 +
 +            // check conventions w.r.t. conversion method names and predicates
 +            if let Some(first_arg_ty) = first_arg_ty;
 +
 +            then {
 +                // if this impl block implements a trait, lint in trait definition instead
 +                if !implements_trait && cx.access_levels.is_exported(impl_item.def_id) {
 +                    // check missing trait implementations
 +                    for method_config in &TRAIT_METHODS {
 +                        if name == method_config.method_name &&
 +                            sig.decl.inputs.len() == method_config.param_count &&
 +                            method_config.output_type.matches(&sig.decl.output) &&
 +                            method_config.self_kind.matches(cx, self_ty, *first_arg_ty) &&
 +                            fn_header_equals(method_config.fn_header, sig.header) &&
 +                            method_config.lifetime_param_cond(impl_item)
 +                        {
 +                            span_lint_and_help(
 +                                cx,
 +                                SHOULD_IMPLEMENT_TRAIT,
 +                                impl_item.span,
 +                                &format!(
 +                                    "method `{}` can be confused for the standard trait method `{}::{}`",
 +                                    method_config.method_name,
 +                                    method_config.trait_name,
 +                                    method_config.method_name
 +                                ),
 +                                None,
 +                                &format!(
 +                                    "consider implementing the trait `{}` or choosing a less ambiguous method name",
 +                                    method_config.trait_name
 +                                )
 +                            );
 +                        }
 +                    }
 +                }
 +
 +                if sig.decl.implicit_self.has_implicit_self()
 +                    && !(self.avoid_breaking_exported_api
 +                        && cx.access_levels.is_exported(impl_item.def_id))
 +                {
 +                    wrong_self_convention::check(
 +                        cx,
 +                        name,
 +                        self_ty,
 +                        *first_arg_ty,
 +                        first_arg.pat.span,
 +                        implements_trait,
 +                        false
 +                    );
 +                }
 +            }
 +        }
 +
 +        // if this impl block implements a trait, lint in trait definition instead
 +        if implements_trait {
 +            return;
 +        }
 +
 +        if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
 +            let ret_ty = return_ty(cx, impl_item.hir_id());
 +
 +            // walk the return type and check for Self (this does not check associated types)
 +            if let Some(self_adt) = self_ty.ty_adt_def() {
 +                if contains_adt_constructor(ret_ty, self_adt) {
 +                    return;
 +                }
 +            } else if contains_ty(ret_ty, self_ty) {
 +                return;
 +            }
 +
 +            // if return type is impl trait, check the associated types
 +            if let ty::Opaque(def_id, _) = *ret_ty.kind() {
 +                // one of the associated types must be Self
 +                for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
 +                    if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
 +                        let assoc_ty = match projection_predicate.term {
 +                            ty::Term::Ty(ty) => ty,
 +                            ty::Term::Const(_c) => continue,
 +                        };
 +                        // walk the associated type and check for Self
 +                        if let Some(self_adt) = self_ty.ty_adt_def() {
 +                            if contains_adt_constructor(assoc_ty, self_adt) {
 +                                return;
 +                            }
 +                        } else if contains_ty(assoc_ty, self_ty) {
 +                            return;
 +                        }
 +                    }
 +                }
 +            }
 +
 +            if name == "new" && ret_ty != self_ty {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    impl_item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if in_external_macro(cx.tcx.sess, item.span) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let TraitItemKind::Fn(ref sig, _) = item.kind;
 +            if sig.decl.implicit_self.has_implicit_self();
 +            if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
 +
 +            then {
 +                let first_arg_span = first_arg_ty.span;
 +                let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
 +                let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
 +                wrong_self_convention::check(
 +                    cx,
 +                    item.ident.name.as_str(),
 +                    self_ty,
 +                    first_arg_ty,
 +                    first_arg_span,
 +                    false,
 +                    true
 +                );
 +            }
 +        }
 +
 +        if_chain! {
 +            if item.ident.name == sym::new;
 +            if let TraitItemKind::Fn(_, _) = item.kind;
 +            let ret_ty = return_ty(cx, item.hir_id());
 +            let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
 +            if !contains_ty(ret_ty, self_ty);
 +
 +            then {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +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", []) => 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),
 +                    _ => {},
 +                },
 +                ("drain", [arg]) => {
 +                    iter_with_drain::check(cx, expr, recv, span, arg);
 +                },
 +                ("expect", [_]) => match method_call(recv) {
 +                    Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
 +                    Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
 +                    _ => expect_used::check(cx, expr, recv, 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_last_with_len::check(cx, expr, recv, arg),
 +                ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
 +                ("is_file", []) => filetype_is_file::check(cx, expr, recv),
 +                ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv),
 +                ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
 +                ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
 +                ("join", [join_arg]) => {
 +                    if let Some(("collect", _, span)) = method_call(recv) {
 +                        unnecessary_join::check(cx, expr, recv, join_arg, span);
 +                    }
 +                },
 +                ("last", []) | ("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);
 +                        }
 +                    }
 +                },
 +                (name @ ("map" | "map_err"), [m_arg]) => {
 +                    if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
 +                        match (name, args) {
 +                            ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, 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),
 +                ("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"),
 +                ("or_else", [arg]) => {
 +                    if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
 +                        unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
 +                    }
 +                },
 +                ("splitn" | "rsplitn", [count_arg, pat_arg]) => {
 +                    if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
 +                        suspicious_splitn::check(cx, name, expr, recv, count);
 +                        str_splitn::check(cx, name, expr, recv, pat_arg, count, 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_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
 +                    implicit_clone::check(cx, name, expr, recv);
 +                },
 +                ("unwrap", []) => {
 +                    match method_call(recv) {
 +                        Some(("get", [recv, get_arg], _)) => {
 +                            get_unwrap::check(cx, expr, recv, get_arg, false);
 +                        },
 +                        Some(("get_mut", [recv, get_arg], _)) => {
 +                            get_unwrap::check(cx, expr, recv, get_arg, true);
 +                        },
 +                        Some(("or", [recv, or_arg], or_span)) => {
 +                            or_then_unwrap::check(cx, expr, recv, or_arg, or_span);
 +                        },
 +                        _ => {},
 +                    }
 +                    unwrap_used::check(cx, expr, recv, 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);
 +                    },
 +                    _ => {},
 +                },
 +                ("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");
 +                    },
 +                },
 +                ("replace" | "replacen", [arg1, arg2] | [arg1, arg2, _]) => {
 +                    no_effect_replace::check(cx, expr, arg1, arg2);
 +                },
 +                _ => {},
 +            }
 +        }
 +    }
 +}
 +
 +fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
 +    if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call(recv) {
 +        search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
 +    }
 +}
 +
 +/// Used for `lint_binary_expr_with_method_call`.
 +#[derive(Copy, Clone)]
 +struct BinaryExprInfo<'a> {
 +    expr: &'a hir::Expr<'a>,
 +    chain: &'a hir::Expr<'a>,
 +    other: &'a hir::Expr<'a>,
 +    eq: bool,
 +}
 +
 +/// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
 +fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) {
 +    macro_rules! lint_with_both_lhs_and_rhs {
 +        ($func:expr, $cx:expr, $info:ident) => {
 +            if !$func($cx, $info) {
 +                ::std::mem::swap(&mut $info.chain, &mut $info.other);
 +                if $func($cx, $info) {
 +                    return;
 +                }
 +            }
 +        };
 +    }
 +
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info);
 +}
 +
 +const FN_HEADER: hir::FnHeader = hir::FnHeader {
 +    unsafety: hir::Unsafety::Normal,
 +    constness: hir::Constness::NotConst,
 +    asyncness: hir::IsAsync::NotAsync,
 +    abi: rustc_target::spec::abi::Abi::Rust,
 +};
 +
 +struct ShouldImplTraitCase {
 +    trait_name: &'static str,
 +    method_name: &'static str,
 +    param_count: usize,
 +    fn_header: hir::FnHeader,
 +    // implicit self kind expected (none, self, &self, ...)
 +    self_kind: SelfKind,
 +    // checks against the output type
 +    output_type: OutType,
 +    // certain methods with explicit lifetimes can't implement the equivalent trait method
 +    lint_explicit_lifetime: bool,
 +}
 +impl ShouldImplTraitCase {
 +    const fn new(
 +        trait_name: &'static str,
 +        method_name: &'static str,
 +        param_count: usize,
 +        fn_header: hir::FnHeader,
 +        self_kind: SelfKind,
 +        output_type: OutType,
 +        lint_explicit_lifetime: bool,
 +    ) -> ShouldImplTraitCase {
 +        ShouldImplTraitCase {
 +            trait_name,
 +            method_name,
 +            param_count,
 +            fn_header,
 +            self_kind,
 +            output_type,
 +            lint_explicit_lifetime,
 +        }
 +    }
 +
 +    fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool {
 +        self.lint_explicit_lifetime
 +            || !impl_item.generics.params.iter().any(|p| {
 +                matches!(
 +                    p.kind,
 +                    hir::GenericParamKind::Lifetime {
 +                        kind: hir::LifetimeParamKind::Explicit
 +                    }
 +                )
 +            })
 +    }
 +}
 +
 +#[rustfmt::skip]
 +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
 +    ShouldImplTraitCase::new("std::ops::Add", "add",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::convert::AsMut", "as_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::convert::AsRef", "as_ref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::BitAnd", "bitand",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitOr", "bitor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitXor", "bitxor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::borrow::Borrow", "borrow",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::clone::Clone", "clone",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::cmp::Ord", "cmp",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    // FIXME: default doesn't work
 +    ShouldImplTraitCase::new("std::default::Default", "default",  0,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Deref", "deref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::Div", "div",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Drop", "drop",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Unit, true),
 +    ShouldImplTraitCase::new("std::cmp::PartialEq", "eq",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Bool, true),
 +    ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::str::FromStr", "from_str",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::hash::Hash", "hash",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Unit, true),
 +    ShouldImplTraitCase::new("std::ops::Index", "index",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut",  2,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Mul", "mul",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Neg", "neg",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::iter::Iterator", "next",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Any, false),
 +    ShouldImplTraitCase::new("std::ops::Not", "not",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Rem", "rem",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Shl", "shl",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Shr", "shr",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Sub", "sub",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +];
 +
 +#[derive(Clone, Copy, PartialEq, Eq, Debug)]
 +enum SelfKind {
 +    Value,
 +    Ref,
 +    RefMut,
 +    No,
 +}
 +
 +impl SelfKind {
 +    fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +        fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +            if ty == parent_ty {
 +                true
 +            } else if ty.is_box() {
 +                ty.boxed_ty() == parent_ty
 +            } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
 +                if let ty::Adt(_, substs) = ty.kind() {
 +                    substs.types().next().map_or(false, |t| t == parent_ty)
 +                } else {
 +                    false
 +                }
 +            } else {
 +                false
 +            }
 +        }
 +
 +        fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +            if let ty::Ref(_, t, m) = *ty.kind() {
 +                return m == mutability && t == parent_ty;
 +            }
 +
 +            let trait_path = match mutability {
 +                hir::Mutability::Not => &paths::ASREF_TRAIT,
 +                hir::Mutability::Mut => &paths::ASMUT_TRAIT,
 +            };
 +
 +            let trait_def_id = match get_trait_def_id(cx, trait_path) {
 +                Some(did) => did,
 +                None => return false,
 +            };
 +            implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
 +        }
 +
 +        fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +            !matches_value(cx, parent_ty, ty)
 +                && !matches_ref(cx, hir::Mutability::Not, parent_ty, ty)
 +                && !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty)
 +        }
 +
 +        match self {
 +            Self::Value => matches_value(cx, parent_ty, ty),
 +            Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty),
 +            Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty),
 +            Self::No => matches_none(cx, parent_ty, ty),
 +        }
 +    }
 +
 +    #[must_use]
 +    fn description(self) -> &'static str {
 +        match self {
 +            Self::Value => "`self` by value",
 +            Self::Ref => "`self` by reference",
 +            Self::RefMut => "`self` by mutable reference",
 +            Self::No => "no `self`",
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +enum OutType {
 +    Unit,
 +    Bool,
 +    Any,
 +    Ref,
 +}
 +
 +impl OutType {
 +    fn matches(self, ty: &hir::FnRetTy<'_>) -> bool {
 +        let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[]));
 +        match (self, ty) {
 +            (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true,
 +            (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true,
 +            (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true,
 +            (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true,
 +            (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)),
 +            _ => false,
 +        }
 +    }
 +}
 +
 +fn is_bool(ty: &hir::Ty<'_>) -> bool {
 +    if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
 +        matches!(path.res, Res::PrimTy(PrimTy::Bool))
 +    } else {
 +        false
 +    }
 +}
 +
 +fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
 +    expected.constness == actual.constness
 +        && expected.unsafety == actual.unsafety
 +        && expected.asyncness == actual.asyncness
 +}
index 3d1208824fa34c10e187f873322b462746105104,0000000000000000000000000000000000000000..6af134019a472956958a698163b7837c6a5b9ea3
mode 100644,000000..100644
--- /dev/null
@@@ -1,185 -1,0 +1,175 @@@
- use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_macro_callsite};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::eager_or_lazy::switch_to_lazy_eval;
-         self_expr: &hir::Expr<'_>,
++use clippy_utils::source::{snippet, snippet_with_macro_callsite};
 +use clippy_utils::ty::{implements_trait, match_type};
 +use clippy_utils::{contains_return, is_trait_item, last_path_segment, paths};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::{kw, sym};
 +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,
 +    args: &'tcx [hir::Expr<'_>],
 +) {
 +    /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
 +    #[allow(clippy::too_many_arguments)]
 +    fn check_unwrap_or_default(
 +        cx: &LateContext<'_>,
 +        name: &str,
 +        fun: &hir::Expr<'_>,
-                 let mut applicability = Applicability::MachineApplicable;
-                 let hint = "unwrap_or_default()";
-                 let sugg_span = span;
-                 let sugg: String = format!(
-                     "{}.{}",
-                     snippet_with_applicability(cx, self_expr.span, "..", &mut applicability),
-                     hint
-                 );
 +        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 name == "unwrap_or";
 +            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 {
-                     sugg_span,
 +                span_lint_and_sugg(
 +                    cx,
 +                    OR_FUN_CALL,
-                     sugg,
-                     applicability,
++                    method_span.with_hi(span.hi()),
 +                    &format!("use of `{}` followed by a call to `{}`", name, path),
 +                    "try this",
-                 if !check_unwrap_or_default(cx, name, fun, self_arg, arg, or_has_args, expr.span) {
++                    "unwrap_or_default()".to_string(),
++                    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)
 +        static 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"),
 +        ];
 +
 +        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)) =
 +                KNOW_TYPES.iter().find(|&&i| match_type(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),
 +                                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 `{}` followed by a function call", name),
 +                    "try this",
 +                    format!("{}_{}({})", name, suffix, sugg),
 +                    Applicability::HasPlaceholders,
 +                );
 +            }
 +        }
 +    }
 +
 +    if let [self_arg, 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, self_arg, arg, expr.span, fun_span);
 +                }
 +            },
 +            hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
 +                check_general_case(cx, name, method_span, self_arg, arg, expr.span, None);
 +            },
 +            _ => (),
 +        }
 +    }
 +}
index 21767d74c87b8cb9ba2781ba6daaae980a4d4933,0000000000000000000000000000000000000000..1876c7fb9d0510fd855b52232bdfa38b6e6e7a65
mode 100644,000000..100644
--- /dev/null
@@@ -1,67 -1,0 +1,70 @@@
-     if is_option || is_result {
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{eager_or_lazy, usage};
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_span::sym;
 +
 +use super::UNNECESSARY_LAZY_EVALUATIONS;
 +
 +/// lint use of `<fn>_else(simple closure)` for `Option`s and `Result`s that can be
 +/// replaced with `<fn>(return value of simple closure)`
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx hir::Expr<'_>,
 +    recv: &'tcx hir::Expr<'_>,
 +    arg: &'tcx hir::Expr<'_>,
 +    simplify_using: &str,
 +) {
 +    let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
 +    let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
++    let is_bool = cx.typeck_results().expr_ty(recv).is_bool();
 +
-                 } else {
++    if is_option || is_result || is_bool {
 +        if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind {
 +            let body = cx.tcx.hir().body(body);
 +            let body_expr = &body.value;
 +
 +            if usage::BindingUsageFinder::are_params_used(cx, body) {
 +                return;
 +            }
 +
 +            if eager_or_lazy::switch_to_eager_eval(cx, body_expr) {
 +                let msg = if is_option {
 +                    "unnecessary closure used to substitute value for `Option::None`"
++                } else if is_result {
 +                    "unnecessary closure used to substitute value for `Result::Err`"
++                } else {
++                    "unnecessary closure used with `bool::then`"
 +                };
 +                let applicability = if body
 +                    .params
 +                    .iter()
 +                    // bindings are checked to be unused above
 +                    .all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild))
 +                {
 +                    Applicability::MachineApplicable
 +                } else {
 +                    // replacing the lambda may break type inference
 +                    Applicability::MaybeIncorrect
 +                };
 +
 +                // This is a duplicate of what's happening in clippy_lints::methods::method_call,
 +                // which isn't ideal, We want to get the method call span,
 +                // but prefer to avoid changing the signature of the function itself.
 +                if let hir::ExprKind::MethodCall(_, _, span) = expr.kind {
 +                    span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| {
 +                        diag.span_suggestion(
 +                            span,
 +                            &format!("use `{}(..)` instead", simplify_using),
 +                            format!("{}({})", simplify_using, snippet(cx, body_expr.span, "..")),
 +                            applicability,
 +                        );
 +                    });
 +                }
 +            }
 +        }
 +    }
 +}
index be7df08d89f0521de6f2bd871d415ded2e639c2d,0000000000000000000000000000000000000000..8224e80c9ccb36cb5f75ef2e025ddc7b9074e998
mode 100644,000000..100644
--- /dev/null
@@@ -1,342 -1,0 +1,342 @@@
-         matches!(data.kind, ExpnKind::Macro(MacroKind::Attr|MacroKind::Derive, _))
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then};
 +use clippy_utils::source::{snippet, snippet_opt};
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::FnKind;
 +use rustc_hir::{
 +    self as hir, def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt,
 +    StmtKind, TyKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::hygiene::DesugaringKind;
 +use rustc_span::source_map::{ExpnKind, Span};
 +
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::{get_parent_expr, in_constant, iter_input_pats, last_path_segment, SpanlessEq};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for function arguments and let bindings denoted as
 +    /// `ref`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `ref` declaration makes the function take an owned
 +    /// value, but turns the argument into a reference (which means that the value
 +    /// is destroyed when exiting the function). This adds not much value: either
 +    /// take a reference type, or take an owned value and create references in the
 +    /// body.
 +    ///
 +    /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The
 +    /// type of `x` is more obvious with the former.
 +    ///
 +    /// ### Known problems
 +    /// If the argument is dereferenced within the function,
 +    /// removing the `ref` will lead to errors. This can be fixed by removing the
 +    /// dereferences, e.g., changing `*x` to `x` within the function.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo(ref _x: u8) {}
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// fn foo(_x: &u8) {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TOPLEVEL_REF_ARG,
 +    style,
 +    "an entire binding declared as `ref`, in a function argument or a `let` statement"
 +}
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of bindings with a single leading
 +    /// underscore.
 +    ///
 +    /// ### Why is this bad?
 +    /// A single leading underscore is usually used to indicate
 +    /// that a binding will not be used. Using such a binding breaks this
 +    /// expectation.
 +    ///
 +    /// ### Known problems
 +    /// The lint does not work properly with desugaring and
 +    /// macro, it has been allowed in the mean time.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _x = 0;
 +    /// let y = _x + 1; // Here we are using `_x`, even though it has a leading
 +    ///                 // underscore. We should rename `_x` to `x`
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USED_UNDERSCORE_BINDING,
 +    pedantic,
 +    "using a binding which is prefixed with an underscore"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of short circuit boolean conditions as
 +    /// a
 +    /// statement.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using a short circuit boolean condition as a statement
 +    /// may hide the fact that the second part is executed or not depending on the
 +    /// outcome of the first part.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// f() && g(); // We should write `if f() { g(); }`.
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SHORT_CIRCUIT_STATEMENT,
 +    complexity,
 +    "using a short circuit boolean condition as a statement"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Catch casts from `0` to some pointer type
 +    ///
 +    /// ### Why is this bad?
 +    /// This generally means `null` and is better expressed as
 +    /// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = 0 as *const u32;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = std::ptr::null::<u32>();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ZERO_PTR,
 +    style,
 +    "using `0 as *{const, mut} T`"
 +}
 +
 +declare_lint_pass!(MiscLints => [
 +    TOPLEVEL_REF_ARG,
 +    USED_UNDERSCORE_BINDING,
 +    SHORT_CIRCUIT_STATEMENT,
 +    ZERO_PTR,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MiscLints {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        k: FnKind<'tcx>,
 +        decl: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        span: Span,
 +        _: HirId,
 +    ) {
 +        if let FnKind::Closure = k {
 +            // Does not apply to closures
 +            return;
 +        }
 +        if in_external_macro(cx.tcx.sess, span) {
 +            return;
 +        }
 +        for arg in iter_input_pats(decl, body) {
 +            if let PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) = arg.pat.kind {
 +                span_lint(
 +                    cx,
 +                    TOPLEVEL_REF_ARG,
 +                    arg.pat.span,
 +                    "`ref` directly on a function argument is ignored. \
 +                    Consider using a reference type instead",
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
 +        if_chain! {
 +            if !in_external_macro(cx.tcx.sess, stmt.span);
 +            if let StmtKind::Local(local) = stmt.kind;
 +            if let PatKind::Binding(an, .., name, None) = local.pat.kind;
 +            if let Some(init) = local.init;
 +            if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut;
 +            then {
 +                // use the macro callsite when the init span (but not the whole local span)
 +                // comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];`
 +                let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() {
 +                    Sugg::hir_with_macro_callsite(cx, init, "..")
 +                } else {
 +                    Sugg::hir(cx, init, "..")
 +                };
 +                let (mutopt, initref) = if an == BindingAnnotation::RefMut {
 +                    ("mut ", sugg_init.mut_addr())
 +                } else {
 +                    ("", sugg_init.addr())
 +                };
 +                let tyopt = if let Some(ty) = local.ty {
 +                    format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, ".."))
 +                } else {
 +                    String::new()
 +                };
 +                span_lint_hir_and_then(
 +                    cx,
 +                    TOPLEVEL_REF_ARG,
 +                    init.hir_id,
 +                    local.pat.span,
 +                    "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead",
 +                    |diag| {
 +                        diag.span_suggestion(
 +                            stmt.span,
 +                            "try",
 +                            format!(
 +                                "let {name}{tyopt} = {initref};",
 +                                name=snippet(cx, name.span, ".."),
 +                                tyopt=tyopt,
 +                                initref=initref,
 +                            ),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    }
 +                );
 +            }
 +        };
 +        if_chain! {
 +            if let StmtKind::Semi(expr) = stmt.kind;
 +            if let ExprKind::Binary(ref binop, a, b) = expr.kind;
 +            if binop.node == BinOpKind::And || binop.node == BinOpKind::Or;
 +            if let Some(sugg) = Sugg::hir_opt(cx, a);
 +            then {
 +                span_lint_hir_and_then(
 +                    cx,
 +                    SHORT_CIRCUIT_STATEMENT,
 +                    expr.hir_id,
 +                    stmt.span,
 +                    "boolean short circuit operator in statement may be clearer using an explicit test",
 +                    |diag| {
 +                        let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg };
 +                        diag.span_suggestion(
 +                            stmt.span,
 +                            "replace it with",
 +                            format!(
 +                                "if {} {{ {}; }}",
 +                                sugg,
 +                                &snippet(cx, b.span, ".."),
 +                            ),
 +                            Applicability::MachineApplicable, // snippet
 +                        );
 +                    });
 +            }
 +        };
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::Cast(e, ty) = expr.kind {
 +            check_cast(cx, expr.span, e, ty);
 +            return;
 +        }
 +        if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) {
 +            // Don't lint things expanded by #[derive(...)], etc or `await` desugaring
 +            return;
 +        }
 +        let sym;
 +        let binding = match expr.kind {
 +            ExprKind::Path(ref qpath) if !matches!(qpath, hir::QPath::LangItem(..)) => {
 +                let binding = last_path_segment(qpath).ident.as_str();
 +                if binding.starts_with('_') &&
 +                    !binding.starts_with("__") &&
 +                    binding != "_result" && // FIXME: #944
 +                    is_used(cx, expr) &&
 +                    // don't lint if the declaration is in a macro
 +                    non_macro_local(cx, cx.qpath_res(qpath, expr.hir_id))
 +                {
 +                    Some(binding)
 +                } else {
 +                    None
 +                }
 +            },
 +            ExprKind::Field(_, ident) => {
 +                sym = ident.name;
 +                let name = sym.as_str();
 +                if name.starts_with('_') && !name.starts_with("__") {
 +                    Some(name)
 +                } else {
 +                    None
 +                }
 +            },
 +            _ => None,
 +        };
 +        if let Some(binding) = binding {
 +            span_lint(
 +                cx,
 +                USED_UNDERSCORE_BINDING,
 +                expr.span,
 +                &format!(
 +                    "used binding `{}` which is prefixed with an underscore. A leading \
 +                     underscore signals that a binding will not be used",
 +                    binding
 +                ),
 +            );
 +        }
 +    }
 +}
 +
 +/// Heuristic to see if an expression is used. Should be compatible with
 +/// `unused_variables`'s idea
 +/// of what it means for an expression to be "used".
 +fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    get_parent_expr(cx, expr).map_or(true, |parent| match parent.kind {
 +        ExprKind::Assign(_, rhs, _) | ExprKind::AssignOp(_, _, rhs) => SpanlessEq::new(cx).eq_expr(rhs, expr),
 +        _ => is_used(cx, parent),
 +    })
 +}
 +
 +/// Tests whether an expression is in a macro expansion (e.g., something
 +/// generated by `#[derive(...)]` or the like).
 +fn in_attributes_expansion(expr: &Expr<'_>) -> bool {
 +    use rustc_span::hygiene::MacroKind;
 +    if expr.span.from_expansion() {
 +        let data = expr.span.ctxt().outer_expn_data();
++        matches!(data.kind, ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, _))
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Tests whether `res` is a variable defined outside a macro.
 +fn non_macro_local(cx: &LateContext<'_>, res: def::Res) -> bool {
 +    if let def::Res::Local(id) = res {
 +        !cx.tcx.hir().span(id).from_expansion()
 +    } else {
 +        false
 +    }
 +}
 +
 +fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) {
 +    if_chain! {
 +        if let TyKind::Ptr(ref mut_ty) = ty.kind;
 +        if let ExprKind::Lit(ref lit) = e.kind;
 +        if let LitKind::Int(0, _) = lit.node;
 +        if !in_constant(cx, e.hir_id);
 +        then {
 +            let (msg, sugg_fn) = match mut_ty.mutbl {
 +                Mutability::Mut => ("`0 as *mut _` detected", "std::ptr::null_mut"),
 +                Mutability::Not => ("`0 as *const _` detected", "std::ptr::null"),
 +            };
 +
 +            let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind {
 +                (format!("{}()", sugg_fn), Applicability::MachineApplicable)
 +            } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) {
 +                (format!("{}::<{}>()", sugg_fn, mut_ty_snip), Applicability::MachineApplicable)
 +            } else {
 +                // `MaybeIncorrect` as type inference may not work with the suggested code
 +                (format!("{}()", sugg_fn), Applicability::MaybeIncorrect)
 +            };
 +            span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl);
 +        }
 +    }
 +}
index d466d54a6ba5db92c5af6f2f210644cbb73c9b55,0000000000000000000000000000000000000000..254d9a70010a8e0ab10556f5b8ef322e46299205
mode 100644,000000..100644
--- /dev/null
@@@ -1,116 -1,0 +1,116 @@@
-     /// a type definition and impl block. Specifically, a paramater in an impl
 +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.
 +    ///
 +    /// ### 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.62.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 impl_params = generic_args.args.iter()
 +                .filter_map(|p|
 +                    match p {
 +                        GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) =>
 +                            Some((path.segments[0].ident.to_string(), path.span)),
 +                        _ => None,
 +                    }
 +                );
 +
 +                // find the type that the Impl is for
 +                // only lint on struct/enum/union for now
 +                let defid = match path.res {
 +                    Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) => defid,
 +                    _ => return,
 +                };
 +
 +                // get the names of the generic parameters in the type
 +                let type_params = &cx.tcx.generics_of(defid).params;
 +                let type_param_names: Vec<_> = type_params.iter()
 +                .filter_map(|p|
 +                    match p.kind {
 +                        GenericParamDefKind::Type {..} => Some(p.name.to_string()),
 +                        _ => None,
 +                    }
 +                ).collect();
 +                // hashmap of name -> index for mismatch_param_name
 +                let type_param_names_hashmap: FxHashMap<&String, usize> =
 +                    type_param_names.iter().enumerate().map(|(i, param)| (param, i)).collect();
 +
 +                let type_name = segment.ident;
 +                for (i, (impl_param_name, impl_param_span)) in impl_params.enumerate() {
 +                    if mismatch_param_name(i, &impl_param_name, &type_param_names_hashmap) {
 +                        let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order",
 +                                          type_name, impl_param_name);
 +                        let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params",
 +                                           type_param_names[i], type_name);
 +                        span_lint_and_help(
 +                            cx,
 +                            MISMATCHING_TYPE_PARAM_ORDER,
 +                            impl_param_span,
 +                            &msg,
 +                            None,
 +                            &help
 +                        );
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +// Checks if impl_param_name is the same as one of type_param_names,
 +// and is in a different position
 +fn mismatch_param_name(i: usize, impl_param_name: &String, type_param_names: &FxHashMap<&String, usize>) -> bool {
 +    if let Some(j) = type_param_names.get(impl_param_name) {
 +        if i != *j {
 +            return true;
 +        }
 +    }
 +    false
 +}
index 093ec389335db80d9a75e6ca6b566aad3c02fc16,0000000000000000000000000000000000000000..5c45ee6d94ad3c58ad33daeffc50ae747e5999de
mode 100644,000000..100644
--- /dev/null
@@@ -1,175 -1,0 +1,169 @@@
-                         if impl_item
-                             .generics
-                             .params
-                             .iter()
-                             .any(|gen| matches!(gen.kind, hir::GenericParamKind::Type { .. }))
-                         {
-                             // when the result of `new()` depends on a type parameter we should not require
-                             // an
-                             // impl of `Default`
 +use clippy_utils::diagnostics::span_lint_hir_and_then;
 +use clippy_utils::return_ty;
 +use clippy_utils::source::snippet;
 +use clippy_utils::sugg::DiagnosticExt;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::HirIdSet;
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for public types with a `pub fn new() -> Self` method and no
 +    /// implementation of
 +    /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html).
 +    ///
 +    /// ### Why is this bad?
 +    /// The user might expect to be able to use
 +    /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the
 +    /// type can be constructed without arguments.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// pub struct Foo(Bar);
 +    ///
 +    /// impl Foo {
 +    ///     pub fn new() -> Self {
 +    ///         Foo(Bar::new())
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// To fix the lint, add a `Default` implementation that delegates to `new`:
 +    ///
 +    /// ```ignore
 +    /// pub struct Foo(Bar);
 +    ///
 +    /// impl Default for Foo {
 +    ///     fn default() -> Self {
 +    ///         Foo::new()
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEW_WITHOUT_DEFAULT,
 +    style,
 +    "`pub fn new() -> Self` method without `Default` implementation"
 +}
 +
 +#[derive(Clone, Default)]
 +pub struct NewWithoutDefault {
 +    impling_types: Option<HirIdSet>,
 +}
 +
 +impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        if let hir::ItemKind::Impl(hir::Impl {
 +            of_trait: None,
 +            generics,
 +            self_ty: impl_self_ty,
 +            items,
 +            ..
 +        }) = item.kind
 +        {
 +            for assoc_item in *items {
 +                if assoc_item.kind == (hir::AssocItemKind::Fn { has_self: false }) {
 +                    let impl_item = cx.tcx.hir().impl_item(assoc_item.id);
 +                    if in_external_macro(cx.sess(), impl_item.span) {
 +                        return;
 +                    }
 +                    if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind {
 +                        let name = impl_item.ident.name;
 +                        let id = impl_item.hir_id();
 +                        if sig.header.constness == hir::Constness::Const {
 +                            // can't be implemented by default
 +                            return;
 +                        }
 +                        if sig.header.unsafety == hir::Unsafety::Unsafe {
 +                            // can't be implemented for unsafe new
 +                            return;
 +                        }
 +                        if cx.tcx.is_doc_hidden(impl_item.def_id) {
 +                            // shouldn't be implemented when it is hidden in docs
 +                            return;
 +                        }
++                        if !impl_item.generics.params.is_empty() {
++                            // when the result of `new()` depends on a parameter we should not require
++                            // an impl of `Default`
 +                            return;
 +                        }
 +                        if_chain! {
 +                            if sig.decl.inputs.is_empty();
 +                            if name == sym::new;
 +                            if cx.access_levels.is_reachable(impl_item.def_id);
 +                            let self_def_id = cx.tcx.hir().get_parent_item(id);
 +                            let self_ty = cx.tcx.type_of(self_def_id);
 +                            if self_ty == return_ty(cx, id);
 +                            if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default);
 +                            then {
 +                                if self.impling_types.is_none() {
 +                                    let mut impls = HirIdSet::default();
 +                                    cx.tcx.for_each_impl(default_trait_id, |d| {
 +                                        if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() {
 +                                            if let Some(local_def_id) = ty_def.did().as_local() {
 +                                                impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id));
 +                                            }
 +                                        }
 +                                    });
 +                                    self.impling_types = Some(impls);
 +                                }
 +
 +                                // Check if a Default implementation exists for the Self type, regardless of
 +                                // generics
 +                                if_chain! {
 +                                    if let Some(ref impling_types) = self.impling_types;
 +                                    if let Some(self_def) = cx.tcx.type_of(self_def_id).ty_adt_def();
 +                                    if let Some(self_local_did) = self_def.did().as_local();
 +                                    let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did);
 +                                    if impling_types.contains(&self_id);
 +                                    then {
 +                                        return;
 +                                    }
 +                                }
 +
 +                                let generics_sugg = snippet(cx, generics.span, "");
 +                                let self_ty_fmt = self_ty.to_string();
 +                                let self_type_snip = snippet(cx, impl_self_ty.span, &self_ty_fmt);
 +                                span_lint_hir_and_then(
 +                                    cx,
 +                                    NEW_WITHOUT_DEFAULT,
 +                                    id,
 +                                    impl_item.span,
 +                                    &format!(
 +                                        "you should consider adding a `Default` implementation for `{}`",
 +                                        self_type_snip
 +                                    ),
 +                                    |diag| {
 +                                        diag.suggest_prepend_item(
 +                                            cx,
 +                                            item.span,
 +                                            "try adding this",
 +                                            &create_new_without_default_suggest_msg(&self_type_snip, &generics_sugg),
 +                                            Applicability::MaybeIncorrect,
 +                                        );
 +                                    },
 +                                );
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn create_new_without_default_suggest_msg(self_type_snip: &str, generics_sugg: &str) -> String {
 +    #[rustfmt::skip]
 +    format!(
 +"impl{} Default for {} {{
 +    fn default() -> Self {{
 +        Self::new()
 +    }}
 +}}", generics_sugg, self_type_snip)
 +}
index 3ab4b6c4f6fab72c079a5b5d6ff28d112e164c2e,0000000000000000000000000000000000000000..6ad6837f0e3507afdb92fdcc0e2fcfbd3d6fe30d
mode 100644,000000..100644
--- /dev/null
@@@ -1,151 -1,0 +1,151 @@@
-     /// # Example
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use rustc_ast::ast::{Expr, ExprKind};
 +use rustc_ast::token::{Lit, LitKind};
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::Span;
 +use std::fmt::Write;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `\0` escapes in string and byte literals that look like octal
 +    /// character escapes in C.
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// C and other languages support octal character escapes in strings, where
 +    /// a backslash is followed by up to three octal digits. For example, `\033`
 +    /// stands for the ASCII character 27 (ESC). Rust does not support this
 +    /// notation, but has the escape code `\0` which stands for a null
 +    /// byte/character, and any following digits do not form part of the escape
 +    /// sequence. Therefore, `\033` is not a compiler error but the result may
 +    /// be surprising.
 +    ///
 +    /// ### Known problems
 +    /// The actual meaning can be the intended one. `\x00` can be used in these
 +    /// cases to be unambiguous.
 +    ///
 +    /// The lint does not trigger for format strings in `print!()`, `write!()`
 +    /// and friends since the string is already preprocessed when Clippy lints
 +    /// can see it.
 +    ///
++    /// ### Example
 +    /// ```rust
 +    /// let one = "\033[1m Bold? \033[0m";  // \033 intended as escape
 +    /// let two = "\033\0";                 // \033 intended as null-3-3
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let one = "\x1b[1mWill this be bold?\x1b[0m";
 +    /// let two = "\x0033\x00";
 +    /// ```
 +    #[clippy::version = "1.59.0"]
 +    pub OCTAL_ESCAPES,
 +    suspicious,
 +    "string escape sequences looking like octal characters"
 +}
 +
 +declare_lint_pass!(OctalEscapes => [OCTAL_ESCAPES]);
 +
 +impl EarlyLintPass for OctalEscapes {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
 +        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
 +
 +        if let ExprKind::Lit(lit) = &expr.kind {
 +            if matches!(lit.token.kind, LitKind::Str) {
 +                check_lit(cx, &lit.token, lit.span, true);
 +            } else if matches!(lit.token.kind, LitKind::ByteStr) {
 +                check_lit(cx, &lit.token, lit.span, false);
 +            }
 +        }
 +    }
 +}
 +
 +fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) {
 +    let contents = lit.symbol.as_str();
 +    let mut iter = contents.char_indices().peekable();
 +    let mut found = vec![];
 +
 +    // go through the string, looking for \0[0-7][0-7]?
 +    while let Some((from, ch)) = iter.next() {
 +        if ch == '\\' {
 +            if let Some((_, '0')) = iter.next() {
 +                // collect up to two further octal digits
 +                if let Some((mut to, '0'..='7')) = iter.next() {
 +                    if let Some((_, '0'..='7')) = iter.peek() {
 +                        to += 1;
 +                    }
 +                    found.push((from, to + 1));
 +                }
 +            }
 +        }
 +    }
 +
 +    if found.is_empty() {
 +        return;
 +    }
 +
 +    // construct two suggestion strings, one with \x escapes with octal meaning
 +    // as in C, and one with \x00 for null bytes.
 +    let mut suggest_1 = if is_string { "\"" } else { "b\"" }.to_string();
 +    let mut suggest_2 = suggest_1.clone();
 +    let mut index = 0;
 +    for (from, to) in found {
 +        suggest_1.push_str(&contents[index..from]);
 +        suggest_2.push_str(&contents[index..from]);
 +
 +        // construct a replacement escape
 +        // the maximum value is \077, or \x3f, so u8 is sufficient here
 +        if let Ok(n) = u8::from_str_radix(&contents[from + 1..to], 8) {
 +            write!(suggest_1, "\\x{:02x}", n).unwrap();
 +        }
 +
 +        // append the null byte as \x00 and the following digits literally
 +        suggest_2.push_str("\\x00");
 +        suggest_2.push_str(&contents[from + 2..to]);
 +
 +        index = to;
 +    }
 +    suggest_1.push_str(&contents[index..]);
 +    suggest_1.push('"');
 +    suggest_2.push_str(&contents[index..]);
 +    suggest_2.push('"');
 +
 +    span_lint_and_then(
 +        cx,
 +        OCTAL_ESCAPES,
 +        span,
 +        &format!(
 +            "octal-looking escape in {} literal",
 +            if is_string { "string" } else { "byte string" }
 +        ),
 +        |diag| {
 +            diag.help(&format!(
 +                "octal escapes are not supported, `\\0` is always a null {}",
 +                if is_string { "character" } else { "byte" }
 +            ));
 +            // suggestion 1: equivalent hex escape
 +            diag.span_suggestion(
 +                span,
 +                "if an octal escape was intended, use the hexadecimal representation instead",
 +                suggest_1,
 +                Applicability::MaybeIncorrect,
 +            );
 +            // suggestion 2: unambiguous null byte
 +            diag.span_suggestion(
 +                span,
 +                &format!(
 +                    "if the null {} is intended, disambiguate using",
 +                    if is_string { "character" } else { "byte" }
 +                ),
 +                suggest_2,
 +                Applicability::MaybeIncorrect,
 +            );
 +        },
 +    );
 +}
index d461668077e0a51f3f2c39b6db51b4bd7913d3d5,0000000000000000000000000000000000000000..413a740be25a529c0fe8fb64361dd38d567de634
mode 100644,000000..100644
--- /dev/null
@@@ -1,660 -1,0 +1,660 @@@
-     Arm, Closure, Block, Body, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path, PathSegment,
-     QPath, Stmt, StmtKind, TyKind, UnOp,
 +use std::collections::VecDeque;
 +
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::is_lint_allowed;
 +use itertools::{izip, Itertools};
 +use rustc_ast::{walk_list, Label, Mutability};
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_errors::Applicability;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
 +use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor};
 +use rustc_hir::{
++    Arm, Block, Body, Closure, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path,
++    PathSegment, QPath, Stmt, StmtKind, TyKind, UnOp,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_middle::ty::{Ty, TyCtxt, TypeckResults};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::kw;
 +use rustc_span::symbol::Ident;
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for arguments that are only used in recursion with no side-effects.
 +    ///
 +    /// ### Why is this bad?
 +    /// It could contain a useless calculation and can make function simpler.
 +    ///
 +    /// The arguments can be involved in calculations and assignments but as long as
 +    /// the calculations have no side-effects (function calls or mutating dereference)
 +    /// and the assigned variables are also only in recursion, it is useless.
 +    ///
 +    /// ### Known problems
 +    /// Too many code paths in the linting code are currently untested and prone to produce false
 +    /// positives or are prone to have performance implications.
 +    ///
 +    /// In some cases, this would not catch all useless arguments.
 +    ///
 +    /// ```rust
 +    /// fn foo(a: usize, b: usize) -> usize {
 +    ///     let f = |x| x + 1;
 +    ///
 +    ///     if a == 0 {
 +    ///         1
 +    ///     } else {
 +    ///         foo(a - 1, f(b))
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// For example, the argument `b` is only used in recursion, but the lint would not catch it.
 +    ///
 +    /// List of some examples that can not be caught:
 +    /// - binary operation of non-primitive types
 +    /// - closure usage
 +    /// - some `break` relative operations
 +    /// - struct pattern binding
 +    ///
 +    /// Also, when you recurse the function name with path segments, it is not possible to detect.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn f(a: usize, b: usize) -> usize {
 +    ///     if a == 0 {
 +    ///         1
 +    ///     } else {
 +    ///         f(a - 1, b + 1)
 +    ///     }
 +    /// }
 +    /// # fn main() {
 +    /// #     print!("{}", f(1, 1));
 +    /// # }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn f(a: usize) -> usize {
 +    ///     if a == 0 {
 +    ///         1
 +    ///     } else {
 +    ///         f(a - 1)
 +    ///     }
 +    /// }
 +    /// # fn main() {
 +    /// #     print!("{}", f(1));
 +    /// # }
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub ONLY_USED_IN_RECURSION,
 +    nursery,
 +    "arguments that is only used in recursion can be removed"
 +}
 +declare_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
 +
 +impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: FnKind<'tcx>,
 +        decl: &'tcx rustc_hir::FnDecl<'tcx>,
 +        body: &'tcx Body<'tcx>,
 +        _: Span,
 +        id: HirId,
 +    ) {
 +        if is_lint_allowed(cx, ONLY_USED_IN_RECURSION, id) {
 +            return;
 +        }
 +        if let FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) = kind {
 +            let def_id = id.owner.to_def_id();
 +            let data = cx.tcx.def_path(def_id).data;
 +
 +            if data.len() > 1 {
 +                match data.get(data.len() - 2) {
 +                    Some(DisambiguatedDefPathData {
 +                        data: DefPathData::Impl,
 +                        disambiguator,
 +                    }) if *disambiguator != 0 => return,
 +                    _ => {},
 +                }
 +            }
 +
 +            let has_self = !matches!(decl.implicit_self, ImplicitSelfKind::None);
 +
 +            let ty_res = cx.typeck_results();
 +            let param_span = body
 +                .params
 +                .iter()
 +                .flat_map(|param| {
 +                    let mut v = Vec::new();
 +                    param.pat.each_binding(|_, hir_id, span, ident| {
 +                        v.push((hir_id, span, ident));
 +                    });
 +                    v
 +                })
 +                .skip(if has_self { 1 } else { 0 })
 +                .filter(|(_, _, ident)| !ident.name.as_str().starts_with('_'))
 +                .collect_vec();
 +
 +            let params = body.params.iter().map(|param| param.pat).collect();
 +
 +            let mut visitor = SideEffectVisit {
 +                graph: FxHashMap::default(),
 +                has_side_effect: FxHashSet::default(),
 +                ret_vars: Vec::new(),
 +                contains_side_effect: false,
 +                break_vars: FxHashMap::default(),
 +                params,
 +                fn_ident: ident,
 +                fn_def_id: def_id,
 +                is_method: matches!(kind, FnKind::Method(..)),
 +                has_self,
 +                ty_res,
 +                tcx: cx.tcx,
 +                visited_exprs: FxHashSet::default(),
 +            };
 +
 +            visitor.visit_expr(&body.value);
 +            let vars = std::mem::take(&mut visitor.ret_vars);
 +            // this would set the return variables to side effect
 +            visitor.add_side_effect(vars);
 +
 +            let mut queue = visitor.has_side_effect.iter().copied().collect::<VecDeque<_>>();
 +
 +            // a simple BFS to check all the variables that have side effect
 +            while let Some(id) = queue.pop_front() {
 +                if let Some(next) = visitor.graph.get(&id) {
 +                    for i in next {
 +                        if !visitor.has_side_effect.contains(i) {
 +                            visitor.has_side_effect.insert(*i);
 +                            queue.push_back(*i);
 +                        }
 +                    }
 +                }
 +            }
 +
 +            for (id, span, ident) in param_span {
 +                // if the variable is not used in recursion, it would be marked as unused
 +                if !visitor.has_side_effect.contains(&id) {
 +                    let mut queue = VecDeque::new();
 +                    let mut visited = FxHashSet::default();
 +
 +                    queue.push_back(id);
 +
 +                    // a simple BFS to check the graph can reach to itself
 +                    // if it can't, it means the variable is never used in recursion
 +                    while let Some(id) = queue.pop_front() {
 +                        if let Some(next) = visitor.graph.get(&id) {
 +                            for i in next {
 +                                if !visited.contains(i) {
 +                                    visited.insert(id);
 +                                    queue.push_back(*i);
 +                                }
 +                            }
 +                        }
 +                    }
 +
 +                    if visited.contains(&id) {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            ONLY_USED_IN_RECURSION,
 +                            span,
 +                            "parameter is only used in recursion",
 +                            "if this is intentional, prefix with an underscore",
 +                            format!("_{}", ident.name.as_str()),
 +                            Applicability::MaybeIncorrect,
 +                        );
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +pub fn is_primitive(ty: Ty<'_>) -> bool {
 +    let ty = ty.peel_refs();
 +    ty.is_primitive() || ty.is_str()
 +}
 +
 +pub fn is_array(ty: Ty<'_>) -> bool {
 +    let ty = ty.peel_refs();
 +    ty.is_array() || ty.is_array_slice()
 +}
 +
 +/// This builds the graph of side effect.
 +/// The edge `a -> b` means if `a` has side effect, `b` will have side effect.
 +///
 +/// There are some example in following code:
 +/// ```rust, ignore
 +/// let b = 1;
 +/// let a = b; // a -> b
 +/// let (c, d) = (a, b); // c -> b, d -> b
 +///
 +/// let e = if a == 0 { // e -> a
 +///     c // e -> c
 +/// } else {
 +///     d // e -> d
 +/// };
 +/// ```
 +pub struct SideEffectVisit<'tcx> {
 +    graph: FxHashMap<HirId, FxHashSet<HirId>>,
 +    has_side_effect: FxHashSet<HirId>,
 +    // bool for if the variable was dereferenced from mutable reference
 +    ret_vars: Vec<(HirId, bool)>,
 +    contains_side_effect: bool,
 +    // break label
 +    break_vars: FxHashMap<Ident, Vec<(HirId, bool)>>,
 +    params: Vec<&'tcx Pat<'tcx>>,
 +    fn_ident: Ident,
 +    fn_def_id: DefId,
 +    is_method: bool,
 +    has_self: bool,
 +    ty_res: &'tcx TypeckResults<'tcx>,
 +    tcx: TyCtxt<'tcx>,
 +    visited_exprs: FxHashSet<HirId>,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
 +    fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) {
 +        match s.kind {
 +            StmtKind::Local(Local {
 +                pat, init: Some(init), ..
 +            }) => {
 +                self.visit_pat_expr(pat, init, false);
 +            },
 +            StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => {
 +                walk_stmt(self, s);
 +            },
 +            StmtKind::Local(_) => {},
 +        }
 +        self.ret_vars.clear();
 +    }
 +
 +    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
 +        if !self.visited_exprs.insert(ex.hir_id) {
 +            return;
 +        }
 +        match ex.kind {
 +            ExprKind::Array(exprs) | ExprKind::Tup(exprs) => {
 +                self.ret_vars = exprs
 +                    .iter()
 +                    .flat_map(|expr| {
 +                        self.visit_expr(expr);
 +                        std::mem::take(&mut self.ret_vars)
 +                    })
 +                    .collect();
 +            },
 +            ExprKind::Call(callee, args) => self.visit_fn(callee, args),
 +            ExprKind::MethodCall(path, args, _) => self.visit_method_call(path, args),
 +            ExprKind::Binary(_, lhs, rhs) => {
 +                self.visit_bin_op(lhs, rhs);
 +            },
 +            ExprKind::Unary(op, expr) => self.visit_un_op(op, expr),
 +            ExprKind::Let(Let { pat, init, .. }) => self.visit_pat_expr(pat, init, false),
 +            ExprKind::If(bind, then_expr, else_expr) => {
 +                self.visit_if(bind, then_expr, else_expr);
 +            },
 +            ExprKind::Match(expr, arms, _) => self.visit_match(expr, arms),
 +            // since analysing the closure is not easy, just set all variables in it to side-effect
 +            ExprKind::Closure(&Closure { body, .. }) => {
 +                let body = self.tcx.hir().body(body);
 +                self.visit_body(body);
 +                let vars = std::mem::take(&mut self.ret_vars);
 +                self.add_side_effect(vars);
 +            },
 +            ExprKind::Loop(block, label, _, _) | ExprKind::Block(block, label) => {
 +                self.visit_block_label(block, label);
 +            },
 +            ExprKind::Assign(bind, expr, _) => {
 +                self.visit_assign(bind, expr);
 +            },
 +            ExprKind::AssignOp(_, bind, expr) => {
 +                self.visit_assign(bind, expr);
 +                self.visit_bin_op(bind, expr);
 +            },
 +            ExprKind::Field(expr, _) => {
 +                self.visit_expr(expr);
 +                if matches!(self.ty_res.expr_ty(expr).kind(), ty::Ref(_, _, Mutability::Mut)) {
 +                    self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
 +                }
 +            },
 +            ExprKind::Index(expr, index) => {
 +                self.visit_expr(expr);
 +                let mut vars = std::mem::take(&mut self.ret_vars);
 +                self.visit_expr(index);
 +                self.ret_vars.append(&mut vars);
 +
 +                if !is_array(self.ty_res.expr_ty(expr)) {
 +                    self.add_side_effect(self.ret_vars.clone());
 +                } else if matches!(self.ty_res.expr_ty(expr).kind(), ty::Ref(_, _, Mutability::Mut)) {
 +                    self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
 +                }
 +            },
 +            ExprKind::Break(dest, Some(expr)) => {
 +                self.visit_expr(expr);
 +                if let Some(label) = dest.label {
 +                    self.break_vars
 +                        .entry(label.ident)
 +                        .or_insert(Vec::new())
 +                        .append(&mut self.ret_vars);
 +                }
 +                self.contains_side_effect = true;
 +            },
 +            ExprKind::Ret(Some(expr)) => {
 +                self.visit_expr(expr);
 +                let vars = std::mem::take(&mut self.ret_vars);
 +                self.add_side_effect(vars);
 +                self.contains_side_effect = true;
 +            },
 +            ExprKind::Break(_, None) | ExprKind::Continue(_) | ExprKind::Ret(None) => {
 +                self.contains_side_effect = true;
 +            },
 +            ExprKind::Struct(_, exprs, expr) => {
 +                let mut ret_vars = exprs
 +                    .iter()
 +                    .flat_map(|field| {
 +                        self.visit_expr(field.expr);
 +                        std::mem::take(&mut self.ret_vars)
 +                    })
 +                    .collect();
 +
 +                walk_list!(self, visit_expr, expr);
 +                self.ret_vars.append(&mut ret_vars);
 +            },
 +            _ => walk_expr(self, ex),
 +        }
 +    }
 +
 +    fn visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) {
 +        if let Res::Local(id) = path.res {
 +            self.ret_vars.push((id, false));
 +        }
 +    }
 +}
 +
 +impl<'tcx> SideEffectVisit<'tcx> {
 +    fn visit_assign(&mut self, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>) {
 +        // Just support array and tuple unwrapping for now.
 +        //
 +        // ex) `(a, b) = (c, d);`
 +        // The graph would look like this:
 +        //   a -> c
 +        //   b -> d
 +        //
 +        // This would minimize the connection of the side-effect graph.
 +        match (&lhs.kind, &rhs.kind) {
 +            (ExprKind::Array(lhs), ExprKind::Array(rhs)) | (ExprKind::Tup(lhs), ExprKind::Tup(rhs)) => {
 +                // if not, it is a compile error
 +                debug_assert!(lhs.len() == rhs.len());
 +                izip!(*lhs, *rhs).for_each(|(lhs, rhs)| self.visit_assign(lhs, rhs));
 +            },
 +            // in other assigns, we have to connect all each other
 +            // because they can be connected somehow
 +            _ => {
 +                self.visit_expr(lhs);
 +                let lhs_vars = std::mem::take(&mut self.ret_vars);
 +                self.visit_expr(rhs);
 +                let rhs_vars = std::mem::take(&mut self.ret_vars);
 +                self.connect_assign(&lhs_vars, &rhs_vars, false);
 +            },
 +        }
 +    }
 +
 +    fn visit_block_label(&mut self, block: &'tcx Block<'tcx>, label: Option<Label>) {
 +        self.visit_block(block);
 +        let _ = label.and_then(|label| {
 +            self.break_vars
 +                .remove(&label.ident)
 +                .map(|mut break_vars| self.ret_vars.append(&mut break_vars))
 +        });
 +    }
 +
 +    fn visit_bin_op(&mut self, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>) {
 +        self.visit_expr(lhs);
 +        let mut ret_vars = std::mem::take(&mut self.ret_vars);
 +        self.visit_expr(rhs);
 +        self.ret_vars.append(&mut ret_vars);
 +
 +        // the binary operation between non primitive values are overloaded operators
 +        // so they can have side-effects
 +        if !is_primitive(self.ty_res.expr_ty(lhs)) || !is_primitive(self.ty_res.expr_ty(rhs)) {
 +            self.ret_vars.iter().for_each(|id| {
 +                self.has_side_effect.insert(id.0);
 +            });
 +            self.contains_side_effect = true;
 +        }
 +    }
 +
 +    fn visit_un_op(&mut self, op: UnOp, expr: &'tcx Expr<'tcx>) {
 +        self.visit_expr(expr);
 +        let ty = self.ty_res.expr_ty(expr);
 +        // dereferencing a reference has no side-effect
 +        if !is_primitive(ty) && !matches!((op, ty.kind()), (UnOp::Deref, ty::Ref(..))) {
 +            self.add_side_effect(self.ret_vars.clone());
 +        }
 +
 +        if matches!((op, ty.kind()), (UnOp::Deref, ty::Ref(_, _, Mutability::Mut))) {
 +            self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
 +        }
 +    }
 +
 +    fn visit_pat_expr(&mut self, pat: &'tcx Pat<'tcx>, expr: &'tcx Expr<'tcx>, connect_self: bool) {
 +        match (&pat.kind, &expr.kind) {
 +            (PatKind::Tuple(pats, _), ExprKind::Tup(exprs)) => {
 +                self.ret_vars = izip!(*pats, *exprs)
 +                    .flat_map(|(pat, expr)| {
 +                        self.visit_pat_expr(pat, expr, connect_self);
 +                        std::mem::take(&mut self.ret_vars)
 +                    })
 +                    .collect();
 +            },
 +            (PatKind::Slice(front_exprs, _, back_exprs), ExprKind::Array(exprs)) => {
 +                let mut vars = izip!(*front_exprs, *exprs)
 +                    .flat_map(|(pat, expr)| {
 +                        self.visit_pat_expr(pat, expr, connect_self);
 +                        std::mem::take(&mut self.ret_vars)
 +                    })
 +                    .collect();
 +                self.ret_vars = izip!(back_exprs.iter().rev(), exprs.iter().rev())
 +                    .flat_map(|(pat, expr)| {
 +                        self.visit_pat_expr(pat, expr, connect_self);
 +                        std::mem::take(&mut self.ret_vars)
 +                    })
 +                    .collect();
 +                self.ret_vars.append(&mut vars);
 +            },
 +            _ => {
 +                let mut lhs_vars = Vec::new();
 +                pat.each_binding(|_, id, _, _| lhs_vars.push((id, false)));
 +                self.visit_expr(expr);
 +                let rhs_vars = std::mem::take(&mut self.ret_vars);
 +                self.connect_assign(&lhs_vars, &rhs_vars, connect_self);
 +                self.ret_vars = rhs_vars;
 +            },
 +        }
 +    }
 +
 +    fn visit_fn(&mut self, callee: &'tcx Expr<'tcx>, args: &'tcx [Expr<'tcx>]) {
 +        self.visit_expr(callee);
 +        let mut ret_vars = std::mem::take(&mut self.ret_vars);
 +        self.add_side_effect(ret_vars.clone());
 +
 +        let mut is_recursive = false;
 +
 +        if_chain! {
 +            if !self.has_self;
 +            if let ExprKind::Path(QPath::Resolved(_, path)) = callee.kind;
 +            if let Res::Def(DefKind::Fn, def_id) = path.res;
 +            if self.fn_def_id == def_id;
 +            then {
 +                is_recursive = true;
 +            }
 +        }
 +
 +        if_chain! {
 +            if !self.has_self && self.is_method;
 +            if let ExprKind::Path(QPath::TypeRelative(ty, segment)) = callee.kind;
 +            if segment.ident == self.fn_ident;
 +            if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
 +            if let Res::SelfTy{ .. } = path.res;
 +            then {
 +                is_recursive = true;
 +            }
 +        }
 +
 +        if is_recursive {
 +            izip!(self.params.clone(), args).for_each(|(pat, expr)| {
 +                self.visit_pat_expr(pat, expr, true);
 +                self.ret_vars.clear();
 +            });
 +        } else {
 +            // This would set arguments used in closure that does not have side-effect.
 +            // Closure itself can be detected whether there is a side-effect, but the
 +            // value of variable that is holding closure can change.
 +            // So, we just check the variables.
 +            self.ret_vars = args
 +                .iter()
 +                .flat_map(|expr| {
 +                    self.visit_expr(expr);
 +                    std::mem::take(&mut self.ret_vars)
 +                })
 +                .collect_vec()
 +                .into_iter()
 +                .map(|id| {
 +                    self.has_side_effect.insert(id.0);
 +                    id
 +                })
 +                .collect();
 +            self.contains_side_effect = true;
 +        }
 +
 +        self.ret_vars.append(&mut ret_vars);
 +    }
 +
 +    fn visit_method_call(&mut self, path: &'tcx PathSegment<'tcx>, args: &'tcx [Expr<'tcx>]) {
 +        if_chain! {
 +            if self.is_method;
 +            if path.ident == self.fn_ident;
 +            if let ExprKind::Path(QPath::Resolved(_, path)) = args.first().unwrap().kind;
 +            if let Res::Local(..) = path.res;
 +            let ident = path.segments.last().unwrap().ident;
 +            if ident.name == kw::SelfLower;
 +            then {
 +                izip!(self.params.clone(), args.iter())
 +                    .for_each(|(pat, expr)| {
 +                        self.visit_pat_expr(pat, expr, true);
 +                        self.ret_vars.clear();
 +                    });
 +            } else {
 +                self.ret_vars = args
 +                    .iter()
 +                    .flat_map(|expr| {
 +                        self.visit_expr(expr);
 +                        std::mem::take(&mut self.ret_vars)
 +                    })
 +                    .collect_vec()
 +                    .into_iter()
 +                    .map(|a| {
 +                        self.has_side_effect.insert(a.0);
 +                        a
 +                    })
 +                    .collect();
 +                self.contains_side_effect = true;
 +            }
 +        }
 +    }
 +
 +    fn visit_if(&mut self, bind: &'tcx Expr<'tcx>, then_expr: &'tcx Expr<'tcx>, else_expr: Option<&'tcx Expr<'tcx>>) {
 +        let contains_side_effect = self.contains_side_effect;
 +        self.contains_side_effect = false;
 +        self.visit_expr(bind);
 +        let mut vars = std::mem::take(&mut self.ret_vars);
 +        self.visit_expr(then_expr);
 +        let mut then_vars = std::mem::take(&mut self.ret_vars);
 +        walk_list!(self, visit_expr, else_expr);
 +        if self.contains_side_effect {
 +            self.add_side_effect(vars.clone());
 +        }
 +        self.contains_side_effect |= contains_side_effect;
 +        self.ret_vars.append(&mut vars);
 +        self.ret_vars.append(&mut then_vars);
 +    }
 +
 +    fn visit_match(&mut self, expr: &'tcx Expr<'tcx>, arms: &'tcx [Arm<'tcx>]) {
 +        self.visit_expr(expr);
 +        let mut expr_vars = std::mem::take(&mut self.ret_vars);
 +        self.ret_vars = arms
 +            .iter()
 +            .flat_map(|arm| {
 +                let contains_side_effect = self.contains_side_effect;
 +                self.contains_side_effect = false;
 +                // this would visit `expr` multiple times
 +                // but couldn't think of a better way
 +                self.visit_pat_expr(arm.pat, expr, false);
 +                let mut vars = std::mem::take(&mut self.ret_vars);
 +                let _ = arm.guard.as_ref().map(|guard| {
 +                    self.visit_expr(match guard {
 +                        Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => expr,
 +                    });
 +                    vars.append(&mut self.ret_vars);
 +                });
 +                self.visit_expr(arm.body);
 +                if self.contains_side_effect {
 +                    self.add_side_effect(vars.clone());
 +                    self.add_side_effect(expr_vars.clone());
 +                }
 +                self.contains_side_effect |= contains_side_effect;
 +                vars.append(&mut self.ret_vars);
 +                vars
 +            })
 +            .collect();
 +        self.ret_vars.append(&mut expr_vars);
 +    }
 +
 +    fn connect_assign(&mut self, lhs: &[(HirId, bool)], rhs: &[(HirId, bool)], connect_self: bool) {
 +        // if mutable dereference is on assignment it can have side-effect
 +        // (this can lead to parameter mutable dereference and change the original value)
 +        // too hard to detect whether this value is from parameter, so this would all
 +        // check mutable dereference assignment to side effect
 +        lhs.iter().filter(|(_, b)| *b).for_each(|(id, _)| {
 +            self.has_side_effect.insert(*id);
 +            self.contains_side_effect = true;
 +        });
 +
 +        // there is no connection
 +        if lhs.is_empty() || rhs.is_empty() {
 +            return;
 +        }
 +
 +        // by connected rhs in cycle, the connections would decrease
 +        // from `n * m` to `n + m`
 +        // where `n` and `m` are length of `lhs` and `rhs`.
 +
 +        // unwrap is possible since rhs is not empty
 +        let rhs_first = rhs.first().unwrap();
 +        for (id, _) in lhs.iter() {
 +            if connect_self || *id != rhs_first.0 {
 +                self.graph
 +                    .entry(*id)
 +                    .or_insert_with(FxHashSet::default)
 +                    .insert(rhs_first.0);
 +            }
 +        }
 +
 +        let rhs = rhs.iter();
 +        izip!(rhs.clone().cycle().skip(1), rhs).for_each(|(from, to)| {
 +            if connect_self || from.0 != to.0 {
 +                self.graph.entry(from.0).or_insert_with(FxHashSet::default).insert(to.0);
 +            }
 +        });
 +    }
 +
 +    fn add_side_effect(&mut self, v: Vec<(HirId, bool)>) {
 +        for (id, _) in v {
 +            self.has_side_effect.insert(id);
 +            self.contains_side_effect = true;
 +        }
 +    }
 +}
index ea5a8f0858b66aa14d084b55a585fa8ae7950be0,0000000000000000000000000000000000000000..44f153cffac511401f43c871690590fd3d29da43
mode 100644,000000..100644
--- /dev/null
@@@ -1,186 -1,0 +1,186 @@@
-                         .or_else(|| (method_sugg == "map_or_else").then(|| ()).and_then(|_| none_captures.get(local_id)))
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{
 +    can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_lang_ctor, peel_blocks,
 +    peel_hir_expr_while, CaptureKind,
 +};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::LangItem::OptionSome;
 +use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, Mutability, PatKind, Path, QPath, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more
 +    /// idiomatically done with `Option::map_or` (if the else bit is a pure
 +    /// expression) or `Option::map_or_else` (if the else bit is an impure
 +    /// expression).
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the dedicated functions of the `Option` type is clearer and
 +    /// more concise than an `if let` expression.
 +    ///
 +    /// ### Known problems
 +    /// This lint uses a deliberately conservative metric for checking
 +    /// if the inside of either body contains breaks or continues which will
 +    /// cause it to not suggest a fix if either block contains a loop with
 +    /// continues or breaks contained within the loop.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let optional: Option<u32> = Some(0);
 +    /// # fn do_complicated_function() -> u32 { 5 };
 +    /// let _ = if let Some(foo) = optional {
 +    ///     foo
 +    /// } else {
 +    ///     5
 +    /// };
 +    /// let _ = if let Some(foo) = optional {
 +    ///     foo
 +    /// } else {
 +    ///     let y = do_complicated_function();
 +    ///     y*y
 +    /// };
 +    /// ```
 +    ///
 +    /// should be
 +    ///
 +    /// ```rust
 +    /// # let optional: Option<u32> = Some(0);
 +    /// # fn do_complicated_function() -> u32 { 5 };
 +    /// let _ = optional.map_or(5, |foo| foo);
 +    /// let _ = optional.map_or_else(||{
 +    ///     let y = do_complicated_function();
 +    ///     y*y
 +    /// }, |foo| foo);
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub OPTION_IF_LET_ELSE,
 +    nursery,
 +    "reimplementation of Option::map_or"
 +}
 +
 +declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
 +
 +/// Returns true iff the given expression is the result of calling `Result::ok`
 +fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
 +    if let ExprKind::MethodCall(path, &[ref receiver], _) = &expr.kind {
 +        path.ident.name.as_str() == "ok"
 +            && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Result)
 +    } else {
 +        false
 +    }
 +}
 +
 +/// A struct containing information about occurrences of the
 +/// `if let Some(..) = .. else` construct that this lint detects.
 +struct OptionIfLetElseOccurrence {
 +    option: String,
 +    method_sugg: String,
 +    some_expr: String,
 +    none_expr: String,
 +}
 +
 +fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String {
 +    format!(
 +        "{}{}",
 +        Sugg::hir_with_macro_callsite(cx, cond_expr, "..").maybe_par(),
 +        if as_mut {
 +            ".as_mut()"
 +        } else if as_ref {
 +            ".as_ref()"
 +        } else {
 +            ""
 +        }
 +    )
 +}
 +
 +/// If this expression is the option if let/else construct we're detecting, then
 +/// this function returns an `OptionIfLetElseOccurrence` struct with details if
 +/// this construct is found, or None if this construct is not found.
 +fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurrence> {
 +    if_chain! {
 +        if !expr.span.from_expansion(); // Don't lint macros, because it behaves weirdly
 +        if !in_constant(cx, expr.hir_id);
 +        if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
 +            = higher::IfLet::hir(cx, expr);
 +        if !is_else_clause(cx.tcx, expr);
 +        if !is_result_ok(cx, let_expr); // Don't lint on Result::ok because a different lint does it already
 +        if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &let_pat.kind;
 +        if is_lang_ctor(cx, struct_qpath, OptionSome);
 +        if let PatKind::Binding(bind_annotation, _, id, None) = &inner_pat.kind;
 +        if let Some(some_captures) = can_move_expr_to_closure(cx, if_then);
 +        if let Some(none_captures) = can_move_expr_to_closure(cx, if_else);
 +        if some_captures
 +            .iter()
 +            .filter_map(|(id, &c)| none_captures.get(id).map(|&c2| (c, c2)))
 +            .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref());
 +
 +        then {
 +            let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
 +            let some_body = peel_blocks(if_then);
 +            let none_body = peel_blocks(if_else);
 +            let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { "map_or" } else { "map_or_else" };
 +            let capture_name = id.name.to_ident_string();
 +            let (as_ref, as_mut) = match &let_expr.kind {
 +                ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
 +                ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
 +                _ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut),
 +            };
 +            let cond_expr = match let_expr.kind {
 +                // Pointer dereferencing happens automatically, so we can omit it in the suggestion
 +                ExprKind::Unary(UnOp::Deref, expr) | ExprKind::AddrOf(_, _, expr) => expr,
 +                _ => let_expr,
 +            };
 +            // Check if captures the closure will need conflict with borrows made in the scrutinee.
 +            // TODO: check all the references made in the scrutinee expression. This will require interacting
 +            // with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
 +            if as_ref || as_mut {
 +                let e = peel_hir_expr_while(cond_expr, |e| match e.kind {
 +                    ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e),
 +                    _ => None,
 +                });
 +                if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(local_id), .. })) = e.kind {
 +                    match some_captures.get(local_id)
++                        .or_else(|| (method_sugg == "map_or_else").then_some(()).and_then(|_| none_captures.get(local_id)))
 +                    {
 +                        Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None,
 +                        Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None,
 +                        Some(CaptureKind::Ref(Mutability::Not)) | None => (),
 +                    }
 +                }
 +            }
 +            Some(OptionIfLetElseOccurrence {
 +                option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
 +                method_sugg: method_sugg.to_string(),
 +                some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")),
 +                none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")),
 +            })
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
 +        if let Some(detection) = detect_option_if_let_else(cx, expr) {
 +            span_lint_and_sugg(
 +                cx,
 +                OPTION_IF_LET_ELSE,
 +                expr.span,
 +                format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(),
 +                "try",
 +                format!(
 +                    "{}.{}({}, {})",
 +                    detection.option, detection.method_sugg, detection.none_expr, detection.some_expr,
 +                ),
 +                Applicability::MaybeIncorrect,
 +            );
 +        }
 +    }
 +}
index 8571607054a0070bed5709fe483b66b8008b3394,0000000000000000000000000000000000000000..8534d8a29f10df201bd146ded37be1afdb409dbe
mode 100644,000000..100644
--- /dev/null
@@@ -1,684 -1,0 +1,684 @@@
-             .map(|(_, mutability, span)| (mutability == Mutability::Not).then(|| span))
 +//! 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_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::sym;
 +use rustc_span::symbol::Symbol;
 +use std::fmt;
 +use std::iter;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for function arguments of type `&String`, `&Vec`,
 +    /// `&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls
 +    /// with the appropriate `.to_owned()`/`to_string()` calls.
 +    ///
 +    /// ### Why is this bad?
 +    /// Requiring the argument to be of the specific size
 +    /// makes the function less useful for no benefit; slices in the form of `&[T]`
 +    /// or `&str` usually suffice and can be obtained from other types, too.
 +    ///
 +    /// ### Known problems
 +    /// There may be `fn(&Vec)`-typed references pointing to your function.
 +    /// If you have them, you will get a compiler error after applying this lint's
 +    /// suggestions. You then have the choice to undo your changes or change the
 +    /// type of the reference.
 +    ///
 +    /// Note that if the function is part of your public interface, there may be
 +    /// other crates referencing it, of which you may not be aware. Carefully
 +    /// deprecate the function before applying the lint suggestions in this case.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// 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::Underscore => f.write_str("'_ ")?,
 +            LifetimeName::Static => f.write_str("'static ")?,
 +            _ => (),
 +        }
 +        f.write_str(self.mutability.prefix_str())
 +    }
 +}
 +
 +struct DerefTyDisplay<'a, 'tcx>(&'a LateContext<'tcx>, &'a DerefTy<'tcx>);
 +impl fmt::Display for DerefTyDisplay<'_, '_> {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        use std::fmt::Write;
 +        match self.1 {
 +            DerefTy::Str => f.write_str("str"),
 +            DerefTy::Path => f.write_str("Path"),
 +            DerefTy::Slice(hir_ty, ty) => {
 +                f.write_char('[')?;
 +                match hir_ty.and_then(|s| snippet_opt(self.0, s)) {
 +                    Some(s) => f.write_str(&s)?,
 +                    None => ty.fmt(f)?,
 +                }
 +                f.write_char(']')
 +            },
 +        }
 +    }
 +}
 +
 +enum DerefTy<'tcx> {
 +    Str,
 +    Path,
 +    Slice(Option<Span>, Ty<'tcx>),
 +}
 +impl<'tcx> DerefTy<'tcx> {
 +    fn argless_str(&self) -> &'static str {
 +        match *self {
 +            Self::Str => "str",
 +            Self::Path => "Path",
 +            Self::Slice(..) => "[_]",
 +        }
 +    }
 +
 +    fn display<'a>(&'a self, cx: &'a LateContext<'tcx>) -> DerefTyDisplay<'a, 'tcx> {
 +        DerefTyDisplay(cx, self)
 +    }
 +}
 +
 +fn check_fn_args<'cx, 'tcx: 'cx>(
 +    cx: &'cx LateContext<'tcx>,
 +    tys: &'tcx [Ty<'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!("&{}{}", mutability.prefix_str(), ty_name),
 +                                        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 args_idx = match path_to_local(e).and_then(|id| self.bindings.get(&id)) {
 +                Some(&i) => i,
 +                None => return walk_expr(self, e),
 +            };
 +            let args = &self.args[args_idx];
 +            let result = &mut self.results[args_idx];
 +
 +            // Helper function to handle early returns.
 +            let mut set_skip_flag = || {
 +                if !result.skip {
 +                    self.skip_count += 1;
 +                }
 +                result.skip = true;
 +            };
 +
 +            match get_expr_use_or_unification_node(self.cx.tcx, e) {
 +                Some((Node::Stmt(_), _)) => (),
 +                Some((Node::Local(l), _)) => {
 +                    // Only trace simple bindings. e.g `let x = y;`
 +                    if let PatKind::Binding(BindingAnnotation::Unannotated, id, _, None) = l.pat.kind {
 +                        self.bindings.insert(id, args_idx);
 +                    } else {
 +                        set_skip_flag();
 +                    }
 +                },
 +                Some((Node::Expr(e), child_id)) => match e.kind {
 +                    ExprKind::Call(f, expr_args) => {
 +                        let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
 +                        if expr_sig(self.cx, f).and_then(|sig| sig.input(i)).map_or(true, |ty| {
 +                            match *ty.skip_binder().peel_refs().kind() {
 +                                ty::Param(_) => true,
 +                                ty::Adt(def, _) => def.did() == args.ty_did,
 +                                _ => false,
 +                            }
 +                        }) {
 +                            // Passed to a function taking the non-dereferenced type.
 +                            set_skip_flag();
 +                        }
 +                    },
 +                    ExprKind::MethodCall(name, expr_args @ [self_arg, ..], _) => {
 +                        let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
 +                        if i == 0 {
 +                            // Check if the method can be renamed.
 +                            let name = name.ident.as_str();
 +                            if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
 +                                result.replacements.push(PtrArgReplacement {
 +                                    expr_span: e.span,
 +                                    self_span: self_arg.span,
 +                                    replacement,
 +                                });
 +                                return;
 +                            }
 +                        }
 +
 +                        let id = if let Some(x) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) {
 +                            x
 +                        } else {
 +                            set_skip_flag();
 +                            return;
 +                        };
 +
 +                        match *self.cx.tcx.fn_sig(id).skip_binder().inputs()[i].peel_refs().kind() {
 +                            ty::Param(_) => {
 +                                set_skip_flag();
 +                            },
 +                            // If the types match check for methods which exist on both types. e.g. `Vec::len` and
 +                            // `slice::len`
 +                            ty::Adt(def, _) if def.did() == args.ty_did => {
 +                                set_skip_flag();
 +                            },
 +                            _ => (),
 +                        }
 +                    },
 +                    // Indexing is fine for currently supported types.
 +                    ExprKind::Index(e, _) if e.hir_id == child_id => (),
 +                    _ => set_skip_flag(),
 +                },
 +                _ => set_skip_flag(),
 +            }
 +        }
 +    }
 +
 +    let mut skip_count = 0;
 +    let mut results = args.iter().map(|_| PtrArgResult::default()).collect::<Vec<_>>();
 +    let mut v = V {
 +        cx,
 +        bindings: args
 +            .iter()
 +            .enumerate()
 +            .filter_map(|(i, arg)| {
 +                let param = &body.params[arg.idx];
 +                match param.pat.kind {
 +                    PatKind::Binding(BindingAnnotation::Unannotated, id, _, None)
 +                        if !is_lint_allowed(cx, PTR_ARG, param.hir_id) =>
 +                    {
 +                        Some((id, i))
 +                    },
 +                    _ => {
 +                        skip_count += 1;
 +                        results[i].skip = true;
 +                        None
 +                    },
 +                }
 +            })
 +            .collect(),
 +        args,
 +        results,
 +        skip_count,
 +    };
 +    v.visit_expr(&body.value);
 +    v.results
 +}
 +
 +fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
 +    if let TyKind::Rptr(ref lt, ref m) = ty.kind {
 +        Some((lt, m.mutbl, ty.span))
 +    } else {
 +        None
 +    }
 +}
 +
 +fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Call(pathexp, []) = expr.kind {
 +        path_def_id(cx, pathexp).map_or(false, |id| {
 +            matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))
 +        })
 +    } else {
 +        false
 +    }
 +}
index 899568a933f7afe196b890a5f81e146aca01ec2a,0000000000000000000000000000000000000000..f0155ed6051f6b7c11408130d1b52d14e167076b
mode 100644,000000..100644
--- /dev/null
@@@ -1,173 -1,0 +1,231 @@@
- use clippy_utils::sugg::Sugg;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::source::snippet_with_applicability;
- use clippy_utils::{eq_expr_value, is_lang_ctor, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt};
 +use clippy_utils::ty::is_type_diagnostic_item;
- use rustc_hir::LangItem::{OptionNone, OptionSome, ResultOk};
- use rustc_hir::{BindingAnnotation, Expr, ExprKind, PatKind};
++use clippy_utils::{
++    eq_expr_value, get_parent_node, is_else_clause, is_lang_ctor, path_to_local, path_to_local_id, peel_blocks,
++    peel_blocks_with_stmt,
++};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
- use rustc_span::sym;
++use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
++use rustc_hir::{BindingAnnotation, Expr, ExprKind, Node, PatKind, PathSegment, QPath};
 +use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::Ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
- impl QuestionMark {
-     /// Checks if the given expression on the given context matches the following structure:
++use rustc_span::{sym, symbol::Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expressions that could be replaced by the question mark operator.
 +    ///
 +    /// ### Why is this bad?
 +    /// Question mark usage is more idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// if option.is_none() {
 +    ///     return None;
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```ignore
 +    /// option?;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub QUESTION_MARK,
 +    style,
 +    "checks for expressions that could be replaced by the question mark operator"
 +}
 +
 +declare_lint_pass!(QuestionMark => [QUESTION_MARK]);
 +
-     /// ```ignore
-     /// if option.is_none() {
-     ///    return None;
-     /// }
-     /// ```
-     ///
-     /// ```ignore
-     /// if result.is_err() {
-     ///     return result;
-     /// }
-     /// ```
++enum IfBlockType<'hir> {
++    /// An `if x.is_xxx() { a } else { b } ` expression.
 +    ///
-     /// If it matches, it will suggest to use the question mark operator instead
-     fn check_is_none_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) {
-         if_chain! {
-             if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
-             if let ExprKind::MethodCall(segment, args, _) = &cond.kind;
-             if let Some(subject) = args.get(0);
-             if (Self::option_check_and_early_return(cx, subject, then) && segment.ident.name == sym!(is_none)) ||
-                 (Self::result_check_and_early_return(cx, subject, then) && segment.ident.name == sym!(is_err));
-             then {
-                 let mut applicability = Applicability::MachineApplicable;
-                 let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability);
-                 let mut replacement: Option<String> = None;
-                 if let Some(else_inner) = r#else {
-                     if eq_expr_value(cx, subject, peel_blocks(else_inner)) {
-                         replacement = Some(format!("Some({}?)", receiver_str));
-                     }
-                 } else if Self::moves_by_default(cx, subject)
-                     && !matches!(subject.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
-                 {
-                     replacement = Some(format!("{}.as_ref()?;", receiver_str));
-                 } else {
-                     replacement = Some(format!("{}?;", receiver_str));
-                 }
++    /// Contains: caller (x), caller_type, call_sym (is_xxx), if_then (a), if_else (b)
++    IfIs(
++        &'hir Expr<'hir>,
++        Ty<'hir>,
++        Symbol,
++        &'hir Expr<'hir>,
++        Option<&'hir Expr<'hir>>,
++    ),
++    /// An `if let Xxx(a) = b { c } else { d }` expression.
 +    ///
-                 if let Some(replacement_str) = replacement {
-                     span_lint_and_sugg(
-                         cx,
-                         QUESTION_MARK,
-                         expr.span,
-                         "this block may be rewritten with the `?` operator",
-                         "replace it with",
-                         replacement_str,
-                         applicability,
-                     );
++    /// Contains: let_pat_qpath (Xxx), let_pat_type, let_pat_sym (a), let_expr (b), if_then (c),
++    /// if_else (d)
++    IfLet(
++        &'hir QPath<'hir>,
++        Ty<'hir>,
++        Symbol,
++        &'hir Expr<'hir>,
++        &'hir Expr<'hir>,
++        Option<&'hir Expr<'hir>>,
++    ),
++}
 +
-             }
++/// Checks if the given expression on the given context matches the following structure:
++///
++/// ```ignore
++/// if option.is_none() {
++///    return None;
++/// }
++/// ```
++///
++/// ```ignore
++/// if result.is_err() {
++///     return result;
++/// }
++/// ```
++///
++/// If it matches, it will suggest to use the question mark operator instead
++fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
++    if_chain! {
++        if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
++        if !is_else_clause(cx.tcx, expr);
++        if let ExprKind::MethodCall(segment, args, _) = &cond.kind;
++        if let Some(caller) = args.get(0);
++        let caller_ty = cx.typeck_results().expr_ty(caller);
++        let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then, r#else);
++        if is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block);
++        then {
++            let mut applicability = Applicability::MachineApplicable;
++            let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability);
++            let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx.at(caller.span), cx.param_env) &&
++                !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..));
++            let sugg = if let Some(else_inner) = r#else {
++                if eq_expr_value(cx, caller, peel_blocks(else_inner)) {
++                    format!("Some({}?)", receiver_str)
++                } else {
++                    return;
 +                }
-     fn check_if_let_some_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) {
-         if_chain! {
-             if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
-                 = higher::IfLet::hir(cx, expr);
-             if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind;
-             if (Self::option_check_and_early_return(cx, let_expr, if_else) && is_lang_ctor(cx, path1, OptionSome)) ||
-                 (Self::result_check_and_early_return(cx, let_expr, if_else) && is_lang_ctor(cx, path1, ResultOk));
-             if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind;
++            } else {
++                format!("{}{}?;", receiver_str, if by_ref { ".as_ref()" } else { "" })
++            };
++
++            span_lint_and_sugg(
++                cx,
++                QUESTION_MARK,
++                expr.span,
++                "this block may be rewritten with the `?` operator",
++                "replace it with",
++                sugg,
++                applicability,
++            );
 +        }
 +    }
++}
 +
-             if path_to_local_id(peel_blocks(if_then), bind_id);
-             then {
-                 let mut applicability = Applicability::MachineApplicable;
-                 let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
-                 let replacement = format!("{}{}?", receiver_str, if by_ref { ".as_ref()" } else { "" },);
-                 span_lint_and_sugg(
-                     cx,
-                     QUESTION_MARK,
-                     expr.span,
-                     "this if-let-else may be rewritten with the `?` operator",
-                     "replace it with",
-                     replacement,
-                     applicability,
-                 );
-             }
++fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
++    if_chain! {
++        if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr);
++        if !is_else_clause(cx.tcx, expr);
++        if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind;
++        if let PatKind::Binding(annot, bind_id, ident, _) = fields[0].kind;
++        let caller_ty = cx.typeck_results().expr_ty(let_expr);
++        let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else);
++        if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))
++            || is_early_return(sym::Result, cx, &if_block);
++        if if_else.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))).filter(|e| *e).is_none();
++        then {
++            let mut applicability = Applicability::MachineApplicable;
++            let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
 +            let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
-     fn result_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool {
-         Self::is_result(cx, expr) && Self::expression_returns_unmodified_err(nested_expr, expr)
-     }
-     fn option_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool {
-         Self::is_option(cx, expr) && Self::expression_returns_none(cx, nested_expr)
-     }
-     fn moves_by_default(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
-         let expr_ty = cx.typeck_results().expr_ty(expression);
-         !expr_ty.is_copy_modulo_regions(cx.tcx.at(expression.span), cx.param_env)
-     }
-     fn is_option(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
-         let expr_ty = cx.typeck_results().expr_ty(expression);
-         is_type_diagnostic_item(cx, expr_ty, sym::Option)
-     }
-     fn is_result(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
-         let expr_ty = cx.typeck_results().expr_ty(expression);
-         is_type_diagnostic_item(cx, expr_ty, sym::Result)
-     }
-     fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
-         match peel_blocks_with_stmt(expression).kind {
-             ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr),
-             ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
-             _ => false,
-         }
++            let requires_semi = matches!(get_parent_node(cx.tcx, expr.hir_id), Some(Node::Stmt(_)));
++            let sugg = format!(
++                "{}{}?{}",
++                receiver_str,
++                if by_ref { ".as_ref()" } else { "" },
++                if requires_semi { ";" } else { "" }
++            );
++            span_lint_and_sugg(
++                cx,
++                QUESTION_MARK,
++                expr.span,
++                "this block may be rewritten with the `?` operator",
++                "replace it with",
++                sugg,
++                applicability,
++            );
 +        }
 +    }
++}
 +
-     fn expression_returns_unmodified_err(expr: &Expr<'_>, cond_expr: &Expr<'_>) -> bool {
-         match peel_blocks_with_stmt(expr).kind {
-             ExprKind::Ret(Some(ret_expr)) => Self::expression_returns_unmodified_err(ret_expr, cond_expr),
-             ExprKind::Path(_) => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr),
++fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_>) -> bool {
++    match *if_block {
++        IfBlockType::IfIs(caller, caller_ty, call_sym, if_then, _) => {
++            // If the block could be identified as `if x.is_none()/is_err()`,
++            // we then only need to check the if_then return to see if it is none/err.
++            is_type_diagnostic_item(cx, caller_ty, smbl)
++                && expr_return_none_or_err(smbl, cx, if_then, caller, None)
++                && match smbl {
++                    sym::Option => call_sym == sym!(is_none),
++                    sym::Result => call_sym == sym!(is_err),
++                    _ => false,
++                }
++        },
++        IfBlockType::IfLet(qpath, let_expr_ty, let_pat_sym, let_expr, if_then, if_else) => {
++            is_type_diagnostic_item(cx, let_expr_ty, smbl)
++                && match smbl {
++                    sym::Option => {
++                        // We only need to check `if let Some(x) = option` not `if let None = option`,
++                        // because the later one will be suggested as `if option.is_none()` thus causing conflict.
++                        is_lang_ctor(cx, qpath, OptionSome)
++                            && if_else.is_some()
++                            && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, None)
++                    },
++                    sym::Result => {
++                        (is_lang_ctor(cx, qpath, ResultOk)
++                            && if_else.is_some()
++                            && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, Some(let_pat_sym)))
++                            || is_lang_ctor(cx, qpath, ResultErr)
++                                && expr_return_none_or_err(smbl, cx, if_then, let_expr, Some(let_pat_sym))
++                    },
++                    _ => false,
++                }
++        },
 +    }
++}
 +
-         }
++fn expr_return_none_or_err(
++    smbl: Symbol,
++    cx: &LateContext<'_>,
++    expr: &Expr<'_>,
++    cond_expr: &Expr<'_>,
++    err_sym: Option<Symbol>,
++) -> bool {
++    match peel_blocks_with_stmt(expr).kind {
++        ExprKind::Ret(Some(ret_expr)) => expr_return_none_or_err(smbl, cx, ret_expr, cond_expr, err_sym),
++        ExprKind::Path(ref qpath) => match smbl {
++            sym::Option => is_lang_ctor(cx, qpath, OptionNone),
++            sym::Result => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr),
 +            _ => false,
-         Self::check_is_none_or_err_and_early_return(cx, expr);
-         Self::check_if_let_some_or_err_and_early_return(cx, expr);
++        },
++        ExprKind::Call(call_expr, args_expr) => {
++            if_chain! {
++                if smbl == sym::Result;
++                if let ExprKind::Path(QPath::Resolved(_, path)) = &call_expr.kind;
++                if let Some(segment) = path.segments.first();
++                if let Some(err_sym) = err_sym;
++                if let Some(arg) = args_expr.first();
++                if let ExprKind::Path(QPath::Resolved(_, arg_path)) = &arg.kind;
++                if let Some(PathSegment { ident, .. }) = arg_path.segments.first();
++                then {
++                    return segment.ident.name == sym::Err && err_sym == ident.name;
++                }
++            }
++            false
++        },
++        _ => false,
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for QuestionMark {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
++        check_is_none_or_err_and_early_return(cx, expr);
++        check_if_let_some_or_err_and_early_return(cx, expr);
 +    }
 +}
index 67129299e2f9294292c1abd84b95a86d74bce721,0000000000000000000000000000000000000000..f9a9b0691935ad82cc3b817f5d276524a18efd41
mode 100644,000000..100644
--- /dev/null
@@@ -1,208 -1,0 +1,208 @@@
-     /// Regex::new("|")
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 +use clippy_utils::{match_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_ast::ast::{LitKind, StrStyle};
 +use rustc_hir::{BorrowKind, Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::{BytePos, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks [regex](https://crates.io/crates/regex) creation
 +    /// (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`) for correct
 +    /// regex syntax.
 +    ///
 +    /// ### Why is this bad?
 +    /// This will lead to a runtime panic.
 +    ///
 +    /// ### Example
 +    /// ```ignore
++    /// Regex::new("(")
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub INVALID_REGEX,
 +    correctness,
 +    "invalid regular expressions"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for trivial [regex](https://crates.io/crates/regex)
 +    /// creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`).
 +    ///
 +    /// ### Why is this bad?
 +    /// Matching the regex can likely be replaced by `==` or
 +    /// `str::starts_with`, `str::ends_with` or `std::contains` or other `str`
 +    /// methods.
 +    ///
 +    /// ### Known problems
 +    /// If the same regex is going to be applied to multiple
 +    /// inputs, the precomputations done by `Regex` construction can give
 +    /// significantly better performance than any of the `str`-based methods.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// Regex::new("^foobar")
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRIVIAL_REGEX,
 +    nursery,
 +    "trivial regular expressions"
 +}
 +
 +declare_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Regex {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::Call(fun, args) = expr.kind;
 +            if let ExprKind::Path(ref qpath) = fun.kind;
 +            if args.len() == 1;
 +            if let Some(def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
 +            then {
 +                if match_def_path(cx, def_id, &paths::REGEX_NEW) ||
 +                   match_def_path(cx, def_id, &paths::REGEX_BUILDER_NEW) {
 +                    check_regex(cx, &args[0], true);
 +                } else if match_def_path(cx, def_id, &paths::REGEX_BYTES_NEW) ||
 +                   match_def_path(cx, def_id, &paths::REGEX_BYTES_BUILDER_NEW) {
 +                    check_regex(cx, &args[0], false);
 +                } else if match_def_path(cx, def_id, &paths::REGEX_SET_NEW) {
 +                    check_set(cx, &args[0], true);
 +                } else if match_def_path(cx, def_id, &paths::REGEX_BYTES_SET_NEW) {
 +                    check_set(cx, &args[0], false);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#[must_use]
 +fn str_span(base: Span, c: regex_syntax::ast::Span, offset: u8) -> Span {
 +    let offset = u32::from(offset);
 +    let end = base.lo() + BytePos(u32::try_from(c.end.offset).expect("offset too large") + offset);
 +    let start = base.lo() + BytePos(u32::try_from(c.start.offset).expect("offset too large") + offset);
 +    assert!(start <= end);
 +    Span::new(start, end, base.ctxt(), base.parent())
 +}
 +
 +fn const_str<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<String> {
 +    constant(cx, cx.typeck_results(), e).and_then(|(c, _)| match c {
 +        Constant::Str(s) => Some(s),
 +        _ => None,
 +    })
 +}
 +
 +fn is_trivial_regex(s: &regex_syntax::hir::Hir) -> Option<&'static str> {
 +    use regex_syntax::hir::Anchor::{EndText, StartText};
 +    use regex_syntax::hir::HirKind::{Alternation, Anchor, Concat, Empty, Literal};
 +
 +    let is_literal = |e: &[regex_syntax::hir::Hir]| e.iter().all(|e| matches!(*e.kind(), Literal(_)));
 +
 +    match *s.kind() {
 +        Empty | Anchor(_) => Some("the regex is unlikely to be useful as it is"),
 +        Literal(_) => Some("consider using `str::contains`"),
 +        Alternation(ref exprs) => {
 +            if exprs.iter().all(|e| e.kind().is_empty()) {
 +                Some("the regex is unlikely to be useful as it is")
 +            } else {
 +                None
 +            }
 +        },
 +        Concat(ref exprs) => match (exprs[0].kind(), exprs[exprs.len() - 1].kind()) {
 +            (&Anchor(StartText), &Anchor(EndText)) if exprs[1..(exprs.len() - 1)].is_empty() => {
 +                Some("consider using `str::is_empty`")
 +            },
 +            (&Anchor(StartText), &Anchor(EndText)) if is_literal(&exprs[1..(exprs.len() - 1)]) => {
 +                Some("consider using `==` on `str`s")
 +            },
 +            (&Anchor(StartText), &Literal(_)) if is_literal(&exprs[1..]) => Some("consider using `str::starts_with`"),
 +            (&Literal(_), &Anchor(EndText)) if is_literal(&exprs[1..(exprs.len() - 1)]) => {
 +                Some("consider using `str::ends_with`")
 +            },
 +            _ if is_literal(exprs) => Some("consider using `str::contains`"),
 +            _ => None,
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
 +    if_chain! {
 +        if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind;
 +        if let ExprKind::Array(exprs) = expr.kind;
 +        then {
 +            for expr in exprs {
 +                check_regex(cx, expr, utf8);
 +            }
 +        }
 +    }
 +}
 +
 +fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
 +    let mut parser = regex_syntax::ParserBuilder::new()
 +        .unicode(true)
 +        .allow_invalid_utf8(!utf8)
 +        .build();
 +
 +    if let ExprKind::Lit(ref lit) = expr.kind {
 +        if let LitKind::Str(ref r, style) = lit.node {
 +            let r = r.as_str();
 +            let offset = if let StrStyle::Raw(n) = style { 2 + n } else { 1 };
 +            match parser.parse(r) {
 +                Ok(r) => {
 +                    if let Some(repl) = is_trivial_regex(&r) {
 +                        span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl);
 +                    }
 +                },
 +                Err(regex_syntax::Error::Parse(e)) => {
 +                    span_lint(
 +                        cx,
 +                        INVALID_REGEX,
 +                        str_span(expr.span, *e.span(), offset),
 +                        &format!("regex syntax error: {}", e.kind()),
 +                    );
 +                },
 +                Err(regex_syntax::Error::Translate(e)) => {
 +                    span_lint(
 +                        cx,
 +                        INVALID_REGEX,
 +                        str_span(expr.span, *e.span(), offset),
 +                        &format!("regex syntax error: {}", e.kind()),
 +                    );
 +                },
 +                Err(e) => {
 +                    span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {}", e));
 +                },
 +            }
 +        }
 +    } else if let Some(r) = const_str(cx, expr) {
 +        match parser.parse(&r) {
 +            Ok(r) => {
 +                if let Some(repl) = is_trivial_regex(&r) {
 +                    span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl);
 +                }
 +            },
 +            Err(regex_syntax::Error::Parse(e)) => {
 +                span_lint(
 +                    cx,
 +                    INVALID_REGEX,
 +                    expr.span,
 +                    &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()),
 +                );
 +            },
 +            Err(regex_syntax::Error::Translate(e)) => {
 +                span_lint(
 +                    cx,
 +                    INVALID_REGEX,
 +                    expr.span,
 +                    &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()),
 +                );
 +            },
 +            Err(e) => {
 +                span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {}", e));
 +            },
 +        }
 +    }
 +}
index bf318c055dad1d48ae38e37b2ceb70fad1a75b74,0000000000000000000000000000000000000000..5dcdab5b8ab90e9a35a57ca5735ff56825ed9aad
mode 100644,000000..100644
--- /dev/null
@@@ -1,254 -1,0 +1,252 @@@
-     bindings: Vec<FxHashMap<Symbol, Vec<ItemLocalId>>>,
 +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 {
-         let data = self.bindings.last_mut().unwrap();
++    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 (id, ident) = match pat.kind {
 +            PatKind::Binding(_, hir_id, ident, _) => (hir_id, ident),
 +            _ => 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
-             if is_shadow(cx, owner, prev, local_id) {
++        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 !matches!(
-             hir.body_owner_kind(hir.body_owner_def_id(body.id())),
-             BodyOwnerKind::Closure
-         ) {
-             self.bindings.push(FxHashMap::default());
++            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 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..56f2a7bae152b9a27a710b8fc936b9b2bca33b66
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,134 @@@
++use clippy_utils::diagnostics::span_lint_and_help;
++use rustc_hir::{def::Res, HirId, Path, PathSegment};
++use rustc_lint::{LateContext, LateLintPass, Lint};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::{sym, symbol::kw, Symbol};
++
++declare_clippy_lint! {
++    /// ### What it does
++    ///
++    /// Finds items imported through `std` when available through `core`.
++    ///
++    /// ### Why is this bad?
++    ///
++    /// Crates which have `no_std` compatibility may wish to ensure types are imported from core to ensure
++    /// disabling `std` does not cause the crate to fail to compile. This lint is also useful for crates
++    /// migrating to become `no_std` compatible.
++    ///
++    /// ### Example
++    /// ```rust
++    /// use std::hash::Hasher;
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// use core::hash::Hasher;
++    /// ```
++    #[clippy::version = "1.64.0"]
++    pub STD_INSTEAD_OF_CORE,
++    restriction,
++    "type is imported from std when available in core"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    ///
++    /// Finds items imported through `std` when available through `alloc`.
++    ///
++    /// ### Why is this bad?
++    ///
++    /// Crates which have `no_std` compatibility and require alloc may wish to ensure types are imported from
++    /// alloc to ensure disabling `std` does not cause the crate to fail to compile. This lint is also useful
++    /// for crates migrating to become `no_std` compatible.
++    ///
++    /// ### Example
++    /// ```rust
++    /// use std::vec::Vec;
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// # extern crate alloc;
++    /// use alloc::vec::Vec;
++    /// ```
++    #[clippy::version = "1.64.0"]
++    pub STD_INSTEAD_OF_ALLOC,
++    restriction,
++    "type is imported from std when available in alloc"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    ///
++    /// Finds items imported through `alloc` when available through `core`.
++    ///
++    /// ### Why is this bad?
++    ///
++    /// Crates which have `no_std` compatibility and may optionally require alloc may wish to ensure types are
++    /// imported from alloc to ensure disabling `alloc` does not cause the crate to fail to compile. This lint
++    /// is also useful for crates migrating to become `no_std` compatible.
++    ///
++    /// ### Example
++    /// ```rust
++    /// # extern crate alloc;
++    /// use alloc::slice::from_ref;
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// use core::slice::from_ref;
++    /// ```
++    #[clippy::version = "1.64.0"]
++    pub ALLOC_INSTEAD_OF_CORE,
++    restriction,
++    "type is imported from alloc when available in core"
++}
++
++declare_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]);
++
++impl<'tcx> LateLintPass<'tcx> for StdReexports {
++    fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) {
++        // std_instead_of_core
++        check_path(cx, path, sym::std, sym::core, STD_INSTEAD_OF_CORE);
++        // std_instead_of_alloc
++        check_path(cx, path, sym::std, sym::alloc, STD_INSTEAD_OF_ALLOC);
++        // alloc_instead_of_core
++        check_path(cx, path, sym::alloc, sym::core, ALLOC_INSTEAD_OF_CORE);
++    }
++}
++
++fn check_path(cx: &LateContext<'_>, path: &Path<'_>, krate: Symbol, suggested_crate: Symbol, lint: &'static Lint) {
++    if_chain! {
++        // check if path resolves to the suggested crate.
++        if let Res::Def(_, def_id) = path.res;
++        if suggested_crate == cx.tcx.crate_name(def_id.krate);
++
++        // check if the first segment of the path is the crate we want to identify
++        if let Some(path_root_segment) = get_first_segment(path);
++
++        // check if the path matches the crate we want to suggest the other path for.
++        if krate == path_root_segment.ident.name;
++        then {
++            span_lint_and_help(
++                cx,
++                lint,
++                path.span,
++                &format!("used import from `{}` instead of `{}`", krate, suggested_crate),
++                None,
++                &format!("consider importing the item from `{}`", suggested_crate),
++            );
++        }
++    }
++}
++
++/// Returns the first named segment of a [`Path`].
++///
++/// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`]
++/// is returned.
++fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
++    let segment = path.segments.first()?;
++
++    // A global path will have PathRoot as the first segment. In this case, return the segment after.
++    if segment.ident.name == kw::PathRoot {
++        path.segments.get(1)
++    } else {
++        Some(segment)
++    }
++}
index 75d3b040c968f9b3f47889d9a164203e4357a1b6,0000000000000000000000000000000000000000..3cbbda80f3a9fc6d11de47e6baea4481aea61c36
mode 100644,000000..100644
--- /dev/null
@@@ -1,80 -1,0 +1,80 @@@
-         (true, (borrowed_expr.span.ctxt() == ctxt || derefed_expr.span.ctxt() == ctxt).then(|| derefed_expr.span))
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet_with_context;
 +use clippy_utils::{match_def_path, path_def_id, paths};
 +use rustc_errors::Applicability;
 +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{Span, SyntaxContext};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `core::mem::swap` where either parameter is derived from a pointer
 +    ///
 +    /// ### Why is this bad?
 +    /// When at least one parameter to `swap` is derived from a pointer it may overlap with the
 +    /// other. This would then lead to undefined behavior.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) {
 +    ///     for (&x, &y) in x.iter().zip(y) {
 +    ///         core::mem::swap(&mut *x, &mut *y);
 +    ///     }
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) {
 +    ///     for (&x, &y) in x.iter().zip(y) {
 +    ///         core::ptr::swap(x, y);
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub SWAP_PTR_TO_REF,
 +    suspicious,
 +    "call to `mem::swap` using pointer derived references"
 +}
 +declare_lint_pass!(SwapPtrToRef => [SWAP_PTR_TO_REF]);
 +
 +impl LateLintPass<'_> for SwapPtrToRef {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
 +        if let ExprKind::Call(fn_expr, [arg1, arg2]) = e.kind
 +            && let Some(fn_id) = path_def_id(cx, fn_expr)
 +            && match_def_path(cx, fn_id, &paths::MEM_SWAP)
 +            && let ctxt = e.span.ctxt()
 +            && let (from_ptr1, arg1_span) = is_ptr_to_ref(cx, arg1, ctxt)
 +            && let (from_ptr2, arg2_span) = is_ptr_to_ref(cx, arg2, ctxt)
 +            && (from_ptr1 || from_ptr2)
 +        {
 +            span_lint_and_then(
 +                cx,
 +                SWAP_PTR_TO_REF,
 +                e.span,
 +                "call to `core::mem::swap` with a parameter derived from a raw pointer",
 +                |diag| {
 +                    if !((from_ptr1 && arg1_span.is_none()) || (from_ptr2 && arg2_span.is_none())) {
 +                        let mut app = Applicability::MachineApplicable;
 +                        let snip1 = snippet_with_context(cx, arg1_span.unwrap_or(arg1.span), ctxt, "..", &mut app).0;
 +                        let snip2 = snippet_with_context(cx, arg2_span.unwrap_or(arg2.span), ctxt, "..", &mut app).0;
 +                        diag.span_suggestion(e.span, "use ptr::swap", format!("core::ptr::swap({}, {})", snip1, snip2), app);
 +                    }
 +                }
 +            );
 +        }
 +    }
 +}
 +
 +/// Checks if the expression converts a mutable pointer to a mutable reference. If it is, also
 +/// returns the span of the pointer expression if it's suitable for making a suggestion.
 +fn is_ptr_to_ref(cx: &LateContext<'_>, e: &Expr<'_>, ctxt: SyntaxContext) -> (bool, Option<Span>) {
 +    if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, borrowed_expr) = e.kind
 +        && let ExprKind::Unary(UnOp::Deref, derefed_expr) = borrowed_expr.kind
 +        && cx.typeck_results().expr_ty(derefed_expr).is_unsafe_ptr()
 +    {
++        (true, (borrowed_expr.span.ctxt() == ctxt || derefed_expr.span.ctxt() == ctxt).then_some(derefed_expr.span))
 +    } else {
 +        (false, None)
 +    }
 +}
index ac63d1823371abdc03adb81822598b10a42086b5,0000000000000000000000000000000000000000..0a42a31fb8cf9e0a96dac08ede5a215e3e736880
mode 100644,000000..100644
--- /dev/null
@@@ -1,262 -1,0 +1,376 @@@
- use clippy_utils::diagnostics::span_lint_and_help;
- use clippy_utils::source::{snippet, snippet_with_applicability};
++use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
++use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 +use clippy_utils::{SpanlessEq, SpanlessHash};
 +use core::hash::{Hash, Hasher};
 +use if_chain::if_chain;
++use itertools::Itertools;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_data_structures::unhash::UnhashMap;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::Res;
 +use rustc_hir::{
-     GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, PredicateOrigin, QPath, TraitItem, Ty, TyKind,
-     WherePredicate,
++    GenericArg, GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, PredicateOrigin, QPath,
++    TraitBoundModifier, TraitItem, TraitRef, Ty, TyKind, WherePredicate,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
- use rustc_span::Span;
- use std::fmt::Write as _;
++use rustc_span::{BytePos, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns about unnecessary type repetitions in trait bounds
 +    ///
 +    /// ### Why is this bad?
 +    /// Repeating the type for every bound makes the code
 +    /// less readable than combining the bounds
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// pub fn foo<T>(t: T) where T: Copy, T: Clone {}
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// pub fn foo<T>(t: T) where T: Copy + Clone {}
 +    /// ```
 +    #[clippy::version = "1.38.0"]
 +    pub TYPE_REPETITION_IN_BOUNDS,
-     pedantic,
-     "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
++    nursery,
++    "types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for cases where generics are being used and multiple
 +    /// syntax specifications for trait bounds are used simultaneously.
 +    ///
 +    /// ### Why is this bad?
 +    /// Duplicate bounds makes the code
 +    /// less readable than specifying them only once.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn func<T: Clone + Default>(arg: T) where T: Clone + Default {}
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # mod hidden {
 +    /// fn func<T: Clone + Default>(arg: T) {}
 +    /// # }
 +    ///
 +    /// // or
 +    ///
 +    /// fn func<T>(arg: T) where T: Clone + Default {}
 +    /// ```
++    ///
++    /// ```rust
++    /// fn foo<T: Default + Default>(bar: T) {}
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// fn foo<T: Default>(bar: T) {}
++    /// ```
++    ///
++    /// ```rust
++    /// fn foo<T>(bar: T) where T: Default + Default {}
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// fn foo<T>(bar: T) where T: Default {}
++    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub TRAIT_DUPLICATION_IN_BOUNDS,
-     pedantic,
-     "Check if the same trait bounds are specified twice during a function declaration"
++    nursery,
++    "check if the same trait bounds are specified more than once during a generic declaration"
 +}
 +
 +#[derive(Copy, Clone)]
 +pub struct TraitBounds {
 +    max_trait_bounds: u64,
 +}
 +
 +impl TraitBounds {
 +    #[must_use]
 +    pub fn new(max_trait_bounds: u64) -> Self {
 +        Self { max_trait_bounds }
 +    }
 +}
 +
 +impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for TraitBounds {
 +    fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
 +        self.check_type_repetition(cx, gen);
 +        check_trait_bound_duplication(cx, gen);
++        check_bounds_or_where_duplication(cx, gen);
++    }
++
++    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
++        // special handling for self trait bounds as these are not considered generics
++        // ie. trait Foo: Display {}
++        if let Item {
++            kind: ItemKind::Trait(_, _, _, bounds, ..),
++            ..
++        } = item
++        {
++            rollup_traits(cx, bounds, "these bounds contain repeated elements");
++        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) {
 +        let mut self_bounds_map = FxHashMap::default();
 +
 +        for predicate in item.generics.predicates {
 +            if_chain! {
 +                if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
 +                if bound_predicate.origin != PredicateOrigin::ImplTrait;
 +                if !bound_predicate.span.from_expansion();
 +                if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
 +                if let Some(PathSegment {
 +                    res: Some(Res::SelfTy{ trait_: Some(def_id), alias_to: _ }), ..
 +                }) = segments.first();
 +                if let Some(
 +                    Node::Item(
 +                        Item {
 +                            kind: ItemKind::Trait(_, _, _, self_bounds, _),
 +                            .. }
 +                        )
 +                    ) = cx.tcx.hir().get_if_local(*def_id);
 +                then {
 +                    if self_bounds_map.is_empty() {
 +                        for bound in self_bounds.iter() {
 +                            let Some((self_res, self_segments, _)) = get_trait_info_from_bound(bound) else { continue };
 +                            self_bounds_map.insert(self_res, self_segments);
 +                        }
 +                    }
 +
 +                    bound_predicate
 +                        .bounds
 +                        .iter()
 +                        .filter_map(get_trait_info_from_bound)
 +                        .for_each(|(trait_item_res, trait_item_segments, span)| {
 +                            if let Some(self_segments) = self_bounds_map.get(&trait_item_res) {
 +                                if SpanlessEq::new(cx).eq_path_segments(self_segments, trait_item_segments) {
 +                                    span_lint_and_help(
 +                                        cx,
 +                                        TRAIT_DUPLICATION_IN_BOUNDS,
 +                                        span,
 +                                        "this trait bound is already specified in trait declaration",
 +                                        None,
 +                                        "consider removing this trait bound",
 +                                    );
 +                                }
 +                            }
 +                        });
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +impl TraitBounds {
 +    fn check_type_repetition<'tcx>(self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
 +        struct SpanlessTy<'cx, 'tcx> {
 +            ty: &'tcx Ty<'tcx>,
 +            cx: &'cx LateContext<'tcx>,
 +        }
 +        impl PartialEq for SpanlessTy<'_, '_> {
 +            fn eq(&self, other: &Self) -> bool {
 +                let mut eq = SpanlessEq::new(self.cx);
 +                eq.inter_expr().eq_ty(self.ty, other.ty)
 +            }
 +        }
 +        impl Hash for SpanlessTy<'_, '_> {
 +            fn hash<H: Hasher>(&self, h: &mut H) {
 +                let mut t = SpanlessHash::new(self.cx);
 +                t.hash_ty(self.ty);
 +                h.write_u64(t.finish());
 +            }
 +        }
 +        impl Eq for SpanlessTy<'_, '_> {}
 +
 +        if gen.span.from_expansion() {
 +            return;
 +        }
 +        let mut map: UnhashMap<SpanlessTy<'_, '_>, Vec<&GenericBound<'_>>> = UnhashMap::default();
 +        let mut applicability = Applicability::MaybeIncorrect;
 +        for bound in gen.predicates {
 +            if_chain! {
 +                if let WherePredicate::BoundPredicate(ref p) = bound;
 +                if p.origin != PredicateOrigin::ImplTrait;
 +                if p.bounds.len() as u64 <= self.max_trait_bounds;
 +                if !p.span.from_expansion();
 +                if let Some(ref v) = map.insert(
 +                    SpanlessTy { ty: p.bounded_ty, cx },
 +                    p.bounds.iter().collect::<Vec<_>>()
 +                );
 +
 +                then {
-                     let mut hint_string = format!(
-                         "consider combining the bounds: `{}:",
-                         snippet(cx, p.bounded_ty.span, "_")
++                    let trait_bounds = v
++                        .iter()
++                        .copied()
++                        .chain(p.bounds.iter())
++                        .filter_map(get_trait_info_from_bound)
++                        .map(|(_, _, span)| snippet_with_applicability(cx, span, "..", &mut applicability))
++                        .join(" + ");
++                    let hint_string = format!(
++                        "consider combining the bounds: `{}: {}`",
++                        snippet(cx, p.bounded_ty.span, "_"),
++                        trait_bounds,
 +                    );
-                     for b in v.iter() {
-                         if let GenericBound::Trait(ref poly_trait_ref, _) = b {
-                             let path = &poly_trait_ref.trait_ref.path;
-                             let _ = write!(hint_string,
-                                 " {} +",
-                                 snippet_with_applicability(cx, path.span, "..", &mut applicability)
-                             );
-                         }
-                     }
-                     for b in p.bounds.iter() {
-                         if let GenericBound::Trait(ref poly_trait_ref, _) = b {
-                             let path = &poly_trait_ref.trait_ref.path;
-                             let _ = write!(hint_string,
-                                 " {} +",
-                                 snippet_with_applicability(cx, path.span, "..", &mut applicability)
-                             );
-                         }
-                     }
-                     hint_string.truncate(hint_string.len() - 2);
-                     hint_string.push('`');
 +                    span_lint_and_help(
 +                        cx,
 +                        TYPE_REPETITION_IN_BOUNDS,
 +                        p.span,
 +                        "this type has already been used as a bound predicate",
 +                        None,
 +                        &hint_string,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
 +    if gen.span.from_expansion() || gen.params.is_empty() || gen.predicates.is_empty() {
 +        return;
 +    }
 +
 +    let mut map = FxHashMap::<_, Vec<_>>::default();
 +    for predicate in gen.predicates {
 +        if_chain! {
 +            if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
 +            if bound_predicate.origin != PredicateOrigin::ImplTrait;
 +            if !bound_predicate.span.from_expansion();
 +            if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
 +            if let Some(segment) = segments.first();
 +            then {
 +                for (res_where, _, span_where) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
 +                    let trait_resolutions_direct = map.entry(segment.ident).or_default();
 +                    if let Some((_, span_direct)) = trait_resolutions_direct
 +                                                .iter()
 +                                                .find(|(res_direct, _)| *res_direct == res_where) {
 +                        span_lint_and_help(
 +                            cx,
 +                            TRAIT_DUPLICATION_IN_BOUNDS,
 +                            *span_direct,
 +                            "this trait bound is already specified in the where clause",
 +                            None,
 +                            "consider removing this trait bound",
 +                        );
 +                    }
 +                    else {
 +                        trait_resolutions_direct.push((res_where, span_where));
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
++#[derive(PartialEq, Eq, Hash, Debug)]
++struct ComparableTraitRef(Res, Vec<Res>);
++
++fn check_bounds_or_where_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
++    if gen.span.from_expansion() {
++        return;
++    }
++
++    for predicate in gen.predicates {
++        if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate {
++            let msg = if predicate.in_where_clause() {
++                "these where clauses contain repeated elements"
++            } else {
++                "these bounds contain repeated elements"
++            };
++            rollup_traits(cx, bound_predicate.bounds, msg);
++        }
++    }
++}
++
 +fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'a [PathSegment<'a>], Span)> {
-     if let GenericBound::Trait(t, _) = bound {
-         Some((t.trait_ref.path.res, t.trait_ref.path.segments, t.span))
++    if let GenericBound::Trait(t, tbm) = bound {
++        let trait_path = t.trait_ref.path;
++        let trait_span = {
++            let path_span = trait_path.span;
++            if let TraitBoundModifier::Maybe = tbm {
++                path_span.with_lo(path_span.lo() - BytePos(1)) // include the `?`
++            } else {
++                path_span
++            }
++        };
++        Some((trait_path.res, trait_path.segments, trait_span))
 +    } else {
 +        None
 +    }
 +}
++
++// FIXME: ComparableTraitRef does not support nested bounds needed for associated_type_bounds
++fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef {
++    ComparableTraitRef(
++        trait_ref.path.res,
++        trait_ref
++            .path
++            .segments
++            .iter()
++            .filter_map(|segment| {
++                // get trait bound type arguments
++                Some(segment.args?.args.iter().filter_map(|arg| {
++                    if_chain! {
++                        if let GenericArg::Type(ty) = arg;
++                        if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
++                        then { return Some(path.res) }
++                    }
++                    None
++                }))
++            })
++            .flatten()
++            .collect(),
++    )
++}
++
++fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
++    let mut map = FxHashMap::default();
++    let mut repeated_res = false;
++
++    let only_comparable_trait_refs = |bound: &GenericBound<'_>| {
++        if let GenericBound::Trait(t, _) = bound {
++            Some((into_comparable_trait_ref(&t.trait_ref), t.span))
++        } else {
++            None
++        }
++    };
++
++    for bound in bounds.iter().filter_map(only_comparable_trait_refs) {
++        let (comparable_bound, span_direct) = bound;
++        if map.insert(comparable_bound, span_direct).is_some() {
++            repeated_res = true;
++        }
++    }
++
++    if_chain! {
++        if repeated_res;
++        if let [first_trait, .., last_trait] = bounds;
++        then {
++            let all_trait_span = first_trait.span().to(last_trait.span());
++
++            let mut traits = map.values()
++                .filter_map(|span| snippet_opt(cx, *span))
++                .collect::<Vec<_>>();
++            traits.sort_unstable();
++            let traits = traits.join(" + ");
++
++            span_lint_and_sugg(
++                cx,
++                TRAIT_DUPLICATION_IN_BOUNDS,
++                all_trait_span,
++                msg,
++                "try",
++                traits,
++                Applicability::MachineApplicable
++            );
++        }
++    }
++}
index 21a9558ec076a54e938fadc06f56fb6f9fa5ee34,0000000000000000000000000000000000000000..ba51404d214832976e245a61076d08bb7bfbceb8
mode 100644,000000..100644
--- /dev/null
@@@ -1,45 -1,0 +1,54 @@@
-                     "you seem to be trying to use `Box<{outer}{generic}>`. Consider using just `{outer}{generic}`",
-                     outer=item_type,
-                     generic = generic),
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::{path_def_id, qpath_generic_tys};
 +use rustc_hir::{self as hir, def_id::DefId, QPath};
 +use rustc_lint::LateContext;
 +use rustc_span::{sym, Symbol};
 +
 +use super::BOX_COLLECTION;
 +
 +pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
 +    if_chain! {
 +        if Some(def_id) == cx.tcx.lang_items().owned_box();
 +        if let Some(item_type) = get_std_collection(cx, qpath);
 +        then {
 +            let generic = match item_type {
 +                sym::String => "",
 +                _ => "<..>",
 +            };
++
++            let box_content = format!("{outer}{generic}", outer = item_type);
 +            span_lint_and_help(
 +                cx,
 +                BOX_COLLECTION,
 +                hir_ty.span,
 +                &format!(
-                     "`{outer}{generic}` is already on the heap, `Box<{outer}{generic}>` makes an extra allocation",
-                     outer=item_type,
-                     generic = generic)
++                    "you seem to be trying to use `Box<{box_content}>`. Consider using just `{box_content}`"),
 +                None,
 +                &format!(
-     cx.tcx
-         .get_diagnostic_name(id)
-         .filter(|&name| matches!(name, sym::HashMap | sym::String | sym::Vec))
++                    "`{box_content}` is already on the heap, `Box<{box_content}>` makes an extra allocation")
 +            );
 +            true
 +        } else {
 +            false
 +        }
 +    }
 +}
 +
 +fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Symbol> {
 +    let param = qpath_generic_tys(qpath).next()?;
 +    let id = path_def_id(cx, param)?;
++    cx.tcx.get_diagnostic_name(id).filter(|&name| {
++        matches!(
++            name,
++            sym::HashMap
++                | sym::String
++                | sym::Vec
++                | sym::HashSet
++                | sym::VecDeque
++                | sym::LinkedList
++                | sym::BTreeMap
++                | sym::BTreeSet
++                | sym::BinaryHeap
++        )
++    })
 +}
index 025dd57e83aa59ccfe8e7da39f19ef6521597c4b,0000000000000000000000000000000000000000..04f16fd2161c51a0cdbed5e35419540052e912c5
mode 100644,000000..100644
--- /dev/null
@@@ -1,344 -1,0 +1,358 @@@
-         && let Some(body) = cx.enclosing_body
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::source::walk_span_to_context;
 +use clippy_utils::{get_parent_node, is_lint_allowed};
 +use rustc_data_structures::sync::Lrc;
 +use rustc_hir as hir;
 +use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource};
 +use rustc_lexer::{tokenize, TokenKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{BytePos, Pos, Span, SyntaxContext};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `unsafe` blocks and impls without a `// SAFETY: ` comment
 +    /// explaining why the unsafe operations performed inside
 +    /// the block are safe.
 +    ///
 +    /// Note the comment must appear on the line(s) preceding the unsafe block
 +    /// with nothing appearing in between. The following is ok:
 +    /// ```ignore
 +    /// foo(
 +    ///     // SAFETY:
 +    ///     // This is a valid safety comment
 +    ///     unsafe { *x }
 +    /// )
 +    /// ```
 +    /// But neither of these are:
 +    /// ```ignore
 +    /// // SAFETY:
 +    /// // This is not a valid safety comment
 +    /// foo(
 +    ///     /* SAFETY: Neither is this */ unsafe { *x },
 +    /// );
 +    /// ```
 +    ///
 +    /// ### Why is this bad?
 +    /// Undocumented unsafe blocks and impls can make it difficult to
 +    /// read and maintain code, as well as uncover unsoundness
 +    /// and bugs.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::ptr::NonNull;
 +    /// let a = &mut 42;
 +    ///
 +    /// let ptr = unsafe { NonNull::new_unchecked(a) };
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::ptr::NonNull;
 +    /// let a = &mut 42;
 +    ///
 +    /// // SAFETY: references are guaranteed to be non-null.
 +    /// let ptr = unsafe { NonNull::new_unchecked(a) };
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub UNDOCUMENTED_UNSAFE_BLOCKS,
 +    restriction,
 +    "creating an unsafe block without explaining why it is safe"
 +}
 +
 +declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]);
 +
 +impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
 +    fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) {
 +        if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
 +            && !in_external_macro(cx.tcx.sess, block.span)
 +            && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id)
 +            && !is_unsafe_from_proc_macro(cx, block.span)
 +            && !block_has_safety_comment(cx, block)
 +        {
 +            let source_map = cx.tcx.sess.source_map();
 +            let span = if source_map.is_multiline(block.span) {
 +                source_map.span_until_char(block.span, '\n')
 +            } else {
 +                block.span
 +            };
 +
 +            span_lint_and_help(
 +                cx,
 +                UNDOCUMENTED_UNSAFE_BLOCKS,
 +                span,
 +                "unsafe block missing a safety comment",
 +                None,
 +                "consider adding a safety comment on the preceding line",
 +            );
 +        }
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
 +        if let hir::ItemKind::Impl(imple) = item.kind
 +            && imple.unsafety == hir::Unsafety::Unsafe
 +            && !in_external_macro(cx.tcx.sess, item.span)
 +            && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id())
 +            && !is_unsafe_from_proc_macro(cx, item.span)
 +            && !item_has_safety_comment(cx, item)
 +        {
 +            let source_map = cx.tcx.sess.source_map();
 +            let span = if source_map.is_multiline(item.span) {
 +                source_map.span_until_char(item.span, '\n')
 +            } else {
 +                item.span
 +            };
 +
 +            span_lint_and_help(
 +                cx,
 +                UNDOCUMENTED_UNSAFE_BLOCKS,
 +                span,
 +                "unsafe impl missing a safety comment",
 +                None,
 +                "consider adding a safety comment on the preceding line",
 +            );
 +        }
 +    }
 +}
 +
 +fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool {
 +    let source_map = cx.sess().source_map();
 +    let file_pos = source_map.lookup_byte_offset(span.lo());
 +    file_pos
 +        .sf
 +        .src
 +        .as_deref()
 +        .and_then(|src| src.get(file_pos.pos.to_usize()..))
 +        .map_or(true, |src| !src.starts_with("unsafe"))
 +}
 +
 +/// Checks if the lines immediately preceding the block contain a safety comment.
 +fn block_has_safety_comment(cx: &LateContext<'_>, block: &hir::Block<'_>) -> bool {
 +    // This intentionally ignores text before the start of a function so something like:
 +    // ```
 +    //     // SAFETY: reason
 +    //     fn foo() { unsafe { .. } }
 +    // ```
 +    // won't work. This is to avoid dealing with where such a comment should be place relative to
 +    // attributes and doc comments.
 +
 +    span_from_macro_expansion_has_safety_comment(cx, block.span) || span_in_body_has_safety_comment(cx, block.span)
 +}
 +
 +/// Checks if the lines immediately preceding the item contain a safety comment.
 +#[allow(clippy::collapsible_match)]
 +fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool {
 +    if span_from_macro_expansion_has_safety_comment(cx, item.span) {
 +        return true;
 +    }
 +
 +    if item.span.ctxt() == SyntaxContext::root() {
 +        if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) {
 +            let comment_start = match parent_node {
 +                Node::Crate(parent_mod) => {
 +                    comment_start_before_impl_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item)
 +                },
 +                Node::Item(parent_item) => {
 +                    if let ItemKind::Mod(parent_mod) = &parent_item.kind {
 +                        comment_start_before_impl_in_mod(cx, parent_mod, parent_item.span, item)
 +                    } else {
 +                        // Doesn't support impls in this position. Pretend a comment was found.
 +                        return true;
 +                    }
 +                },
 +                Node::Stmt(stmt) => {
 +                    if let Some(stmt_parent) = get_parent_node(cx.tcx, stmt.hir_id) {
 +                        match stmt_parent {
 +                            Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo),
 +                            _ => {
 +                                // Doesn't support impls in this position. Pretend a comment was found.
 +                                return true;
 +                            },
 +                        }
 +                    } else {
 +                        // Problem getting the parent node. Pretend a comment was found.
 +                        return true;
 +                    }
 +                },
 +                _ => {
 +                    // Doesn't support impls in this position. Pretend a comment was found.
 +                    return true;
 +                },
 +            };
 +
 +            let source_map = cx.sess().source_map();
 +            if let Some(comment_start) = comment_start
 +                && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo())
 +                && let Ok(comment_start_line) = source_map.lookup_line(comment_start)
 +                && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
 +                && let Some(src) = unsafe_line.sf.src.as_deref()
 +            {
 +                unsafe_line.sf.lines(|lines| {
 +                    comment_start_line.line < unsafe_line.line && text_has_safety_comment(
 +                        src,
 +                        &lines[comment_start_line.line + 1..=unsafe_line.line],
 +                        unsafe_line.sf.start_pos.to_usize(),
 +                    )
 +                })
 +            } else {
 +                // Problem getting source text. Pretend a comment was found.
 +                true
 +            }
 +        } else {
 +            // No parent node. Pretend a comment was found.
 +            true
 +        }
 +    } else {
 +        false
 +    }
 +}
 +
 +fn comment_start_before_impl_in_mod(
 +    cx: &LateContext<'_>,
 +    parent_mod: &hir::Mod<'_>,
 +    parent_mod_span: Span,
 +    imple: &hir::Item<'_>,
 +) -> Option<BytePos> {
 +    parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| {
 +        if *item_id == imple.item_id() {
 +            if idx == 0 {
 +                // mod A { /* comment */ unsafe impl T {} ... }
 +                // ^------------------------------------------^ returns the start of this span
 +                // ^---------------------^ finally checks comments in this range
 +                if let Some(sp) = walk_span_to_context(parent_mod_span, SyntaxContext::root()) {
 +                    return Some(sp.lo());
 +                }
 +            } else {
 +                // some_item /* comment */ unsafe impl T {}
 +                // ^-------^ returns the end of this span
 +                //         ^---------------^ finally checks comments in this range
 +                let prev_item = cx.tcx.hir().item(parent_mod.item_ids[idx - 1]);
 +                if let Some(sp) = walk_span_to_context(prev_item.span, SyntaxContext::root()) {
 +                    return Some(sp.hi());
 +                }
 +            }
 +        }
 +        None
 +    })
 +}
 +
 +fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
 +    let source_map = cx.sess().source_map();
 +    let ctxt = span.ctxt();
 +    if ctxt == SyntaxContext::root() {
 +        false
 +    } else {
 +        // From a macro expansion. Get the text from the start of the macro declaration to start of the
 +        // unsafe block.
 +        //     macro_rules! foo { () => { stuff }; (x) => { unsafe { stuff } }; }
 +        //     ^--------------------------------------------^
 +        if let Ok(unsafe_line) = source_map.lookup_line(span.lo())
 +            && let Ok(macro_line) = source_map.lookup_line(ctxt.outer_expn_data().def_site.lo())
 +            && Lrc::ptr_eq(&unsafe_line.sf, &macro_line.sf)
 +            && let Some(src) = unsafe_line.sf.src.as_deref()
 +        {
 +            unsafe_line.sf.lines(|lines| {
 +                macro_line.line < unsafe_line.line && text_has_safety_comment(
 +                    src,
 +                    &lines[macro_line.line + 1..=unsafe_line.line],
 +                    unsafe_line.sf.start_pos.to_usize(),
 +                )
 +            })
 +        } else {
 +            // Problem getting source text. Pretend a comment was found.
 +            true
 +        }
 +    }
 +}
 +
++fn get_body_search_span(cx: &LateContext<'_>) -> Option<Span> {
++    let body = cx.enclosing_body?;
++    let map = cx.tcx.hir();
++    let mut span = map.body(body).value.span;
++    for (_, node) in map.parent_iter(body.hir_id) {
++        match node {
++            Node::Expr(e) => span = e.span,
++            Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::Local(_) => (),
++            _ => break,
++        }
++    }
++    Some(span)
++}
++
 +fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
 +    let source_map = cx.sess().source_map();
 +    let ctxt = span.ctxt();
 +    if ctxt == SyntaxContext::root()
-             && let Some(body_span) = walk_span_to_context(cx.tcx.hir().body(body).value.span, SyntaxContext::root())
++        && let Some(search_span) = get_body_search_span(cx)
 +    {
 +        if let Ok(unsafe_line) = source_map.lookup_line(span.lo())
++            && let Some(body_span) = walk_span_to_context(search_span, SyntaxContext::root())
 +            && let Ok(body_line) = source_map.lookup_line(body_span.lo())
 +            && Lrc::ptr_eq(&unsafe_line.sf, &body_line.sf)
 +            && let Some(src) = unsafe_line.sf.src.as_deref()
 +        {
 +            // Get the text from the start of function body to the unsafe block.
 +            //     fn foo() { some_stuff; unsafe { stuff }; other_stuff; }
 +            //              ^-------------^
 +            unsafe_line.sf.lines(|lines| {
 +                body_line.line < unsafe_line.line && text_has_safety_comment(
 +                    src,
 +                    &lines[body_line.line + 1..=unsafe_line.line],
 +                    unsafe_line.sf.start_pos.to_usize(),
 +                )
 +            })
 +        } else {
 +            // Problem getting source text. Pretend a comment was found.
 +            true
 +        }
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Checks if the given text has a safety comment for the immediately proceeding line.
 +fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> bool {
 +    let mut lines = line_starts
 +        .array_windows::<2>()
 +        .rev()
 +        .map_while(|[start, end]| {
 +            let start = start.to_usize() - offset;
 +            let end = end.to_usize() - offset;
 +            src.get(start..end).map(|text| (start, text.trim_start()))
 +        })
 +        .filter(|(_, text)| !text.is_empty());
 +
 +    let Some((line_start, line)) = lines.next() else {
 +        return false;
 +    };
 +    // Check for a sequence of line comments.
 +    if line.starts_with("//") {
 +        let mut line = line;
 +        loop {
 +            if line.to_ascii_uppercase().contains("SAFETY:") {
 +                return true;
 +            }
 +            match lines.next() {
 +                Some((_, x)) if x.starts_with("//") => line = x,
 +                _ => return false,
 +            }
 +        }
 +    }
 +    // No line comments; look for the start of a block comment.
 +    // This will only find them if they are at the start of a line.
 +    let (mut line_start, mut line) = (line_start, line);
 +    loop {
 +        if line.starts_with("/*") {
 +            let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start();
 +            let mut tokens = tokenize(src);
 +            return src[..tokens.next().unwrap().len]
 +                .to_ascii_uppercase()
 +                .contains("SAFETY:")
 +                && tokens.all(|t| t.kind == TokenKind::Whitespace);
 +        }
 +        match lines.next() {
 +            Some(x) => (line_start, line) = x,
 +            None => return false,
 +        }
 +    }
 +}
index cf509455aad0aeea766170e78a53aedc94377699,0000000000000000000000000000000000000000..aec028d5c4824b63bfad47af38eca5846a18ab1a
mode 100644,000000..100644
--- /dev/null
@@@ -1,110 -1,0 +1,165 @@@
- use clippy_utils::visitors::for_each_value_source;
 +use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::get_parent_node;
 +use clippy_utils::source::snippet_with_macro_callsite;
- use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind};
++use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source};
 +use core::ops::ControlFlow;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::{DefKind, Res};
- use rustc_middle::ty::{self, Ty, TypeVisitable, TypeSuperVisitable, TypeVisitor};
++use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, Node, PatKind, QPath, TyKind};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::lint::in_external_macro;
- pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
-     if let StmtKind::Local(local) = stmt.kind
-         && let Some(init) = local.init
++use rustc_middle::ty;
 +
 +use super::LET_UNIT_VALUE;
 +
-         && !in_external_macro(cx.sess(), stmt.span)
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
++    if let Some(init) = local.init
 +        && !local.pat.span.from_expansion()
-         let needs_inferred = for_each_value_source(init, &mut |e| if needs_inferred_result_ty(cx, e) {
-             ControlFlow::Continue(())
-         } else {
-             ControlFlow::Break(())
-         }).is_continue();
-         if needs_inferred {
-             if !matches!(local.pat.kind, PatKind::Wild) {
++        && !in_external_macro(cx.sess(), local.span)
 +        && cx.typeck_results().pat_ty(local.pat).is_unit()
 +    {
-                     stmt.span,
++        if (local.ty.map_or(false, |ty| !matches!(ty.kind, TyKind::Infer))
++            || matches!(local.pat.kind, PatKind::Tuple([], None)))
++            && expr_needs_inferred_result(cx, init)
++        {
++            if !matches!(local.pat.kind, PatKind::Wild | PatKind::Tuple([], None)) {
 +                span_lint_and_then(
 +                    cx,
 +                    LET_UNIT_VALUE,
-                             diag.span_suggestion(
-                                 local.pat.span,
-                                 "use a wild (`_`) binding",
-                                 "_",
-                                 Applicability::MaybeIncorrect, // snippet
-                             );
++                    local.span,
 +                    "this let-binding has unit value",
 +                    |diag| {
-                 stmt.span,
++                        diag.span_suggestion(
++                            local.pat.span,
++                            "use a wild (`_`) binding",
++                            "_",
++                            Applicability::MaybeIncorrect, // snippet
++                        );
 +                    },
 +                );
 +            }
 +        } else {
 +            span_lint_and_then(
 +                cx,
 +                LET_UNIT_VALUE,
-                             stmt.span,
++                local.span,
 +                "this let-binding has unit value",
 +                |diag| {
 +                    if let Some(expr) = &local.init {
 +                        let snip = snippet_with_macro_callsite(cx, expr.span, "()");
 +                        diag.span_suggestion(
-                             format!("{};", snip),
++                            local.span,
 +                            "omit the `let` binding",
- fn needs_inferred_result_ty(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
-     let id = match e.kind {
++                            format!("{snip};"),
 +                            Applicability::MachineApplicable, // snippet
 +                        );
 +                    }
 +                },
 +            );
 +        }
 +    }
 +}
 +
-             _,
++/// Checks sub-expressions which create the value returned by the given expression for whether
++/// return value inference is needed. This checks through locals to see if they also need inference
++/// at this point.
++///
++/// e.g.
++/// ```rust,ignore
++/// let bar = foo();
++/// let x: u32 = if true { baz() } else { bar };
++/// ```
++/// Here the sources of the value assigned to `x` would be `baz()`, and `foo()` via the
++/// initialization of `bar`. If both `foo` and `baz` have a return type which require type
++/// inference then this function would return `true`.
++fn expr_needs_inferred_result<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
++    // The locals used for initialization which have yet to be checked.
++    let mut locals_to_check = Vec::new();
++    // All the locals which have been added to `locals_to_check`. Needed to prevent cycles.
++    let mut seen_locals = HirIdSet::default();
++    if !each_value_source_needs_inference(cx, e, &mut locals_to_check, &mut seen_locals) {
++        return false;
++    }
++    while let Some(id) = locals_to_check.pop() {
++        if let Some(Node::Local(l)) = get_parent_node(cx.tcx, id) {
++            if !l.ty.map_or(true, |ty| matches!(ty.kind, TyKind::Infer)) {
++                return false;
++            }
++            if let Some(e) = l.init {
++                if !each_value_source_needs_inference(cx, e, &mut locals_to_check, &mut seen_locals) {
++                    return false;
++                }
++            } else if for_each_local_assignment(cx, id, |e| {
++                if each_value_source_needs_inference(cx, e, &mut locals_to_check, &mut seen_locals) {
++                    ControlFlow::Continue(())
++                } else {
++                    ControlFlow::Break(())
++                }
++            })
++            .is_break()
++            {
++                return false;
++            }
++        }
++    }
++
++    true
++}
++
++fn each_value_source_needs_inference(
++    cx: &LateContext<'_>,
++    e: &Expr<'_>,
++    locals_to_check: &mut Vec<HirId>,
++    seen_locals: &mut HirIdSet,
++) -> bool {
++    for_each_value_source(e, &mut |e| {
++        if needs_inferred_result_ty(cx, e, locals_to_check, seen_locals) {
++            ControlFlow::Continue(())
++        } else {
++            ControlFlow::Break(())
++        }
++    })
++    .is_continue()
++}
++
++fn needs_inferred_result_ty(
++    cx: &LateContext<'_>,
++    e: &Expr<'_>,
++    locals_to_check: &mut Vec<HirId>,
++    seen_locals: &mut HirIdSet,
++) -> bool {
++    let (id, args) = match e.kind {
 +        ExprKind::Call(
 +            Expr {
 +                kind: ExprKind::Path(ref path),
 +                hir_id,
 +                ..
 +            },
-             Res::Def(DefKind::AssocFn | DefKind::Fn, id) => id,
++            args,
 +        ) => match cx.qpath_res(path, *hir_id) {
-         ExprKind::MethodCall(..) => match cx.typeck_results().type_dependent_def_id(e.hir_id) {
-             Some(id) => id,
++            Res::Def(DefKind::AssocFn | DefKind::Fn, id) => (id, args),
 +            _ => return false,
 +        },
-         sig.inputs().iter().all(|&ty| !ty_contains_param(ty, output_ty.index))
++        ExprKind::MethodCall(_, args, _) => match cx.typeck_results().type_dependent_def_id(e.hir_id) {
++            Some(id) => (id, args),
 +            None => return false,
 +        },
++        ExprKind::Path(QPath::Resolved(None, path)) => {
++            if let Res::Local(id) = path.res
++                && seen_locals.insert(id)
++            {
++                locals_to_check.push(id);
++            }
++            return true;
++        },
 +        _ => return false,
 +    };
 +    let sig = cx.tcx.fn_sig(id).skip_binder();
 +    if let ty::Param(output_ty) = *sig.output().kind() {
- fn ty_contains_param(ty: Ty<'_>, index: u32) -> bool {
-     struct Visitor(u32);
-     impl<'tcx> TypeVisitor<'tcx> for Visitor {
-         type BreakTy = ();
-         fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-             if let ty::Param(ty) = *ty.kind() {
-                 if ty.index == self.0 {
-                     ControlFlow::BREAK
-                 } else {
-                     ControlFlow::CONTINUE
-                 }
-             } else {
-                 ty.super_visit_with(self)
-             }
-         }
-     }
-     ty.visit_with(&mut Visitor(index)).is_break()
- }
++        sig.inputs().iter().zip(args).all(|(&ty, arg)| {
++            !ty.is_param(output_ty.index) || each_value_source_needs_inference(cx, arg, locals_to_check, seen_locals)
++        })
 +    } else {
 +        false
 +    }
 +}
index a9e2073dec251de0d50b62c3b8d12df6531409f3,0000000000000000000000000000000000000000..6aa86a57c9bdf58b2ccb07cd804c5a1d292da1b6
mode 100644,000000..100644
--- /dev/null
@@@ -1,110 -1,0 +1,110 @@@
- use rustc_hir::{Expr, Stmt};
 +mod let_unit_value;
 +mod unit_arg;
 +mod unit_cmp;
 +mod utils;
 +
- impl LateLintPass<'_> for UnitTypes {
-     fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
-         let_unit_value::check(cx, stmt);
++use rustc_hir::{Expr, Local};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for binding a unit value.
 +    ///
 +    /// ### Why is this bad?
 +    /// A unit value cannot usefully be used anywhere. So
 +    /// binding one is kind of pointless.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = {
 +    ///     1;
 +    /// };
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub LET_UNIT_VALUE,
 +    style,
 +    "creating a `let` binding to a value of unit type, which usually can't be used afterwards"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for comparisons to unit. This includes all binary
 +    /// comparisons (like `==` and `<`) and asserts.
 +    ///
 +    /// ### Why is this bad?
 +    /// Unit is always equal to itself, and thus is just a
 +    /// clumsily written constant. Mostly this happens when someone accidentally
 +    /// adds semicolons at the end of the operands.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn foo() {};
 +    /// # fn bar() {};
 +    /// # fn baz() {};
 +    /// if {
 +    ///     foo();
 +    /// } == {
 +    ///     bar();
 +    /// } {
 +    ///     baz();
 +    /// }
 +    /// ```
 +    /// is equal to
 +    /// ```rust
 +    /// # fn foo() {};
 +    /// # fn bar() {};
 +    /// # fn baz() {};
 +    /// {
 +    ///     foo();
 +    ///     bar();
 +    ///     baz();
 +    /// }
 +    /// ```
 +    ///
 +    /// For asserts:
 +    /// ```rust
 +    /// # fn foo() {};
 +    /// # fn bar() {};
 +    /// assert_eq!({ foo(); }, { bar(); });
 +    /// ```
 +    /// will always succeed
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNIT_CMP,
 +    correctness,
 +    "comparing unit values"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for passing a unit value as an argument to a function without using a
 +    /// unit literal (`()`).
 +    ///
 +    /// ### Why is this bad?
 +    /// This is likely the result of an accidental semicolon.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// foo({
 +    ///     let a = bar();
 +    ///     baz(a);
 +    /// })
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNIT_ARG,
 +    complexity,
 +    "passing unit to a function"
 +}
 +
 +declare_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP, UNIT_ARG]);
 +
++impl<'tcx> LateLintPass<'tcx> for UnitTypes {
++    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
++        let_unit_value::check(cx, local);
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        unit_cmp::check(cx, expr);
 +        unit_arg::check(cx, expr);
 +    }
 +}
index a94f0357977eab8766bea03e03d1eee36b0b003f,0000000000000000000000000000000000000000..b309653291b11ce58d3d1d3a45eb696705490cbd
mode 100644,000000..100644
--- /dev/null
@@@ -1,1436 -1,0 +1,1436 @@@
-     BinOpKind, Block, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty, TyKind,
-     UnOp,
 +use crate::utils::internal_lints::metadata_collector::is_deprecated_lint;
 +use clippy_utils::consts::{constant_simple, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::macros::root_macro_call_first_node;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::match_type;
 +use clippy_utils::{
 +    def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path,
 +    method_calls, paths, peel_blocks_with_stmt, SpanlessEq,
 +};
 +use if_chain::if_chain;
 +use rustc_ast as ast;
 +use rustc_ast::ast::{Crate, ItemKind, LitKind, ModKind, NodeId};
 +use rustc_ast::visit::FnKind;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::hir_id::CRATE_HIR_ID;
 +use rustc_hir::intravisit::Visitor;
 +use rustc_hir::{
-             if let ExprKind::Closure { body, .. } = &and_then_args[4].kind;
-             let body = cx.tcx.hir().body(*body);
++    BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty,
++    TyKind, UnOp,
 +};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::mir::interpret::ConstValue;
 +use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, FloatTy};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Spanned;
 +use rustc_span::symbol::Symbol;
 +use rustc_span::{sym, BytePos, Span};
 +use rustc_typeck::hir_ty_to_ty;
 +
 +use std::borrow::{Borrow, Cow};
 +
 +#[cfg(feature = "internal")]
 +pub mod metadata_collector;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for various things we like to keep tidy in clippy.
 +    ///
 +    /// ### Why is this bad?
 +    /// We like to pretend we're an example of tidy code.
 +    ///
 +    /// ### Example
 +    /// Wrong ordering of the util::paths constants.
 +    pub CLIPPY_LINTS_INTERNAL,
 +    internal,
 +    "various things that will negatively affect your clippy experience"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Ensures every lint is associated to a `LintPass`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The compiler only knows lints via a `LintPass`. Without
 +    /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
 +    /// know the name of the lint.
 +    ///
 +    /// ### Known problems
 +    /// Only checks for lints associated using the
 +    /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// declare_lint! { pub LINT_1, ... }
 +    /// declare_lint! { pub LINT_2, ... }
 +    /// declare_lint! { pub FORGOTTEN_LINT, ... }
 +    /// // ...
 +    /// declare_lint_pass!(Pass => [LINT_1, LINT_2]);
 +    /// // missing FORGOTTEN_LINT
 +    /// ```
 +    pub LINT_WITHOUT_LINT_PASS,
 +    internal,
 +    "declaring a lint without associating it in a LintPass"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*`
 +    /// variant of the function.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `utils::*` variants also add a link to the Clippy documentation to the
 +    /// warning/error messages.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// cx.span_lint(LINT_NAME, "message");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// utils::span_lint(cx, LINT_NAME, "message");
 +    /// ```
 +    pub COMPILER_LINT_FUNCTIONS,
 +    internal,
 +    "usage of the lint functions of the compiler instead of the utils::* variant"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `cx.outer().expn_data()` and suggests to use
 +    /// the `cx.outer_expn_data()`
 +    ///
 +    /// ### Why is this bad?
 +    /// `cx.outer_expn_data()` is faster and more concise.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// expr.span.ctxt().outer().expn_data()
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// expr.span.ctxt().outer_expn_data()
 +    /// ```
 +    pub OUTER_EXPN_EXPN_DATA,
 +    internal,
 +    "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Not an actual lint. This lint is only meant for testing our customized internal compiler
 +    /// error message by calling `panic`.
 +    ///
 +    /// ### Why is this bad?
 +    /// ICE in large quantities can damage your teeth
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// 🍦🍦🍦🍦🍦
 +    /// ```
 +    pub PRODUCE_ICE,
 +    internal,
 +    "this message should not appear anywhere as we ICE before and don't emit the lint"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for cases of an auto-generated lint without an updated description,
 +    /// i.e. `default lint description`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Indicates that the lint is not finished.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// declare_lint! { pub COOL_LINT, nursery, "default lint description" }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" }
 +    /// ```
 +    pub DEFAULT_LINT,
 +    internal,
 +    "found 'default lint description' in a lint declaration"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Lints `span_lint_and_then` function calls, where the
 +    /// closure argument has only one statement and that statement is a method
 +    /// call to `span_suggestion`, `span_help`, `span_note` (using the same
 +    /// span), `help` or `note`.
 +    ///
 +    /// These usages of `span_lint_and_then` should be replaced with one of the
 +    /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or
 +    /// `span_lint_and_note`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the wrapper `span_lint_and_*` functions, is more
 +    /// convenient, readable and less error prone.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.span_suggestion(
 +    ///         expr.span,
 +    ///         help_msg,
 +    ///         sugg.to_string(),
 +    ///         Applicability::MachineApplicable,
 +    ///     );
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.span_help(expr.span, help_msg);
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.help(help_msg);
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.span_note(expr.span, note_msg);
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.note(note_msg);
 +    /// });
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// span_lint_and_sugg(
 +    ///     cx,
 +    ///     TEST_LINT,
 +    ///     expr.span,
 +    ///     lint_msg,
 +    ///     help_msg,
 +    ///     sugg.to_string(),
 +    ///     Applicability::MachineApplicable,
 +    /// );
 +    /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg);
 +    /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg);
 +    /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg);
 +    /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg);
 +    /// ```
 +    pub COLLAPSIBLE_SPAN_LINT_CALLS,
 +    internal,
 +    "found collapsible `span_lint_and_then` calls"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `utils::match_type()` on a type diagnostic item
 +    /// and suggests to use `utils::is_type_diagnostic_item()` instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// `utils::is_type_diagnostic_item()` does not require hardcoded paths.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// utils::match_type(cx, ty, &paths::VEC)
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// utils::is_type_diagnostic_item(cx, ty, sym::Vec)
 +    /// ```
 +    pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +    internal,
 +    "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks the paths module for invalid paths.
 +    ///
 +    /// ### Why is this bad?
 +    /// It indicates a bug in the code.
 +    ///
 +    /// ### Example
 +    /// None.
 +    pub INVALID_PATHS,
 +    internal,
 +    "invalid path"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for interning symbols that have already been pre-interned and defined as constants.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's faster and easier to use the symbol constant.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let _ = sym!(f32);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// let _ = sym::f32;
 +    /// ```
 +    pub INTERNING_DEFINED_SYMBOL,
 +    internal,
 +    "interning a symbol that is pre-interned and defined as a constant"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary conversion from Symbol to a string.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's faster use symbols directly instead of strings.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// symbol.as_str() == "clippy";
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// symbol == sym::clippy;
 +    /// ```
 +    pub UNNECESSARY_SYMBOL_STR,
 +    internal,
 +    "unnecessary conversion between Symbol and string"
 +}
 +
 +declare_clippy_lint! {
 +    /// Finds unidiomatic usage of `if_chain!`
 +    pub IF_CHAIN_STYLE,
 +    internal,
 +    "non-idiomatic `if_chain!` usage"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for invalid `clippy::version` attributes.
 +    ///
 +    /// Valid values are:
 +    /// * "pre 1.29.0"
 +    /// * any valid semantic version
 +    pub INVALID_CLIPPY_VERSION_ATTRIBUTE,
 +    internal,
 +    "found an invalid `clippy::version` attribute"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for declared clippy lints without the `clippy::version` attribute.
 +    ///
 +    pub MISSING_CLIPPY_VERSION_ATTRIBUTE,
 +    internal,
 +    "found clippy lint without `clippy::version` attribute"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV.
 +    ///
 +    pub MISSING_MSRV_ATTR_IMPL,
 +    internal,
 +    "checking if all necessary steps were taken when adding a MSRV to a lint"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for cases of an auto-generated deprecated lint without an updated reason,
 +    /// i.e. `"default deprecation note"`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Indicates that the documentation is incomplete.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// declare_deprecated_lint! {
 +    ///     /// ### What it does
 +    ///     /// Nothing. This lint has been deprecated.
 +    ///     ///
 +    ///     /// ### Deprecation reason
 +    ///     /// TODO
 +    ///     #[clippy::version = "1.63.0"]
 +    ///     pub COOL_LINT,
 +    ///     "default deprecation note"
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// declare_deprecated_lint! {
 +    ///     /// ### What it does
 +    ///     /// Nothing. This lint has been deprecated.
 +    ///     ///
 +    ///     /// ### Deprecation reason
 +    ///     /// This lint has been replaced by `cooler_lint`
 +    ///     #[clippy::version = "1.63.0"]
 +    ///     pub COOL_LINT,
 +    ///     "this lint has been replaced by `cooler_lint`"
 +    /// }
 +    /// ```
 +    pub DEFAULT_DEPRECATION_REASON,
 +    internal,
 +    "found 'default deprecation note' in a deprecated lint declaration"
 +}
 +
 +declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
 +
 +impl EarlyLintPass for ClippyLintsInternal {
 +    fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
 +        if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") {
 +            if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind {
 +                if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") {
 +                    if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind {
 +                        let mut last_name: Option<&str> = None;
 +                        for item in items {
 +                            let name = item.ident.as_str();
 +                            if let Some(last_name) = last_name {
 +                                if *last_name > *name {
 +                                    span_lint(
 +                                        cx,
 +                                        CLIPPY_LINTS_INTERNAL,
 +                                        item.span,
 +                                        "this constant should be before the previous constant due to lexical \
 +                                         ordering",
 +                                    );
 +                                }
 +                            }
 +                            last_name = Some(name);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Debug, Default)]
 +pub struct LintWithoutLintPass {
 +    declared_lints: FxHashMap<Symbol, Span>,
 +    registered_lints: FxHashSet<Symbol>,
 +}
 +
 +impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]);
 +
 +impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id())
 +            || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id())
 +        {
 +            return;
 +        }
 +
 +        if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
 +            let is_lint_ref_ty = is_lint_ref_type(cx, ty);
 +            if is_deprecated_lint(cx, ty) || is_lint_ref_ty {
 +                check_invalid_clippy_version_attribute(cx, item);
 +
 +                let expr = &cx.tcx.hir().body(body_id).value;
 +                let fields;
 +                if is_lint_ref_ty {
 +                    if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind
 +                        && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind {
 +                            fields = struct_fields;
 +                    } else {
 +                        return;
 +                    }
 +                } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind {
 +                    fields = struct_fields;
 +                } else {
 +                    return;
 +                }
 +
 +                let field = fields
 +                    .iter()
 +                    .find(|f| f.ident.as_str() == "desc")
 +                    .expect("lints must have a description field");
 +
 +                if let ExprKind::Lit(Spanned {
 +                    node: LitKind::Str(ref sym, _),
 +                    ..
 +                }) = field.expr.kind
 +                {
 +                    let sym_str = sym.as_str();
 +                    if is_lint_ref_ty {
 +                        if sym_str == "default lint description" {
 +                            span_lint(
 +                                cx,
 +                                DEFAULT_LINT,
 +                                item.span,
 +                                &format!("the lint `{}` has the default lint description", item.ident.name),
 +                            );
 +                        }
 +
 +                        self.declared_lints.insert(item.ident.name, item.span);
 +                    } else if sym_str == "default deprecation note" {
 +                        span_lint(
 +                            cx,
 +                            DEFAULT_DEPRECATION_REASON,
 +                            item.span,
 +                            &format!("the lint `{}` has the default deprecation reason", item.ident.name),
 +                        );
 +                    }
 +                }
 +            }
 +        } else if let Some(macro_call) = root_macro_call_first_node(cx, item) {
 +            if !matches!(
 +                cx.tcx.item_name(macro_call.def_id).as_str(),
 +                "impl_lint_pass" | "declare_lint_pass"
 +            ) {
 +                return;
 +            }
 +            if let hir::ItemKind::Impl(hir::Impl {
 +                of_trait: None,
 +                items: impl_item_refs,
 +                ..
 +            }) = item.kind
 +            {
 +                let mut collector = LintCollector {
 +                    output: &mut self.registered_lints,
 +                    cx,
 +                };
 +                let body_id = cx.tcx.hir().body_owned_by(
 +                    impl_item_refs
 +                        .iter()
 +                        .find(|iiref| iiref.ident.as_str() == "get_lints")
 +                        .expect("LintPass needs to implement get_lints")
 +                        .id
 +                        .hir_id(),
 +                );
 +                collector.visit_expr(&cx.tcx.hir().body(body_id).value);
 +            }
 +        }
 +    }
 +
 +    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
 +        if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) {
 +            return;
 +        }
 +
 +        for (lint_name, &lint_span) in &self.declared_lints {
 +            // When using the `declare_tool_lint!` macro, the original `lint_span`'s
 +            // file points to "<rustc macros>".
 +            // `compiletest-rs` thinks that's an error in a different file and
 +            // just ignores it. This causes the test in compile-fail/lint_pass
 +            // not able to capture the error.
 +            // Therefore, we need to climb the macro expansion tree and find the
 +            // actual span that invoked `declare_tool_lint!`:
 +            let lint_span = lint_span.ctxt().outer_expn_data().call_site;
 +
 +            if !self.registered_lints.contains(lint_name) {
 +                span_lint(
 +                    cx,
 +                    LINT_WITHOUT_LINT_PASS,
 +                    lint_span,
 +                    &format!("the lint `{}` is not added to any `LintPass`", lint_name),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool {
 +    if let TyKind::Rptr(
 +        _,
 +        MutTy {
 +            ty: inner,
 +            mutbl: Mutability::Not,
 +        },
 +    ) = ty.kind
 +    {
 +        if let TyKind::Path(ref path) = inner.kind {
 +            if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) {
 +                return match_def_path(cx, def_id, &paths::LINT);
 +            }
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
 +    if let Some(value) = extract_clippy_version_value(cx, item) {
 +        // The `sym!` macro doesn't work as it only expects a single token.
 +        // It's better to keep it this way and have a direct `Symbol::intern` call here.
 +        if value == Symbol::intern("pre 1.29.0") {
 +            return;
 +        }
 +
 +        if RustcVersion::parse(value.as_str()).is_err() {
 +            span_lint_and_help(
 +                cx,
 +                INVALID_CLIPPY_VERSION_ATTRIBUTE,
 +                item.span,
 +                "this item has an invalid `clippy::version` attribute",
 +                None,
 +                "please use a valid sematic version, see `doc/adding_lints.md`",
 +            );
 +        }
 +    } else {
 +        span_lint_and_help(
 +            cx,
 +            MISSING_CLIPPY_VERSION_ATTRIBUTE,
 +            item.span,
 +            "this lint is missing the `clippy::version` attribute or version value",
 +            None,
 +            "please use a `clippy::version` attribute, see `doc/adding_lints.md`",
 +        );
 +    }
 +}
 +
 +/// This function extracts the version value of a `clippy::version` attribute if the given value has
 +/// one
 +fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> {
 +    let attrs = cx.tcx.hir().attrs(item.hir_id());
 +    attrs.iter().find_map(|attr| {
 +        if_chain! {
 +            // Identify attribute
 +            if let ast::AttrKind::Normal(ref attr_kind, _) = &attr.kind;
 +            if let [tool_name, attr_name] = &attr_kind.path.segments[..];
 +            if tool_name.ident.name == sym::clippy;
 +            if attr_name.ident.name == sym::version;
 +            if let Some(version) = attr.value_str();
 +            then {
 +                Some(version)
 +            } else {
 +                None
 +            }
 +        }
 +    })
 +}
 +
 +struct LintCollector<'a, 'tcx> {
 +    output: &'a mut FxHashSet<Symbol>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
 +        if path.segments.len() == 1 {
 +            self.output.insert(path.segments[0].ident.name);
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
 +
 +#[derive(Clone, Default)]
 +pub struct CompilerLintFunctions {
 +    map: FxHashMap<&'static str, &'static str>,
 +}
 +
 +impl CompilerLintFunctions {
 +    #[must_use]
 +    pub fn new() -> Self {
 +        let mut map = FxHashMap::default();
 +        map.insert("span_lint", "utils::span_lint");
 +        map.insert("struct_span_lint", "utils::span_lint");
 +        map.insert("lint", "utils::span_lint");
 +        map.insert("span_lint_note", "utils::span_lint_and_note");
 +        map.insert("span_lint_help", "utils::span_lint_and_help");
 +        Self { map }
 +    }
 +}
 +
 +impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
 +            let fn_name = path.ident;
 +            if let Some(sugg) = self.map.get(fn_name.as_str());
 +            let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
 +            if match_type(cx, ty, &paths::EARLY_CONTEXT)
 +                || match_type(cx, ty, &paths::LATE_CONTEXT);
 +            then {
 +                span_lint_and_help(
 +                    cx,
 +                    COMPILER_LINT_FUNCTIONS,
 +                    path.ident.span,
 +                    "usage of a compiler lint function",
 +                    None,
 +                    &format!("please use the Clippy variant of this function: `{}`", sugg),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
 +
 +impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) {
 +            return;
 +        }
 +
 +        let (method_names, arg_lists, spans) = method_calls(expr, 2);
 +        let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect();
 +        if_chain! {
 +            if let ["expn_data", "outer_expn"] = method_names.as_slice();
 +            let args = arg_lists[1];
 +            if args.len() == 1;
 +            let self_arg = &args[0];
 +            let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
 +            if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    OUTER_EXPN_EXPN_DATA,
 +                    spans[1].with_hi(expr.span.hi()),
 +                    "usage of `outer_expn().expn_data()`",
 +                    "try",
 +                    "outer_expn_data()".to_string(),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +declare_lint_pass!(ProduceIce => [PRODUCE_ICE]);
 +
 +impl EarlyLintPass for ProduceIce {
 +    fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
 +        assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?");
 +    }
 +}
 +
 +fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
 +    match fn_kind {
 +        FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy",
 +        FnKind::Closure(..) => false,
 +    }
 +}
 +
 +declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::Call(func, and_then_args) = expr.kind;
 +            if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
 +            if and_then_args.len() == 5;
++            if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind;
++            let body = cx.tcx.hir().body(body);
 +            let only_expr = peel_blocks_with_stmt(&body.value);
 +            if let ExprKind::MethodCall(ps, span_call_args, _) = &only_expr.kind;
 +            if let ExprKind::Path(..) = span_call_args[0].kind;
 +            then {
 +                let and_then_snippets = get_and_then_snippets(cx, and_then_args);
 +                let mut sle = SpanlessEq::new(cx).deny_side_effects();
 +                match ps.ident.as_str() {
 +                    "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
 +                        suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args));
 +                    },
 +                    "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
 +                        let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
 +                        suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
 +                    },
 +                    "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
 +                        let note_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
 +                        suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
 +                    },
 +                    "help" => {
 +                        let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
 +                        suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
 +                    }
 +                    "note" => {
 +                        let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
 +                        suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false);
 +                    }
 +                    _  => (),
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +struct AndThenSnippets<'a> {
 +    cx: Cow<'a, str>,
 +    lint: Cow<'a, str>,
 +    span: Cow<'a, str>,
 +    msg: Cow<'a, str>,
 +}
 +
 +fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> {
 +    let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx");
 +    let lint_snippet = snippet(cx, and_then_snippets[1].span, "..");
 +    let span_snippet = snippet(cx, and_then_snippets[2].span, "span");
 +    let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#);
 +
 +    AndThenSnippets {
 +        cx: cx_snippet,
 +        lint: lint_snippet,
 +        span: span_snippet,
 +        msg: msg_snippet,
 +    }
 +}
 +
 +struct SpanSuggestionSnippets<'a> {
 +    help: Cow<'a, str>,
 +    sugg: Cow<'a, str>,
 +    applicability: Cow<'a, str>,
 +}
 +
 +fn span_suggestion_snippets<'a, 'hir>(
 +    cx: &LateContext<'_>,
 +    span_call_args: &'hir [Expr<'hir>],
 +) -> SpanSuggestionSnippets<'a> {
 +    let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
 +    let sugg_snippet = snippet(cx, span_call_args[3].span, "..");
 +    let applicability_snippet = snippet(cx, span_call_args[4].span, "Applicability::MachineApplicable");
 +
 +    SpanSuggestionSnippets {
 +        help: help_snippet,
 +        sugg: sugg_snippet,
 +        applicability: applicability_snippet,
 +    }
 +}
 +
 +fn suggest_suggestion(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    span_suggestion_snippets: &SpanSuggestionSnippets<'_>,
 +) {
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
 +        "this call is collapsible",
 +        "collapse into",
 +        format!(
 +            "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})",
 +            and_then_snippets.cx,
 +            and_then_snippets.lint,
 +            and_then_snippets.span,
 +            and_then_snippets.msg,
 +            span_suggestion_snippets.help,
 +            span_suggestion_snippets.sugg,
 +            span_suggestion_snippets.applicability
 +        ),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn suggest_help(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    help: &str,
 +    with_span: bool,
 +) {
 +    let option_span = if with_span {
 +        format!("Some({})", and_then_snippets.span)
 +    } else {
 +        "None".to_string()
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
 +        "this call is collapsible",
 +        "collapse into",
 +        format!(
 +            "span_lint_and_help({}, {}, {}, {}, {}, {})",
 +            and_then_snippets.cx,
 +            and_then_snippets.lint,
 +            and_then_snippets.span,
 +            and_then_snippets.msg,
 +            &option_span,
 +            help
 +        ),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn suggest_note(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    note: &str,
 +    with_span: bool,
 +) {
 +    let note_span = if with_span {
 +        format!("Some({})", and_then_snippets.span)
 +    } else {
 +        "None".to_string()
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
 +        "this call is collapsible",
 +        "collapse into",
 +        format!(
 +            "span_lint_and_note({}, {}, {}, {}, {}, {})",
 +            and_then_snippets.cx,
 +            and_then_snippets.lint,
 +            and_then_snippets.span,
 +            and_then_snippets.msg,
 +            note_span,
 +            note
 +        ),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            // Check if this is a call to utils::match_type()
 +            if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind;
 +            if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]);
 +            // Extract the path to the matched type
 +            if let Some(segments) = path_to_matched_type(cx, ty_path);
 +            let segments: Vec<&str> = segments.iter().map(Symbol::as_str).collect();
 +            if let Some(ty_did) = def_path_res(cx, &segments[..]).opt_def_id();
 +            // Check if the matched type is a diagnostic item
 +            if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did);
 +            then {
 +                // TODO: check paths constants from external crates.
 +                let cx_snippet = snippet(cx, context.span, "_");
 +                let ty_snippet = snippet(cx, ty.span, "_");
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +                    expr.span,
 +                    "usage of `clippy_utils::ty::match_type()` on a type diagnostic item",
 +                    "try",
 +                    format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
 +                    Applicability::MaybeIncorrect,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<Symbol>> {
 +    use rustc_hir::ItemKind;
 +
 +    match &expr.kind {
 +        ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr),
 +        ExprKind::Path(qpath) => match cx.qpath_res(qpath, expr.hir_id) {
 +            Res::Local(hir_id) => {
 +                let parent_id = cx.tcx.hir().get_parent_node(hir_id);
 +                if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) {
 +                    if let Some(init) = local.init {
 +                        return path_to_matched_type(cx, init);
 +                    }
 +                }
 +            },
 +            Res::Def(DefKind::Const | DefKind::Static(..), def_id) => {
 +                if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
 +                    if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
 +                        let body = cx.tcx.hir().body(body_id);
 +                        return path_to_matched_type(cx, &body.value);
 +                    }
 +                }
 +            },
 +            _ => {},
 +        },
 +        ExprKind::Array(exprs) => {
 +            let segments: Vec<Symbol> = exprs
 +                .iter()
 +                .filter_map(|expr| {
 +                    if let ExprKind::Lit(lit) = &expr.kind {
 +                        if let LitKind::Str(sym, _) = lit.node {
 +                            return Some(sym);
 +                        }
 +                    }
 +
 +                    None
 +                })
 +                .collect();
 +
 +            if segments.len() == exprs.len() {
 +                return Some(segments);
 +            }
 +        },
 +        _ => {},
 +    }
 +
 +    None
 +}
 +
 +// This is not a complete resolver for paths. It works on all the paths currently used in the paths
 +// module.  That's all it does and all it needs to do.
 +pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
 +    if def_path_res(cx, path) != Res::Err {
 +        return true;
 +    }
 +
 +    // Some implementations can't be found by `path_to_res`, particularly inherent
 +    // implementations of native types. Check lang items.
 +    let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
 +    let lang_items = cx.tcx.lang_items();
 +    // This list isn't complete, but good enough for our current list of paths.
 +    let incoherent_impls = [
 +        SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32),
 +        SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64),
 +        SimplifiedTypeGen::SliceSimplifiedType,
 +        SimplifiedTypeGen::StrSimplifiedType,
 +    ]
 +    .iter()
 +    .flat_map(|&ty| cx.tcx.incoherent_impls(ty));
 +    for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) {
 +        let lang_item_path = cx.get_def_path(*item_def_id);
 +        if path_syms.starts_with(&lang_item_path) {
 +            if let [item] = &path_syms[lang_item_path.len()..] {
 +                if matches!(
 +                    cx.tcx.def_kind(*item_def_id),
 +                    DefKind::Mod | DefKind::Enum | DefKind::Trait
 +                ) {
 +                    for child in cx.tcx.module_children(*item_def_id) {
 +                        if child.ident.name == *item {
 +                            return true;
 +                        }
 +                    }
 +                } else {
 +                    for child in cx.tcx.associated_item_def_ids(*item_def_id) {
 +                        if cx.tcx.item_name(*child) == *item {
 +                            return true;
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    false
 +}
 +
 +declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        let local_def_id = &cx.tcx.parent_module(item.hir_id());
 +        let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
 +        if_chain! {
 +            if mod_name.as_str() == "paths";
 +            if let hir::ItemKind::Const(ty, body_id) = item.kind;
 +            let ty = hir_ty_to_ty(cx.tcx, ty);
 +            if let ty::Array(el_ty, _) = &ty.kind();
 +            if let ty::Ref(_, el_ty, _) = &el_ty.kind();
 +            if el_ty.is_str();
 +            let body = cx.tcx.hir().body(body_id);
 +            let typeck_results = cx.tcx.typeck_body(body_id);
 +            if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value);
 +            let path: Vec<&str> = path.iter().map(|x| {
 +                    if let Constant::Str(s) = x {
 +                        s.as_str()
 +                    } else {
 +                        // We checked the type of the constant above
 +                        unreachable!()
 +                    }
 +                }).collect();
 +            if !check_path(cx, &path[..]);
 +            then {
 +                span_lint(cx, INVALID_PATHS, item.span, "invalid path");
 +            }
 +        }
 +    }
 +}
 +
 +#[derive(Default)]
 +pub struct InterningDefinedSymbol {
 +    // Maps the symbol value to the constant DefId.
 +    symbol_map: FxHashMap<u32, DefId>,
 +}
 +
 +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]);
 +
 +impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
 +    fn check_crate(&mut self, cx: &LateContext<'_>) {
 +        if !self.symbol_map.is_empty() {
 +            return;
 +        }
 +
 +        for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
 +            if let Some(def_id) = def_path_res(cx, module).opt_def_id() {
 +                for item in cx.tcx.module_children(def_id).iter() {
 +                    if_chain! {
 +                        if let Res::Def(DefKind::Const, item_def_id) = item.res;
 +                        let ty = cx.tcx.type_of(item_def_id);
 +                        if match_type(cx, ty, &paths::SYMBOL);
 +                        if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
 +                        if let Ok(value) = value.to_u32();
 +                        then {
 +                            self.symbol_map.insert(value, item_def_id);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::Call(func, [arg]) = &expr.kind;
 +            if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind();
 +            if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN);
 +            if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg);
 +            let value = Symbol::intern(&arg).as_u32();
 +            if let Some(&def_id) = self.symbol_map.get(&value);
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    INTERNING_DEFINED_SYMBOL,
 +                    is_expn_of(expr.span, "sym").unwrap_or(expr.span),
 +                    "interning a defined symbol",
 +                    "try",
 +                    cx.tcx.def_path_str(def_id),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +        if let ExprKind::Binary(op, left, right) = expr.kind {
 +            if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) {
 +                let data = [
 +                    (left, self.symbol_str_expr(left, cx)),
 +                    (right, self.symbol_str_expr(right, cx)),
 +                ];
 +                match data {
 +                    // both operands are a symbol string
 +                    [(_, Some(left)), (_, Some(right))] => {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            UNNECESSARY_SYMBOL_STR,
 +                            expr.span,
 +                            "unnecessary `Symbol` to string conversion",
 +                            "try",
 +                            format!(
 +                                "{} {} {}",
 +                                left.as_symbol_snippet(cx),
 +                                op.node.as_str(),
 +                                right.as_symbol_snippet(cx),
 +                            ),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                    // one of the operands is a symbol string
 +                    [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => {
 +                        // creating an owned string for comparison
 +                        if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) {
 +                            span_lint_and_sugg(
 +                                cx,
 +                                UNNECESSARY_SYMBOL_STR,
 +                                expr.span,
 +                                "unnecessary string allocation",
 +                                "try",
 +                                format!("{}.as_str()", symbol.as_symbol_snippet(cx)),
 +                                Applicability::MachineApplicable,
 +                            );
 +                        }
 +                    },
 +                    // nothing found
 +                    [(_, None), (_, None)] => {},
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +impl InterningDefinedSymbol {
 +    fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option<SymbolStrExpr<'tcx>> {
 +        static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD];
 +        static SYMBOL_STR_PATHS: &[&[&str]] = &[
 +            &paths::SYMBOL_AS_STR,
 +            &paths::SYMBOL_TO_IDENT_STRING,
 +            &paths::TO_STRING_METHOD,
 +        ];
 +        let call = if_chain! {
 +            if let ExprKind::AddrOf(_, _, e) = expr.kind;
 +            if let ExprKind::Unary(UnOp::Deref, e) = e.kind;
 +            then { e } else { expr }
 +        };
 +        if_chain! {
 +            // is a method call
 +            if let ExprKind::MethodCall(_, [item], _) = call.kind;
 +            if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id);
 +            let ty = cx.typeck_results().expr_ty(item);
 +            // ...on either an Ident or a Symbol
 +            if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) {
 +                Some(false)
 +            } else if match_type(cx, ty, &paths::IDENT) {
 +                Some(true)
 +            } else {
 +                None
 +            };
 +            // ...which converts it to a string
 +            let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS };
 +            if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path));
 +            then {
 +                let is_to_owned = path.last().unwrap().ends_with("string");
 +                return Some(SymbolStrExpr::Expr {
 +                    item,
 +                    is_ident,
 +                    is_to_owned,
 +                });
 +            }
 +        }
 +        // is a string constant
 +        if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) {
 +            let value = Symbol::intern(&s).as_u32();
 +            // ...which matches a symbol constant
 +            if let Some(&def_id) = self.symbol_map.get(&value) {
 +                return Some(SymbolStrExpr::Const(def_id));
 +            }
 +        }
 +        None
 +    }
 +}
 +
 +enum SymbolStrExpr<'tcx> {
 +    /// a string constant with a corresponding symbol constant
 +    Const(DefId),
 +    /// a "symbol to string" expression like `symbol.as_str()`
 +    Expr {
 +        /// part that evaluates to `Symbol` or `Ident`
 +        item: &'tcx Expr<'tcx>,
 +        is_ident: bool,
 +        /// whether an owned `String` is created like `to_ident_string()`
 +        is_to_owned: bool,
 +    },
 +}
 +
 +impl<'tcx> SymbolStrExpr<'tcx> {
 +    /// Returns a snippet that evaluates to a `Symbol` and is const if possible
 +    fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> {
 +        match *self {
 +            Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(),
 +            Self::Expr { item, is_ident, .. } => {
 +                let mut snip = snippet(cx, item.span.source_callsite(), "..");
 +                if is_ident {
 +                    // get `Ident.name`
 +                    snip.to_mut().push_str(".name");
 +                }
 +                snip
 +            },
 +        }
 +    }
 +}
 +
 +declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for IfChainStyle {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
 +        let (local, after, if_chain_span) = if_chain! {
 +            if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts;
 +            if let Some(if_chain_span) = is_expn_of(block.span, "if_chain");
 +            then { (local, after, if_chain_span) } else { return }
 +        };
 +        if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) {
 +            span_lint(
 +                cx,
 +                IF_CHAIN_STYLE,
 +                if_chain_local_span(cx, local, if_chain_span),
 +                "`let` expression should be above the `if_chain!`",
 +            );
 +        } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) {
 +            span_lint(
 +                cx,
 +                IF_CHAIN_STYLE,
 +                if_chain_local_span(cx, local, if_chain_span),
 +                "`let` expression should be inside `then { .. }`",
 +            );
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) {
 +            (cond, then, r#else.is_some())
 +        } else {
 +            return;
 +        };
 +        let then_block = match then.kind {
 +            ExprKind::Block(block, _) => block,
 +            _ => return,
 +        };
 +        let if_chain_span = is_expn_of(expr.span, "if_chain");
 +        if !els {
 +            check_nested_if_chains(cx, expr, then_block, if_chain_span);
 +        }
 +        let if_chain_span = match if_chain_span {
 +            None => return,
 +            Some(span) => span,
 +        };
 +        // check for `if a && b;`
 +        if_chain! {
 +            if let ExprKind::Binary(op, _, _) = cond.kind;
 +            if op.node == BinOpKind::And;
 +            if cx.sess().source_map().is_multiline(cond.span);
 +            then {
 +                span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`");
 +            }
 +        }
 +        if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span)
 +            && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span)
 +        {
 +            span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`");
 +        }
 +    }
 +}
 +
 +fn check_nested_if_chains(
 +    cx: &LateContext<'_>,
 +    if_expr: &Expr<'_>,
 +    then_block: &Block<'_>,
 +    if_chain_span: Option<Span>,
 +) {
 +    #[rustfmt::skip]
 +    let (head, tail) = match *then_block {
 +        Block { stmts, expr: Some(tail), .. } => (stmts, tail),
 +        Block {
 +            stmts: &[
 +                ref head @ ..,
 +                Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. }
 +            ],
 +            ..
 +        } => (head, tail),
 +        _ => return,
 +    };
 +    if_chain! {
 +        if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail);
 +        let sm = cx.sess().source_map();
 +        if head
 +            .iter()
 +            .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span));
 +        if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr);
 +        then {} else { return }
 +    }
 +    let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) {
 +        (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"),
 +        (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"),
 +        (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"),
 +        _ => return,
 +    };
 +    span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| {
 +        let (span, msg) = match head {
 +            [] => return,
 +            [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"),
 +            [a, .., b] => (
 +                a.span.to(b.span),
 +                "these `let` statements can also be in the `if_chain!`",
 +            ),
 +        };
 +        diag.span_help(span, msg);
 +    });
 +}
 +
 +fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool {
 +    cx.tcx
 +        .hir()
 +        .parent_iter(hir_id)
 +        .find(|(_, node)| {
 +            #[rustfmt::skip]
 +            !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_))
 +        })
 +        .map_or(false, |(id, _)| {
 +            is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span)
 +        })
 +}
 +
 +/// Checks a trailing slice of statements and expression of a `Block` to see if they are part
 +/// of the `then {..}` portion of an `if_chain!`
 +fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool {
 +    let span = if let [stmt, ..] = stmts {
 +        stmt.span
 +    } else if let Some(expr) = expr {
 +        expr.span
 +    } else {
 +        // empty `then {}`
 +        return true;
 +    };
 +    is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span)
 +}
 +
 +/// Creates a `Span` for `let x = ..;` in an `if_chain!` call.
 +fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span {
 +    let mut span = local.pat.span;
 +    if let Some(init) = local.init {
 +        span = span.to(init.span);
 +    }
 +    span.adjust(if_chain_span.ctxt().outer_expn());
 +    let sm = cx.sess().source_map();
 +    let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span);
 +    let span = sm.span_extend_to_next_char(span, ';', false);
 +    Span::new(
 +        span.lo() - BytePos(3),
 +        span.hi() + BytePos(1),
 +        span.ctxt(),
 +        span.parent(),
 +    )
 +}
 +
 +declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]);
 +
 +impl LateLintPass<'_> for MsrvAttrImpl {
 +    fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
 +        if_chain! {
 +            if let hir::ItemKind::Impl(hir::Impl {
 +                of_trait: Some(lint_pass_trait_ref),
 +                self_ty,
 +                items,
 +                ..
 +            }) = &item.kind;
 +            if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id();
 +            let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS);
 +            if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS);
 +            let self_ty = hir_ty_to_ty(cx.tcx, self_ty);
 +            if let ty::Adt(self_ty_def, _) = self_ty.kind();
 +            if self_ty_def.is_struct();
 +            if self_ty_def.all_fields().any(|f| {
 +                cx.tcx
 +                    .type_of(f.did)
 +                    .walk()
 +                    .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
 +                    .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION))
 +            });
 +            if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs));
 +            then {
 +                let context = if is_late_pass { "LateContext" } else { "EarlyContext" };
 +                let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" };
 +                let span = cx.sess().source_map().span_through_char(item.span, '{');
 +                span_lint_and_sugg(
 +                    cx,
 +                    MISSING_MSRV_ATTR_IMPL,
 +                    span,
 +                    &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"),
 +                    &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"),
 +                    format!("{}\n    extract_msrv_attr!({context});", snippet(cx, span, "..")),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
index 6518e0a6ea07390620fff232344e21c30312b417,0000000000000000000000000000000000000000..92934c16d4b406a252e121355671e60dfc425dbe
mode 100644,000000..100644
--- /dev/null
@@@ -1,1169 -1,0 +1,1169 @@@
-     self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath,
 +//! This lint is used to collect metadata about clippy lints. This metadata is exported as a json
 +//! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html)
 +//!
 +//! This module and therefore the entire lint is guarded by a feature flag called `internal`
 +//!
 +//! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches
 +//! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such
 +//! a simple mistake)
 +
 +use crate::renamed_lints::RENAMED_LINTS;
 +use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type};
 +
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
 +use clippy_utils::{last_path_segment, match_def_path, match_function_call, match_path, paths};
 +use if_chain::if_chain;
 +use rustc_ast as ast;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::{
-         .find_map(|(group_name, group_level)| (*group_name == lint_group).then(|| *group_level))
++    self as hir, def::DefKind, intravisit, intravisit::Visitor, Closure, ExprKind, Item, ItemKind, Mutability, QPath,
 +};
 +use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
 +use rustc_middle::hir::nested_filter;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::Ident;
 +use rustc_span::{sym, Loc, Span, Symbol};
 +use serde::{ser::SerializeStruct, Serialize, Serializer};
 +use std::collections::BinaryHeap;
 +use std::fmt;
 +use std::fmt::Write as _;
 +use std::fs::{self, OpenOptions};
 +use std::io::prelude::*;
 +use std::path::Path;
 +use std::path::PathBuf;
 +use std::process::Command;
 +
 +/// This is the output file of the lint collector.
 +const OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
 +/// These lints are excluded from the export.
 +const BLACK_LISTED_LINTS: &[&str] = &["lint_author", "dump_hir", "internal_metadata_collector"];
 +/// These groups will be ignored by the lint group matcher. This is useful for collections like
 +/// `clippy::all`
 +const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"];
 +/// Lints within this group will be excluded from the collection. These groups
 +/// have to be defined without the `clippy::` prefix.
 +const EXCLUDED_LINT_GROUPS: [&str; 1] = ["internal"];
 +/// Collected deprecated lint will be assigned to this group in the JSON output
 +const DEPRECATED_LINT_GROUP_STR: &str = "deprecated";
 +/// This is the lint level for deprecated lints that will be displayed in the lint list
 +const DEPRECATED_LINT_LEVEL: &str = "none";
 +/// This array holds Clippy's lint groups with their corresponding default lint level. The
 +/// lint level for deprecated lints is set in `DEPRECATED_LINT_LEVEL`.
 +const DEFAULT_LINT_LEVELS: &[(&str, &str)] = &[
 +    ("correctness", "deny"),
 +    ("suspicious", "warn"),
 +    ("restriction", "allow"),
 +    ("style", "warn"),
 +    ("pedantic", "allow"),
 +    ("complexity", "warn"),
 +    ("perf", "warn"),
 +    ("cargo", "allow"),
 +    ("nursery", "allow"),
 +];
 +/// This prefix is in front of the lint groups in the lint store. The prefix will be trimmed
 +/// to only keep the actual lint group in the output.
 +const CLIPPY_LINT_GROUP_PREFIX: &str = "clippy::";
 +
 +/// This template will be used to format the configuration section in the lint documentation.
 +/// The `configurations` parameter will be replaced with one or multiple formatted
 +/// `ClippyConfiguration` instances. See `CONFIGURATION_VALUE_TEMPLATE` for further customizations
 +macro_rules! CONFIGURATION_SECTION_TEMPLATE {
 +    () => {
 +        r#"
 +### Configuration
 +This lint has the following configuration variables:
 +
 +{configurations}
 +"#
 +    };
 +}
 +/// This template will be used to format an individual `ClippyConfiguration` instance in the
 +/// lint documentation.
 +///
 +/// The format function will provide strings for the following parameters: `name`, `ty`, `doc` and
 +/// `default`
 +macro_rules! CONFIGURATION_VALUE_TEMPLATE {
 +    () => {
 +        "* `{name}`: `{ty}`: {doc} (defaults to `{default}`)\n"
 +    };
 +}
 +
 +macro_rules! RENAMES_SECTION_TEMPLATE {
 +    () => {
 +        r#"
 +### Past names
 +
 +{names}
 +"#
 +    };
 +}
 +macro_rules! RENAME_VALUE_TEMPLATE {
 +    () => {
 +        "* `{name}`\n"
 +    };
 +}
 +
 +const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [
 +    &["clippy_utils", "diagnostics", "span_lint"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_help"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_note"],
 +    &["clippy_utils", "diagnostics", "span_lint_hir"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_sugg"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_then"],
 +    &["clippy_utils", "diagnostics", "span_lint_hir_and_then"],
 +];
 +const SUGGESTION_DIAGNOSTIC_BUILDER_METHODS: [(&str, bool); 9] = [
 +    ("span_suggestion", false),
 +    ("span_suggestion_short", false),
 +    ("span_suggestion_verbose", false),
 +    ("span_suggestion_hidden", false),
 +    ("tool_only_span_suggestion", false),
 +    ("multipart_suggestion", true),
 +    ("multipart_suggestions", true),
 +    ("tool_only_multipart_suggestion", true),
 +    ("span_suggestions", true),
 +];
 +const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [
 +    &["clippy_utils", "diagnostics", "multispan_sugg"],
 +    &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"],
 +];
 +const DEPRECATED_LINT_TYPE: [&str; 3] = ["clippy_lints", "deprecated_lints", "ClippyDeprecatedLint"];
 +
 +/// The index of the applicability name of `paths::APPLICABILITY_VALUES`
 +const APPLICABILITY_NAME_INDEX: usize = 2;
 +/// This applicability will be set for unresolved applicability values.
 +const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
 +/// The version that will be displayed if none has been defined
 +const VERSION_DEFAULT_STR: &str = "Unknown";
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Collects metadata about clippy lints for the website.
 +    ///
 +    /// This lint will be used to report problems of syntax parsing. You should hopefully never
 +    /// see this but never say never I guess ^^
 +    ///
 +    /// ### Why is this bad?
 +    /// This is not a bad thing but definitely a hacky way to do it. See
 +    /// issue [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) for a discussion
 +    /// about the implementation.
 +    ///
 +    /// ### Known problems
 +    /// Hopefully none. It would be pretty uncool to have a problem here :)
 +    ///
 +    /// ### Example output
 +    /// ```json,ignore
 +    /// {
 +    ///     "id": "internal_metadata_collector",
 +    ///     "id_span": {
 +    ///         "path": "clippy_lints/src/utils/internal_lints/metadata_collector.rs",
 +    ///         "line": 1
 +    ///     },
 +    ///     "group": "clippy::internal",
 +    ///     "docs": " ### What it does\nCollects metadata about clippy lints for the website. [...] "
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.56.0"]
 +    pub INTERNAL_METADATA_COLLECTOR,
 +    internal_warn,
 +    "A busy bee collection metadata about lints"
 +}
 +
 +impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]);
 +
 +#[allow(clippy::module_name_repetitions)]
 +#[derive(Debug, Clone)]
 +pub struct MetadataCollector {
 +    /// All collected lints
 +    ///
 +    /// We use a Heap here to have the lints added in alphabetic order in the export
 +    lints: BinaryHeap<LintMetadata>,
 +    applicability_info: FxHashMap<String, ApplicabilityInfo>,
 +    config: Vec<ClippyConfiguration>,
 +    clippy_project_root: PathBuf,
 +}
 +
 +impl MetadataCollector {
 +    pub fn new() -> Self {
 +        Self {
 +            lints: BinaryHeap::<LintMetadata>::default(),
 +            applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(),
 +            config: collect_configs(),
 +            clippy_project_root: std::env::current_dir()
 +                .expect("failed to get current dir")
 +                .ancestors()
 +                .nth(1)
 +                .expect("failed to get project root")
 +                .to_path_buf(),
 +        }
 +    }
 +
 +    fn get_lint_configs(&self, lint_name: &str) -> Option<String> {
 +        self.config
 +            .iter()
 +            .filter(|config| config.lints.iter().any(|lint| lint == lint_name))
 +            .map(ToString::to_string)
 +            .reduce(|acc, x| acc + &x)
 +            .map(|configurations| format!(CONFIGURATION_SECTION_TEMPLATE!(), configurations = configurations))
 +    }
 +}
 +
 +impl Drop for MetadataCollector {
 +    /// You might ask: How hacky is this?
 +    /// My answer:     YES
 +    fn drop(&mut self) {
 +        // The metadata collector gets dropped twice, this makes sure that we only write
 +        // when the list is full
 +        if self.lints.is_empty() {
 +            return;
 +        }
 +
 +        let mut applicability_info = std::mem::take(&mut self.applicability_info);
 +
 +        // Mapping the final data
 +        let mut lints = std::mem::take(&mut self.lints).into_sorted_vec();
 +        for x in &mut lints {
 +            x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default());
 +            replace_produces(&x.id, &mut x.docs, &self.clippy_project_root);
 +        }
 +
 +        collect_renames(&mut lints);
 +
 +        // Outputting
 +        if Path::new(OUTPUT_FILE).exists() {
 +            fs::remove_file(OUTPUT_FILE).unwrap();
 +        }
 +        let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap();
 +        writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap();
 +    }
 +}
 +
 +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
 +struct LintMetadata {
 +    id: String,
 +    id_span: SerializableSpan,
 +    group: String,
 +    level: String,
 +    docs: String,
 +    version: String,
 +    /// This field is only used in the output and will only be
 +    /// mapped shortly before the actual output.
 +    applicability: Option<ApplicabilityInfo>,
 +}
 +
 +impl LintMetadata {
 +    fn new(
 +        id: String,
 +        id_span: SerializableSpan,
 +        group: String,
 +        level: &'static str,
 +        version: String,
 +        docs: String,
 +    ) -> Self {
 +        Self {
 +            id,
 +            id_span,
 +            group,
 +            level: level.to_string(),
 +            version,
 +            docs,
 +            applicability: None,
 +        }
 +    }
 +}
 +
 +fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Path) {
 +    let mut doc_lines = docs.lines().map(ToString::to_string).collect::<Vec<_>>();
 +    let mut lines = doc_lines.iter_mut();
 +
 +    'outer: loop {
 +        // Find the start of the example
 +
 +        // ```rust
 +        loop {
 +            match lines.next() {
 +                Some(line) if line.trim_start().starts_with("```rust") => {
 +                    if line.contains("ignore") || line.contains("no_run") {
 +                        // A {{produces}} marker may have been put on a ignored code block by mistake,
 +                        // just seek to the end of the code block and continue checking.
 +                        if lines.any(|line| line.trim_start().starts_with("```")) {
 +                            continue;
 +                        }
 +
 +                        panic!("lint `{}` has an unterminated code block", lint_name)
 +                    }
 +
 +                    break;
 +                },
 +                Some(line) if line.trim_start() == "{{produces}}" => {
 +                    panic!(
 +                        "lint `{}` has marker {{{{produces}}}} with an ignored or missing code block",
 +                        lint_name
 +                    )
 +                },
 +                Some(line) => {
 +                    let line = line.trim();
 +                    // These are the two most common markers of the corrections section
 +                    if line.eq_ignore_ascii_case("Use instead:") || line.eq_ignore_ascii_case("Could be written as:") {
 +                        break 'outer;
 +                    }
 +                },
 +                None => break 'outer,
 +            }
 +        }
 +
 +        // Collect the example
 +        let mut example = Vec::new();
 +        loop {
 +            match lines.next() {
 +                Some(line) if line.trim_start() == "```" => break,
 +                Some(line) => example.push(line),
 +                None => panic!("lint `{}` has an unterminated code block", lint_name),
 +            }
 +        }
 +
 +        // Find the {{produces}} and attempt to generate the output
 +        loop {
 +            match lines.next() {
 +                Some(line) if line.is_empty() => {},
 +                Some(line) if line.trim() == "{{produces}}" => {
 +                    let output = get_lint_output(lint_name, &example, clippy_project_root);
 +                    line.replace_range(
 +                        ..,
 +                        &format!(
 +                            "<details>\
 +                            <summary>Produces</summary>\n\
 +                            \n\
 +                            ```text\n\
 +                            {}\n\
 +                            ```\n\
 +                        </details>",
 +                            output
 +                        ),
 +                    );
 +
 +                    break;
 +                },
 +                // No {{produces}}, we can move on to the next example
 +                Some(_) => break,
 +                None => break 'outer,
 +            }
 +        }
 +    }
 +
 +    *docs = cleanup_docs(&doc_lines);
 +}
 +
 +fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root: &Path) -> String {
 +    let dir = tempfile::tempdir().unwrap_or_else(|e| panic!("failed to create temp dir: {e}"));
 +    let file = dir.path().join("lint_example.rs");
 +
 +    let mut source = String::new();
 +    let unhidden = example
 +        .iter()
 +        .map(|line| line.trim_start().strip_prefix("# ").unwrap_or(line));
 +
 +    // Get any attributes
 +    let mut lines = unhidden.peekable();
 +    while let Some(line) = lines.peek() {
 +        if line.starts_with("#!") {
 +            source.push_str(line);
 +            source.push('\n');
 +            lines.next();
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    let needs_main = !example.iter().any(|line| line.contains("fn main"));
 +    if needs_main {
 +        source.push_str("fn main() {\n");
 +    }
 +
 +    for line in lines {
 +        source.push_str(line);
 +        source.push('\n');
 +    }
 +
 +    if needs_main {
 +        source.push_str("}\n");
 +    }
 +
 +    if let Err(e) = fs::write(&file, &source) {
 +        panic!("failed to write to `{}`: {e}", file.as_path().to_string_lossy());
 +    }
 +
 +    let prefixed_name = format!("{}{lint_name}", CLIPPY_LINT_GROUP_PREFIX);
 +
 +    let mut cmd = Command::new("cargo");
 +
 +    cmd.current_dir(clippy_project_root)
 +        .env("CARGO_INCREMENTAL", "0")
 +        .env("CLIPPY_ARGS", "")
 +        .env("CLIPPY_DISABLE_DOCS_LINKS", "1")
 +        // We need to disable this to enable all lints
 +        .env("ENABLE_METADATA_COLLECTION", "0")
 +        .args(["run", "--bin", "clippy-driver"])
 +        .args(["--target-dir", "./clippy_lints/target"])
 +        .args(["--", "--error-format=json"])
 +        .args(["--edition", "2021"])
 +        .arg("-Cdebuginfo=0")
 +        .args(["-A", "clippy::all"])
 +        .args(["-W", &prefixed_name])
 +        .args(["-L", "./target/debug"])
 +        .args(["-Z", "no-codegen"]);
 +
 +    let output = cmd
 +        .arg(file.as_path())
 +        .output()
 +        .unwrap_or_else(|e| panic!("failed to run `{:?}`: {e}", cmd));
 +
 +    let tmp_file_path = file.to_string_lossy();
 +    let stderr = std::str::from_utf8(&output.stderr).unwrap();
 +    let msgs = stderr
 +        .lines()
 +        .filter(|line| line.starts_with('{'))
 +        .map(|line| serde_json::from_str(line).unwrap())
 +        .collect::<Vec<serde_json::Value>>();
 +
 +    let mut rendered = String::new();
 +    let iter = msgs
 +        .iter()
 +        .filter(|msg| matches!(&msg["code"]["code"], serde_json::Value::String(s) if s == &prefixed_name));
 +
 +    for message in iter {
 +        let rendered_part = message["rendered"].as_str().expect("rendered field should exist");
 +        rendered.push_str(rendered_part);
 +    }
 +
 +    if rendered.is_empty() {
 +        let rendered: Vec<&str> = msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect();
 +        let non_json: Vec<&str> = stderr.lines().filter(|line| !line.starts_with('{')).collect();
 +        panic!(
 +            "did not find lint `{}` in output of example, got:\n{}\n{}",
 +            lint_name,
 +            non_json.join("\n"),
 +            rendered.join("\n")
 +        );
 +    }
 +
 +    // The reader doesn't need to see `/tmp/.tmpfiy2Qd/lint_example.rs` :)
 +    rendered.trim_end().replace(&*tmp_file_path, "lint_example.rs")
 +}
 +
 +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
 +struct SerializableSpan {
 +    path: String,
 +    line: usize,
 +}
 +
 +impl fmt::Display for SerializableSpan {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line)
 +    }
 +}
 +
 +impl SerializableSpan {
 +    fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self {
 +        Self::from_span(cx, item.ident.span)
 +    }
 +
 +    fn from_span(cx: &LateContext<'_>, span: Span) -> Self {
 +        let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo());
 +
 +        Self {
 +            path: format!("{}", loc.file.name.prefer_remapped()),
 +            line: loc.line,
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
 +struct ApplicabilityInfo {
 +    /// Indicates if any of the lint emissions uses multiple spans. This is related to
 +    /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can
 +    /// currently not be applied automatically.
 +    is_multi_part_suggestion: bool,
 +    applicability: Option<usize>,
 +}
 +
 +impl Serialize for ApplicabilityInfo {
 +    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 +    where
 +        S: Serializer,
 +    {
 +        let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?;
 +        s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?;
 +        if let Some(index) = self.applicability {
 +            s.serialize_field(
 +                "applicability",
 +                &paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX],
 +            )?;
 +        } else {
 +            s.serialize_field("applicability", APPLICABILITY_UNRESOLVED_STR)?;
 +        }
 +        s.end()
 +    }
 +}
 +
 +// ==================================================================
 +// Configuration
 +// ==================================================================
 +#[derive(Debug, Clone, Default)]
 +pub struct ClippyConfiguration {
 +    name: String,
 +    config_type: &'static str,
 +    default: String,
 +    lints: Vec<String>,
 +    doc: String,
 +    #[allow(dead_code)]
 +    deprecation_reason: Option<&'static str>,
 +}
 +
 +impl ClippyConfiguration {
 +    pub fn new(
 +        name: &'static str,
 +        config_type: &'static str,
 +        default: String,
 +        doc_comment: &'static str,
 +        deprecation_reason: Option<&'static str>,
 +    ) -> Self {
 +        let (lints, doc) = parse_config_field_doc(doc_comment)
 +            .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
 +
 +        Self {
 +            name: to_kebab(name),
 +            lints,
 +            doc,
 +            config_type,
 +            default,
 +            deprecation_reason,
 +        }
 +    }
 +}
 +
 +fn collect_configs() -> Vec<ClippyConfiguration> {
 +    crate::utils::conf::metadata::get_configuration_metadata()
 +}
 +
 +/// This parses the field documentation of the config struct.
 +///
 +/// ```rust, ignore
 +/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
 +/// ```
 +///
 +/// Would yield:
 +/// ```rust, ignore
 +/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
 +/// ```
 +fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
 +    const DOC_START: &str = " Lint: ";
 +    if_chain! {
 +        if doc_comment.starts_with(DOC_START);
 +        if let Some(split_pos) = doc_comment.find('.');
 +        then {
 +            let mut doc_comment = doc_comment.to_string();
 +            let mut documentation = doc_comment.split_off(split_pos);
 +
 +            // Extract lints
 +            doc_comment.make_ascii_lowercase();
 +            let lints: Vec<String> = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect();
 +
 +            // Format documentation correctly
 +            // split off leading `.` from lint name list and indent for correct formatting
 +            documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n    ");
 +
 +            Some((lints, documentation))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
 +fn to_kebab(config_name: &str) -> String {
 +    config_name.replace('_', "-")
 +}
 +
 +impl fmt::Display for ClippyConfiguration {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
 +        write!(
 +            f,
 +            CONFIGURATION_VALUE_TEMPLATE!(),
 +            name = self.name,
 +            ty = self.config_type,
 +            doc = self.doc,
 +            default = self.default
 +        )
 +    }
 +}
 +
 +// ==================================================================
 +// Lint pass
 +// ==================================================================
 +impl<'hir> LateLintPass<'hir> for MetadataCollector {
 +    /// Collecting lint declarations like:
 +    /// ```rust, ignore
 +    /// declare_clippy_lint! {
 +    ///     /// ### What it does
 +    ///     /// Something IDK.
 +    ///     pub SOME_LINT,
 +    ///     internal,
 +    ///     "Who am I?"
 +    /// }
 +    /// ```
 +    fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) {
 +        if let ItemKind::Static(ty, Mutability::Not, _) = item.kind {
 +            // Normal lint
 +            if_chain! {
 +                // item validation
 +                if is_lint_ref_type(cx, ty);
 +                // blacklist check
 +                let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
 +                if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
 +                // metadata extraction
 +                if let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item);
 +                if let Some(mut 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);
 +                // blacklist check
 +                let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
 +                if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
 +                // Metadata the little we can get from a deprecated lint
 +                if let Some(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),
 +        &[Ident::with_dummy_span(sym::clippy)].into_iter().collect(),
 +    );
 +    if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
 +        if let Some(group) = get_lint_group(cx, lint_lst[0]) {
 +            if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
 +                return None;
 +            }
 +
 +            if let Some(level) = get_lint_level_from_group(&group) {
 +                Some((group, level))
 +            } else {
 +                lint_collection_error_item(
 +                    cx,
 +                    item,
 +                    &format!("Unable to determine lint level for found group `{}`", group),
 +                );
 +                None
 +            }
 +        } else {
 +            lint_collection_error_item(cx, item, "Unable to determine lint group");
 +            None
 +        }
 +    } else {
 +        lint_collection_error_item(cx, item, "Unable to find lint in lint_store");
 +        None
 +    }
 +}
 +
 +fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
 +    for (group_name, lints, _) in cx.lint_store.get_lint_groups() {
 +        if IGNORED_LINT_GROUPS.contains(&group_name) {
 +            continue;
 +        }
 +
 +        if lints.iter().any(|group_lint| *group_lint == lint_id) {
 +            let group = group_name.strip_prefix(CLIPPY_LINT_GROUP_PREFIX).unwrap_or(group_name);
 +            return Some((*group).to_string());
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
 +    DEFAULT_LINT_LEVELS
 +        .iter()
-     if let ExprKind::Closure { body, .. } = closure_expr.kind {
++        .find_map(|(group_name, group_level)| (*group_name == lint_group).then_some(*group_level))
 +}
 +
 +pub(super) fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
 +    if let hir::TyKind::Path(ref path) = ty.kind {
 +        if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) {
 +            return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE);
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn collect_renames(lints: &mut Vec<LintMetadata>) {
 +    for lint in lints {
 +        let mut collected = String::new();
 +        let mut names = vec![lint.id.clone()];
 +
 +        loop {
 +            if let Some(lint_name) = names.pop() {
 +                for (k, v) in RENAMED_LINTS {
 +                    if_chain! {
 +                        if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX);
 +                        if name == lint_name;
 +                        if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX);
 +                        then {
 +                            write!(collected, RENAME_VALUE_TEMPLATE!(), name = past_name).unwrap();
 +                            names.push(past_name.to_string());
 +                        }
 +                    }
 +                }
 +
 +                continue;
 +            }
 +
 +            break;
 +        }
 +
 +        if !collected.is_empty() {
 +            write!(&mut lint.docs, RENAMES_SECTION_TEMPLATE!(), names = collected).unwrap();
 +        }
 +    }
 +}
 +
 +// ==================================================================
 +// Lint emission
 +// ==================================================================
 +fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) {
 +    span_lint(
 +        cx,
 +        INTERNAL_METADATA_COLLECTOR,
 +        item.ident.span,
 +        &format!("metadata collection error for `{}`: {}", item.ident.name, message),
 +    );
 +}
 +
 +// ==================================================================
 +// Applicability
 +// ==================================================================
 +/// This function checks if a given expression is equal to a simple lint emission function call.
 +/// It will return the function arguments if the emission matched any function.
 +fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) -> Option<&'hir [hir::Expr<'hir>]> {
 +    LINT_EMISSION_FUNCTIONS
 +        .iter()
 +        .find_map(|emission_fn| match_function_call(cx, expr, emission_fn))
 +}
 +
 +fn take_higher_applicability(a: Option<usize>, b: Option<usize>) -> Option<usize> {
 +    a.map_or(b, |a| a.max(b.unwrap_or_default()).into())
 +}
 +
 +fn extract_emission_info<'hir>(
 +    cx: &LateContext<'hir>,
 +    args: &'hir [hir::Expr<'hir>],
 +) -> Vec<(String, Option<usize>, bool)> {
 +    let mut lints = Vec::new();
 +    let mut applicability = None;
 +    let mut multi_part = false;
 +
 +    for arg in args {
 +        let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(arg));
 +
 +        if match_type(cx, arg_ty, &paths::LINT) {
 +            // If we found the lint arg, extract the lint name
 +            let mut resolved_lints = resolve_lints(cx, arg);
 +            lints.append(&mut resolved_lints);
 +        } else if match_type(cx, arg_ty, &paths::APPLICABILITY) {
 +            applicability = resolve_applicability(cx, arg);
 +        } else if arg_ty.is_closure() {
 +            multi_part |= check_is_multi_part(cx, arg);
 +            applicability = applicability.or_else(|| resolve_applicability(cx, arg));
 +        }
 +    }
 +
 +    lints
 +        .into_iter()
 +        .map(|lint_name| (lint_name, applicability, multi_part))
 +        .collect()
 +}
 +
 +/// Resolves the possible lints that this expression could reference
 +fn resolve_lints<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> {
 +    let mut resolver = LintResolver::new(cx);
 +    resolver.visit_expr(expr);
 +    resolver.lints
 +}
 +
 +/// This function tries to resolve the linked applicability to the given expression.
 +fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> {
 +    let mut resolver = ApplicabilityResolver::new(cx);
 +    resolver.visit_expr(expr);
 +    resolver.complete()
 +}
 +
 +fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
-     /// This is the index of hightest `Applicability` for `paths::APPLICABILITY_VALUES`
++    if let ExprKind::Closure(&Closure { body, .. }) = closure_expr.kind {
 +        let mut scanner = IsMultiSpanScanner::new(cx);
 +        intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body));
 +        return scanner.is_multi_part();
 +    } else if let Some(local) = get_parent_local(cx, closure_expr) {
 +        if let Some(local_init) = local.init {
 +            return check_is_multi_part(cx, local_init);
 +        }
 +    }
 +
 +    false
 +}
 +
 +struct LintResolver<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    lints: Vec<String>,
 +}
 +
 +impl<'a, 'hir> LintResolver<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            lints: Vec::<String>::default(),
 +        }
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        if_chain! {
 +            if let ExprKind::Path(qpath) = &expr.kind;
 +            if let QPath::Resolved(_, path) = qpath;
 +
 +            let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
 +            if match_type(self.cx, expr_ty, &paths::LINT);
 +            then {
 +                if let hir::def::Res::Def(DefKind::Static(..), _) = path.res {
 +                    let lint_name = last_path_segment(qpath).ident.name;
 +                    self.lints.push(sym_to_string(lint_name).to_ascii_lowercase());
 +                } else if let Some(local) = get_parent_local(self.cx, expr) {
 +                    if let Some(local_init) = local.init {
 +                        intravisit::walk_expr(self, local_init);
 +                    }
 +                }
 +            }
 +        }
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
 +
 +/// This visitor finds the highest applicability value in the visited expressions
 +struct ApplicabilityResolver<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
++    /// This is the index of highest `Applicability` for `paths::APPLICABILITY_VALUES`
 +    applicability_index: Option<usize>,
 +}
 +
 +impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            applicability_index: None,
 +        }
 +    }
 +
 +    fn add_new_index(&mut self, new_index: usize) {
 +        self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index));
 +    }
 +
 +    fn complete(self) -> Option<usize> {
 +        self.applicability_index
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_path(&mut self, path: &'hir hir::Path<'hir>, _id: hir::HirId) {
 +        for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() {
 +            if match_path(path, enum_value) {
 +                self.add_new_index(index);
 +                return;
 +            }
 +        }
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
 +
 +        if_chain! {
 +            if match_type(self.cx, expr_ty, &paths::APPLICABILITY);
 +            if let Some(local) = get_parent_local(self.cx, expr);
 +            if let Some(local_init) = local.init;
 +            then {
 +                intravisit::walk_expr(self, local_init);
 +            }
 +        };
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
 +
 +/// This returns the parent local node if the expression is a reference one
 +fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> {
 +    if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
 +        if let hir::def::Res::Local(local_hir) = path.res {
 +            return get_parent_local_hir_id(cx, local_hir);
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> {
 +    let map = cx.tcx.hir();
 +
 +    match map.find(map.get_parent_node(hir_id)) {
 +        Some(hir::Node::Local(local)) => Some(local),
 +        Some(hir::Node::Pat(pattern)) => get_parent_local_hir_id(cx, pattern.hir_id),
 +        _ => None,
 +    }
 +}
 +
 +/// This visitor finds the highest applicability value in the visited expressions
 +struct IsMultiSpanScanner<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    suggestion_count: usize,
 +}
 +
 +impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            suggestion_count: 0,
 +        }
 +    }
 +
 +    /// Add a new single expression suggestion to the counter
 +    fn add_single_span_suggestion(&mut self) {
 +        self.suggestion_count += 1;
 +    }
 +
 +    /// Signals that a suggestion with possible multiple spans was found
 +    fn add_multi_part_suggestion(&mut self) {
 +        self.suggestion_count += 2;
 +    }
 +
 +    /// Checks if the suggestions include multiple spans
 +    fn is_multi_part(&self) -> bool {
 +        self.suggestion_count > 1
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        // Early return if the lint is already multi span
 +        if self.is_multi_part() {
 +            return;
 +        }
 +
 +        match &expr.kind {
 +            ExprKind::Call(fn_expr, _args) => {
 +                let found_function = SUGGESTION_FUNCTIONS
 +                    .iter()
 +                    .any(|func_path| match_function_call(self.cx, fn_expr, func_path).is_some());
 +                if found_function {
 +                    // These functions are all multi part suggestions
 +                    self.add_single_span_suggestion();
 +                }
 +            },
 +            ExprKind::MethodCall(path, arg, _arg_span) => {
 +                let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&arg[0]));
 +                if match_type(self.cx, self_ty, &paths::DIAGNOSTIC_BUILDER) {
 +                    let called_method = path.ident.name.as_str().to_string();
 +                    for (method_name, is_multi_part) in &SUGGESTION_DIAGNOSTIC_BUILDER_METHODS {
 +                        if *method_name == called_method {
 +                            if *is_multi_part {
 +                                self.add_multi_part_suggestion();
 +                            } else {
 +                                self.add_single_span_suggestion();
 +                            }
 +                            break;
 +                        }
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
index 67b2bc8c3f3cd86e823868a0475f083ea535f659,0000000000000000000000000000000000000000..08b8894752011c86a51f8ea36aa15eba82a19cd8
mode 100644,000000..100644
--- /dev/null
@@@ -1,709 -1,0 +1,709 @@@
-         parser.errors.is_empty().then(move || args)
 +use std::borrow::Cow;
 +use std::iter;
 +use std::ops::{Deref, Range};
 +
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::{snippet_opt, snippet_with_applicability};
 +use rustc_ast::ast::{Expr, ExprKind, Impl, Item, ItemKind, MacCall, Path, StrLit, StrStyle};
 +use rustc_ast::token::{self, LitKind};
 +use rustc_ast::tokenstream::TokenStream;
 +use rustc_errors::{Applicability, DiagnosticBuilder};
 +use rustc_lexer::unescape::{self, EscapeError};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 +use rustc_parse::parser;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::{kw, Symbol};
 +use rustc_span::{sym, BytePos, InnerSpan, Span, DUMMY_SP};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns when you use `println!("")` to
 +    /// print a newline.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `println!()`, which is simpler.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// println!("");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// println!();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub PRINTLN_EMPTY_STRING,
 +    style,
 +    "using `println!(\"\")` with an empty string"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns when you use `print!()` with a format
 +    /// string that ends in a newline.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `println!()` instead, which appends the
 +    /// newline.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let name = "World";
 +    /// print!("Hello {}!\n", name);
 +    /// ```
 +    /// use println!() instead
 +    /// ```rust
 +    /// # let name = "World";
 +    /// println!("Hello {}!", name);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub PRINT_WITH_NEWLINE,
 +    style,
 +    "using `print!()` with a format string that ends in a single newline"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for printing on *stdout*. The purpose of this lint
 +    /// is to catch debugging remnants.
 +    ///
 +    /// ### Why is this bad?
 +    /// People often print on *stdout* while debugging an
 +    /// application and might forget to remove those prints afterward.
 +    ///
 +    /// ### Known problems
 +    /// * Only catches `print!` and `println!` calls.
 +    /// * The lint level is unaffected by crate attributes. The level can still
 +    ///   be set for functions, modules and other items. To change the level for
 +    ///   the entire crate, please use command line flags. More information and a
 +    ///   configuration example can be found in [clippy#6610].
 +    ///
 +    /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// println!("Hello world!");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub PRINT_STDOUT,
 +    restriction,
 +    "printing on stdout"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for printing on *stderr*. The purpose of this lint
 +    /// is to catch debugging remnants.
 +    ///
 +    /// ### Why is this bad?
 +    /// People often print on *stderr* while debugging an
 +    /// application and might forget to remove those prints afterward.
 +    ///
 +    /// ### Known problems
 +    /// * Only catches `eprint!` and `eprintln!` calls.
 +    /// * The lint level is unaffected by crate attributes. The level can still
 +    ///   be set for functions, modules and other items. To change the level for
 +    ///   the entire crate, please use command line flags. More information and a
 +    ///   configuration example can be found in [clippy#6610].
 +    ///
 +    /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// eprintln!("Hello world!");
 +    /// ```
 +    #[clippy::version = "1.50.0"]
 +    pub PRINT_STDERR,
 +    restriction,
 +    "printing on stderr"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `Debug` formatting. The purpose of this
 +    /// lint is to catch debugging remnants.
 +    ///
 +    /// ### Why is this bad?
 +    /// The purpose of the `Debug` trait is to facilitate
 +    /// debugging Rust code. It should not be used in user-facing output.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = "bar";
 +    /// println!("{:?}", foo);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USE_DEBUG,
 +    restriction,
 +    "use of `Debug`-based formatting"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns about the use of literals as `print!`/`println!` args.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using literals as `println!` args is inefficient
 +    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
 +    /// (i.e., just put the literal in the format string)
 +    ///
 +    /// ### Known problems
 +    /// Will also warn with macro calls as arguments that expand to literals
 +    /// -- e.g., `println!("{}", env!("FOO"))`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// println!("{}", "foo");
 +    /// ```
 +    /// use the literal without formatting:
 +    /// ```rust
 +    /// println!("foo");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub PRINT_LITERAL,
 +    style,
 +    "printing a literal with a format string"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns when you use `writeln!(buf, "")` to
 +    /// print a newline.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `writeln!(buf)`, which is simpler.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// writeln!(buf, "");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// writeln!(buf);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRITELN_EMPTY_STRING,
 +    style,
 +    "using `writeln!(buf, \"\")` with an empty string"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns when you use `write!()` with a format
 +    /// string that
 +    /// ends in a newline.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `writeln!()` instead, which appends the
 +    /// newline.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// # let name = "World";
 +    /// write!(buf, "Hello {}!\n", name);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// # let name = "World";
 +    /// writeln!(buf, "Hello {}!", name);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRITE_WITH_NEWLINE,
 +    style,
 +    "using `write!()` with a format string that ends in a single newline"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns about the use of literals as `write!`/`writeln!` args.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using literals as `writeln!` args is inefficient
 +    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
 +    /// (i.e., just put the literal in the format string)
 +    ///
 +    /// ### Known problems
 +    /// Will also warn with macro calls as arguments that expand to literals
 +    /// -- e.g., `writeln!(buf, "{}", env!("FOO"))`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// writeln!(buf, "{}", "foo");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// writeln!(buf, "foo");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRITE_LITERAL,
 +    style,
 +    "writing a literal with a format string"
 +}
 +
 +#[derive(Default)]
 +pub struct Write {
 +    in_debug_impl: bool,
 +}
 +
 +impl_lint_pass!(Write => [
 +    PRINT_WITH_NEWLINE,
 +    PRINTLN_EMPTY_STRING,
 +    PRINT_STDOUT,
 +    PRINT_STDERR,
 +    USE_DEBUG,
 +    PRINT_LITERAL,
 +    WRITE_WITH_NEWLINE,
 +    WRITELN_EMPTY_STRING,
 +    WRITE_LITERAL
 +]);
 +
 +impl EarlyLintPass for Write {
 +    fn check_item(&mut self, _: &EarlyContext<'_>, item: &Item) {
 +        if let ItemKind::Impl(box Impl {
 +            of_trait: Some(trait_ref),
 +            ..
 +        }) = &item.kind
 +        {
 +            let trait_name = trait_ref
 +                .path
 +                .segments
 +                .iter()
 +                .last()
 +                .expect("path has at least one segment")
 +                .ident
 +                .name;
 +            if trait_name == sym::Debug {
 +                self.in_debug_impl = true;
 +            }
 +        }
 +    }
 +
 +    fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &Item) {
 +        self.in_debug_impl = false;
 +    }
 +
 +    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
 +        fn is_build_script(cx: &EarlyContext<'_>) -> bool {
 +            // Cargo sets the crate name for build scripts to `build_script_build`
 +            cx.sess()
 +                .opts
 +                .crate_name
 +                .as_ref()
 +                .map_or(false, |crate_name| crate_name == "build_script_build")
 +        }
 +
 +        if mac.path == sym!(print) {
 +            if !is_build_script(cx) {
 +                span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
 +            }
 +            self.lint_print_with_newline(cx, mac);
 +        } else if mac.path == sym!(println) {
 +            if !is_build_script(cx) {
 +                span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
 +            }
 +            self.lint_println_empty_string(cx, mac);
 +        } else if mac.path == sym!(eprint) {
 +            span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`");
 +            self.lint_print_with_newline(cx, mac);
 +        } else if mac.path == sym!(eprintln) {
 +            span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`");
 +            self.lint_println_empty_string(cx, mac);
 +        } else if mac.path == sym!(write) {
 +            if let (Some(fmt_str), dest) = self.check_tts(cx, mac.args.inner_tokens(), true) {
 +                if check_newlines(&fmt_str) {
 +                    let (nl_span, only_nl) = newline_span(&fmt_str);
 +                    let nl_span = match (dest, only_nl) {
 +                        // Special case of `write!(buf, "\n")`: Mark everything from the end of
 +                        // `buf` for removal so no trailing comma [`writeln!(buf, )`] remains.
 +                        (Some(dest_expr), true) => nl_span.with_lo(dest_expr.span.hi()),
 +                        _ => nl_span,
 +                    };
 +                    span_lint_and_then(
 +                        cx,
 +                        WRITE_WITH_NEWLINE,
 +                        mac.span(),
 +                        "using `write!()` with a format string that ends in a single newline",
 +                        |err| {
 +                            err.multipart_suggestion(
 +                                "use `writeln!()` instead",
 +                                vec![(mac.path.span, String::from("writeln")), (nl_span, String::new())],
 +                                Applicability::MachineApplicable,
 +                            );
 +                        },
 +                    );
 +                }
 +            }
 +        } else if mac.path == sym!(writeln) {
 +            if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) {
 +                if fmt_str.symbol == kw::Empty {
 +                    let mut applicability = Applicability::MachineApplicable;
 +                    let suggestion = if let Some(e) = expr {
 +                        snippet_with_applicability(cx, e.span, "v", &mut applicability)
 +                    } else {
 +                        applicability = Applicability::HasPlaceholders;
 +                        Cow::Borrowed("v")
 +                    };
 +
 +                    span_lint_and_sugg(
 +                        cx,
 +                        WRITELN_EMPTY_STRING,
 +                        mac.span(),
 +                        format!("using `writeln!({}, \"\")`", suggestion).as_str(),
 +                        "replace it with",
 +                        format!("writeln!({})", suggestion),
 +                        applicability,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Given a format string that ends in a newline and its span, calculates the span of the
 +/// newline, or the format string itself if the format string consists solely of a newline.
 +/// Return this and a boolean indicating whether it only consisted of a newline.
 +fn newline_span(fmtstr: &StrLit) -> (Span, bool) {
 +    let sp = fmtstr.span;
 +    let contents = fmtstr.symbol.as_str();
 +
 +    if contents == r"\n" {
 +        return (sp, true);
 +    }
 +
 +    let newline_sp_hi = sp.hi()
 +        - match fmtstr.style {
 +            StrStyle::Cooked => BytePos(1),
 +            StrStyle::Raw(hashes) => BytePos((1 + hashes).into()),
 +        };
 +
 +    let newline_sp_len = if contents.ends_with('\n') {
 +        BytePos(1)
 +    } else if contents.ends_with(r"\n") {
 +        BytePos(2)
 +    } else {
 +        panic!("expected format string to contain a newline");
 +    };
 +
 +    (sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi), false)
 +}
 +
 +/// Stores a list of replacement spans for each argument, but only if all the replacements used an
 +/// empty format string.
 +#[derive(Default)]
 +struct SimpleFormatArgs {
 +    unnamed: Vec<Vec<Span>>,
 +    named: Vec<(Symbol, Vec<Span>)>,
 +}
 +impl SimpleFormatArgs {
 +    fn get_unnamed(&self) -> impl Iterator<Item = &[Span]> {
 +        self.unnamed.iter().map(|x| match x.as_slice() {
 +            // Ignore the dummy span added from out of order format arguments.
 +            [DUMMY_SP] => &[],
 +            x => x,
 +        })
 +    }
 +
 +    fn get_named(&self, n: &Path) -> &[Span] {
 +        self.named.iter().find(|x| *n == x.0).map_or(&[], |x| x.1.as_slice())
 +    }
 +
 +    fn push(&mut self, arg: rustc_parse_format::Argument<'_>, span: Span) {
 +        use rustc_parse_format::{
 +            AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec,
 +        };
 +
 +        const SIMPLE: FormatSpec<'_> = FormatSpec {
 +            fill: None,
 +            align: AlignUnknown,
 +            flags: 0,
 +            precision: CountImplied,
 +            precision_span: None,
 +            width: CountImplied,
 +            width_span: None,
 +            ty: "",
 +            ty_span: None,
 +        };
 +
 +        match arg.position {
 +            ArgumentIs(n) | ArgumentImplicitlyIs(n) => {
 +                if self.unnamed.len() <= n {
 +                    // Use a dummy span to mark all unseen arguments.
 +                    self.unnamed.resize_with(n, || vec![DUMMY_SP]);
 +                    if arg.format == SIMPLE {
 +                        self.unnamed.push(vec![span]);
 +                    } else {
 +                        self.unnamed.push(Vec::new());
 +                    }
 +                } else {
 +                    let args = &mut self.unnamed[n];
 +                    match (args.as_mut_slice(), arg.format == SIMPLE) {
 +                        // A non-empty format string has been seen already.
 +                        ([], _) => (),
 +                        // Replace the dummy span, if it exists.
 +                        ([dummy @ DUMMY_SP], true) => *dummy = span,
 +                        ([_, ..], true) => args.push(span),
 +                        ([_, ..], false) => *args = Vec::new(),
 +                    }
 +                }
 +            },
 +            ArgumentNamed(n, _) => {
 +                let n = Symbol::intern(n);
 +                if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) {
 +                    match x.1.as_slice() {
 +                        // A non-empty format string has been seen already.
 +                        [] => (),
 +                        [_, ..] if arg.format == SIMPLE => x.1.push(span),
 +                        [_, ..] => x.1 = Vec::new(),
 +                    }
 +                } else if arg.format == SIMPLE {
 +                    self.named.push((n, vec![span]));
 +                } else {
 +                    self.named.push((n, Vec::new()));
 +                }
 +            },
 +        };
 +    }
 +}
 +
 +impl Write {
 +    /// Parses a format string into a collection of spans for each argument. This only keeps track
 +    /// of empty format arguments. Will also lint usages of debug format strings outside of debug
 +    /// impls.
 +    fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str_lit: &StrLit) -> Option<SimpleFormatArgs> {
 +        use rustc_parse_format::{ParseMode, Parser, Piece};
 +
 +        let str_sym = str_lit.symbol_unescaped.as_str();
 +        let style = match str_lit.style {
 +            StrStyle::Cooked => None,
 +            StrStyle::Raw(n) => Some(n as usize),
 +        };
 +
 +        let mut parser = Parser::new(str_sym, style, snippet_opt(cx, str_lit.span), false, ParseMode::Format);
 +        let mut args = SimpleFormatArgs::default();
 +
 +        while let Some(arg) = parser.next() {
 +            let arg = match arg {
 +                Piece::String(_) => continue,
 +                Piece::NextArgument(arg) => arg,
 +            };
 +            let span = parser
 +                .arg_places
 +                .last()
 +                .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(InnerSpan::new(x.start, x.end)));
 +
 +            if !self.in_debug_impl && arg.format.ty == "?" {
 +                // FIXME: modify rustc's fmt string parser to give us the current span
 +                span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
 +            }
 +
 +            args.push(arg, span);
 +        }
 +
++        parser.errors.is_empty().then_some(args)
 +    }
 +
 +    /// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
 +    /// `Option`s. The first `Option` of the tuple is the macro's format string. It includes
 +    /// the contents of the string, whether it's a raw string, and the span of the literal in the
 +    /// source. The second `Option` in the tuple is, in the `write[ln]!` case, the expression the
 +    /// `format_str` should be written to.
 +    ///
 +    /// Example:
 +    ///
 +    /// Calling this function on
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// # let something = "something";
 +    /// writeln!(buf, "string to write: {}", something);
 +    /// ```
 +    /// will return
 +    /// ```rust,ignore
 +    /// (Some("string to write: {}"), Some(buf))
 +    /// ```
 +    fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option<StrLit>, Option<Expr>) {
 +        let mut parser = parser::Parser::new(&cx.sess().parse_sess, tts, false, None);
 +        let expr = if is_write {
 +            match parser
 +                .parse_expr()
 +                .map(rustc_ast::ptr::P::into_inner)
 +                .map_err(DiagnosticBuilder::cancel)
 +            {
 +                // write!(e, ...)
 +                Ok(p) if parser.eat(&token::Comma) => Some(p),
 +                // write!(e) or error
 +                e => return (None, e.ok()),
 +            }
 +        } else {
 +            None
 +        };
 +
 +        let fmtstr = match parser.parse_str_lit() {
 +            Ok(fmtstr) => fmtstr,
 +            Err(_) => return (None, expr),
 +        };
 +
 +        let args = match self.parse_fmt_string(cx, &fmtstr) {
 +            Some(args) => args,
 +            None => return (Some(fmtstr), expr),
 +        };
 +
 +        let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
 +        let mut unnamed_args = args.get_unnamed();
 +        loop {
 +            if !parser.eat(&token::Comma) {
 +                return (Some(fmtstr), expr);
 +            }
 +
 +            let comma_span = parser.prev_token.span;
 +            let token_expr = if let Ok(expr) = parser.parse_expr().map_err(DiagnosticBuilder::cancel) {
 +                expr
 +            } else {
 +                return (Some(fmtstr), None);
 +            };
 +            let (fmt_spans, lit) = match &token_expr.kind {
 +                ExprKind::Lit(lit) => (unnamed_args.next().unwrap_or(&[]), lit),
 +                ExprKind::Assign(lhs, rhs, _) => match (&lhs.kind, &rhs.kind) {
 +                    (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit),
 +                    _ => continue,
 +                },
 +                _ => {
 +                    unnamed_args.next();
 +                    continue;
 +                },
 +            };
 +
 +            let replacement: String = match lit.token.kind {
 +                LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => {
 +                    lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}")
 +                },
 +                LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => {
 +                    lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}")
 +                },
 +                LitKind::StrRaw(_)
 +                | LitKind::Str
 +                | LitKind::ByteStrRaw(_)
 +                | LitKind::ByteStr
 +                | LitKind::Integer
 +                | LitKind::Float
 +                | LitKind::Err => continue,
 +                LitKind::Byte | LitKind::Char => match lit.token.symbol.as_str() {
 +                    "\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"",
 +                    "\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue,
 +                    "\\\\" if matches!(fmtstr.style, StrStyle::Raw(_)) => "\\",
 +                    "\\'" => "'",
 +                    "{" => "{{",
 +                    "}" => "}}",
 +                    x if matches!(fmtstr.style, StrStyle::Raw(_)) && x.starts_with('\\') => continue,
 +                    x => x,
 +                }
 +                .into(),
 +                LitKind::Bool => lit.token.symbol.as_str().deref().into(),
 +            };
 +
 +            if !fmt_spans.is_empty() {
 +                span_lint_and_then(
 +                    cx,
 +                    lint,
 +                    token_expr.span,
 +                    "literal with an empty format string",
 +                    |diag| {
 +                        diag.multipart_suggestion(
 +                            "try this",
 +                            iter::once((comma_span.to(token_expr.span), String::new()))
 +                                .chain(fmt_spans.iter().copied().zip(iter::repeat(replacement)))
 +                                .collect(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                );
 +            }
 +        }
 +    }
 +
 +    fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
 +        if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
 +            if fmt_str.symbol == kw::Empty {
 +                let name = mac.path.segments[0].ident.name;
 +                span_lint_and_sugg(
 +                    cx,
 +                    PRINTLN_EMPTY_STRING,
 +                    mac.span(),
 +                    &format!("using `{}!(\"\")`", name),
 +                    "replace it with",
 +                    format!("{}!()", name),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +
 +    fn lint_print_with_newline(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
 +        if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
 +            if check_newlines(&fmt_str) {
 +                let name = mac.path.segments[0].ident.name;
 +                let suggested = format!("{}ln", name);
 +                span_lint_and_then(
 +                    cx,
 +                    PRINT_WITH_NEWLINE,
 +                    mac.span(),
 +                    &format!("using `{}!()` with a format string that ends in a single newline", name),
 +                    |err| {
 +                        err.multipart_suggestion(
 +                            &format!("use `{}!` instead", suggested),
 +                            vec![(mac.path.span, suggested), (newline_span(&fmt_str).0, String::new())],
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +/// Checks if the format string contains a single newline that terminates it.
 +///
 +/// Literal and escaped newlines are both checked (only literal for raw strings).
 +fn check_newlines(fmtstr: &StrLit) -> bool {
 +    let mut has_internal_newline = false;
 +    let mut last_was_cr = false;
 +    let mut should_lint = false;
 +
 +    let contents = fmtstr.symbol.as_str();
 +
 +    let mut cb = |r: Range<usize>, c: Result<char, EscapeError>| {
 +        let c = c.unwrap();
 +
 +        if r.end == contents.len() && c == '\n' && !last_was_cr && !has_internal_newline {
 +            should_lint = true;
 +        } else {
 +            last_was_cr = c == '\r';
 +            if c == '\n' {
 +                has_internal_newline = true;
 +            }
 +        }
 +    };
 +
 +    match fmtstr.style {
 +        StrStyle::Cooked => unescape::unescape_literal(contents, unescape::Mode::Str, &mut cb),
 +        StrStyle::Raw(_) => unescape::unescape_literal(contents, unescape::Mode::RawStr, &mut cb),
 +    }
 +
 +    should_lint
 +}
index 431b09d53c33b25a9542f719aa75deb899771692,0000000000000000000000000000000000000000..b226026323be835a7f5be3231f8fe70f491bdc80
mode 100644,000000..100644
--- /dev/null
@@@ -1,704 -1,0 +1,710 @@@
-             eq_closure_binder(lb, rb) && lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(le, re)
 +//! Utilities for manipulating and extracting information from `rustc_ast::ast`.
 +//!
 +//! - The `eq_foobar` functions test for semantic equality but ignores `NodeId`s and `Span`s.
 +
 +#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)]
 +
 +use crate::{both, over};
 +use rustc_ast::ptr::P;
 +use rustc_ast::{self as ast, *};
 +use rustc_span::symbol::Ident;
 +use std::mem;
 +
 +pub mod ident_iter;
 +pub use ident_iter::IdentIter;
 +
 +pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool {
 +    use BinOpKind::*;
 +    matches!(
 +        kind,
 +        Sub | Div | Eq | Lt | Le | Gt | Ge | Ne | And | Or | BitXor | BitAnd | BitOr
 +    )
 +}
 +
 +/// Checks if each element in the first slice is contained within the latter as per `eq_fn`.
 +pub fn unordered_over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
 +    left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r)))
 +}
 +
 +pub fn eq_id(l: Ident, r: Ident) -> bool {
 +    l.name == r.name
 +}
 +
 +pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
 +    use PatKind::*;
 +    match (&l.kind, &r.kind) {
 +        (Paren(l), _) => eq_pat(l, r),
 +        (_, Paren(r)) => eq_pat(l, r),
 +        (Wild, Wild) | (Rest, Rest) => true,
 +        (Lit(l), Lit(r)) => eq_expr(l, r),
 +        (Ident(b1, i1, s1), Ident(b2, i2, s2)) => b1 == b2 && eq_id(*i1, *i2) && both(s1, s2, |l, r| eq_pat(l, r)),
 +        (Range(lf, lt, le), Range(rf, rt, re)) => {
 +            eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt) && eq_range_end(&le.node, &re.node)
 +        },
 +        (Box(l), Box(r))
 +        | (Ref(l, Mutability::Not), Ref(r, Mutability::Not))
 +        | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r),
 +        (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)),
 +        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
 +        (TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => {
 +            eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r))
 +        },
 +        (Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => {
 +            lr == rr && eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && unordered_over(lfs, rfs, eq_field_pat)
 +        },
 +        (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)),
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_range_end(l: &RangeEnd, r: &RangeEnd) -> bool {
 +    match (l, r) {
 +        (RangeEnd::Excluded, RangeEnd::Excluded) => true,
 +        (RangeEnd::Included(l), RangeEnd::Included(r)) => {
 +            matches!(l, RangeSyntax::DotDotEq) == matches!(r, RangeSyntax::DotDotEq)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_field_pat(l: &PatField, r: &PatField) -> bool {
 +    l.is_placeholder == r.is_placeholder
 +        && eq_id(l.ident, r.ident)
 +        && eq_pat(&l.pat, &r.pat)
 +        && over(&l.attrs, &r.attrs, eq_attr)
 +}
 +
 +pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool {
 +    l.position == r.position && eq_ty(&l.ty, &r.ty)
 +}
 +
 +pub fn eq_maybe_qself(l: &Option<QSelf>, r: &Option<QSelf>) -> bool {
 +    match (l, r) {
 +        (Some(l), Some(r)) => eq_qself(l, r),
 +        (None, None) => true,
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_path(l: &Path, r: &Path) -> bool {
 +    over(&l.segments, &r.segments, eq_path_seg)
 +}
 +
 +pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool {
 +    eq_id(l.ident, r.ident) && both(&l.args, &r.args, |l, r| eq_generic_args(l, r))
 +}
 +
 +pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool {
 +    match (l, r) {
 +        (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => over(&l.args, &r.args, eq_angle_arg),
 +        (GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => {
 +            over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_angle_arg(l: &AngleBracketedArg, r: &AngleBracketedArg) -> bool {
 +    match (l, r) {
 +        (AngleBracketedArg::Arg(l), AngleBracketedArg::Arg(r)) => eq_generic_arg(l, r),
 +        (AngleBracketedArg::Constraint(l), AngleBracketedArg::Constraint(r)) => eq_assoc_constraint(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool {
 +    match (l, r) {
 +        (GenericArg::Lifetime(l), GenericArg::Lifetime(r)) => eq_id(l.ident, r.ident),
 +        (GenericArg::Type(l), GenericArg::Type(r)) => eq_ty(l, r),
 +        (GenericArg::Const(l), GenericArg::Const(r)) => eq_expr(&l.value, &r.value),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_expr_opt(l: &Option<P<Expr>>, r: &Option<P<Expr>>) -> bool {
 +    both(l, r, |l, r| eq_expr(l, r))
 +}
 +
 +pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool {
 +    match (l, r) {
 +        (StructRest::Base(lb), StructRest::Base(rb)) => eq_expr(lb, rb),
 +        (StructRest::Rest(_), StructRest::Rest(_)) | (StructRest::None, StructRest::None) => true,
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
 +    use ExprKind::*;
 +    if !over(&l.attrs, &r.attrs, eq_attr) {
 +        return false;
 +    }
 +    match (&l.kind, &r.kind) {
 +        (Paren(l), _) => eq_expr(l, r),
 +        (_, Paren(r)) => eq_expr(l, r),
 +        (Err, Err) => true,
 +        (Box(l), Box(r)) | (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r),
 +        (Array(l), Array(r)) | (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)),
 +        (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value),
 +        (Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)),
 +        (MethodCall(lc, la, _), MethodCall(rc, ra, _)) => eq_path_seg(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)),
 +        (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr),
 +        (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r),
 +        (Lit(l), Lit(r)) => l.kind == r.kind,
 +        (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt),
 +        (Let(lp, le, _), Let(rp, re, _)) => eq_pat(lp, rp) && eq_expr(le, re),
 +        (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re),
 +        (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt),
 +        (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => {
 +            eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt)
 +        },
 +        (Loop(lt, ll), Loop(rt, rl)) => eq_label(ll, rl) && eq_block(lt, rt),
 +        (Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb),
 +        (TryBlock(l), TryBlock(r)) => eq_block(l, r),
 +        (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r),
 +        (Break(ll, le), Break(rl, re)) => eq_label(ll, rl) && eq_expr_opt(le, re),
 +        (Continue(ll), Continue(rl)) => eq_label(ll, rl),
 +        (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2),
 +        (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv),
 +        (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
 +        (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm),
 +        (Closure(lb, lc, la, lm, lf, le, _), Closure(rb, rc, ra, rm, rf, re, _)) => {
-         (ClosureBinder::For { generic_params: lp, .. }, ClosureBinder::For { generic_params: rp, .. }) => 
-             lp.len() == rp.len() && std::iter::zip(lp.iter(), rp.iter()).all(|(l, r)| eq_generic_param(l, r)),
++            eq_closure_binder(lb, rb)
++                && lc == rc
++                && la.is_async() == ra.is_async()
++                && lm == rm
++                && eq_fn_decl(lf, rf)
++                && eq_expr(le, re)
 +        },
 +        (Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb),
 +        (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt),
 +        (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re),
 +        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        (Struct(lse), Struct(rse)) => {
 +            eq_maybe_qself(&lse.qself, &rse.qself)
 +                && eq_path(&lse.path, &rse.path)
 +                && eq_struct_rest(&lse.rest, &rse.rest)
 +                && unordered_over(&lse.fields, &rse.fields, eq_field)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_field(l: &ExprField, r: &ExprField) -> bool {
 +    l.is_placeholder == r.is_placeholder
 +        && eq_id(l.ident, r.ident)
 +        && eq_expr(&l.expr, &r.expr)
 +        && over(&l.attrs, &r.attrs, eq_attr)
 +}
 +
 +pub fn eq_arm(l: &Arm, r: &Arm) -> bool {
 +    l.is_placeholder == r.is_placeholder
 +        && eq_pat(&l.pat, &r.pat)
 +        && eq_expr(&l.body, &r.body)
 +        && eq_expr_opt(&l.guard, &r.guard)
 +        && over(&l.attrs, &r.attrs, eq_attr)
 +}
 +
 +pub fn eq_label(l: &Option<Label>, r: &Option<Label>) -> bool {
 +    both(l, r, |l, r| eq_id(l.ident, r.ident))
 +}
 +
 +pub fn eq_block(l: &Block, r: &Block) -> bool {
 +    l.rules == r.rules && over(&l.stmts, &r.stmts, eq_stmt)
 +}
 +
 +pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
 +    use StmtKind::*;
 +    match (&l.kind, &r.kind) {
 +        (Local(l), Local(r)) => {
 +            eq_pat(&l.pat, &r.pat)
 +                && both(&l.ty, &r.ty, |l, r| eq_ty(l, r))
 +                && eq_local_kind(&l.kind, &r.kind)
 +                && over(&l.attrs, &r.attrs, eq_attr)
 +        },
 +        (Item(l), Item(r)) => eq_item(l, r, eq_item_kind),
 +        (Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r),
 +        (Empty, Empty) => true,
 +        (MacCall(l), MacCall(r)) => {
 +            l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, eq_attr)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_local_kind(l: &LocalKind, r: &LocalKind) -> bool {
 +    use LocalKind::*;
 +    match (l, r) {
 +        (Decl, Decl) => true,
 +        (Init(l), Init(r)) => eq_expr(l, r),
 +        (InitElse(li, le), InitElse(ri, re)) => eq_expr(li, ri) && eq_block(le, re),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool {
 +    eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind)
 +}
 +
 +#[expect(clippy::too_many_lines)] // Just a big match statement
 +pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
 +    use ItemKind::*;
 +    match (l, r) {
 +        (ExternCrate(l), ExternCrate(r)) => l == r,
 +        (Use(l), Use(r)) => eq_use_tree(l, r),
 +        (Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
 +        (Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
 +        (
 +            Fn(box ast::Fn {
 +                defaultness: ld,
 +                sig: lf,
 +                generics: lg,
 +                body: lb,
 +            }),
 +            Fn(box ast::Fn {
 +                defaultness: rd,
 +                sig: rf,
 +                generics: rg,
 +                body: rb,
 +            }),
 +        ) => {
 +            eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
 +        },
 +        (Mod(lu, lmk), Mod(ru, rmk)) => {
 +            lu == ru
 +                && match (lmk, rmk) {
 +                    (ModKind::Loaded(litems, linline, _), ModKind::Loaded(ritems, rinline, _)) => {
 +                        linline == rinline && over(litems, ritems, |l, r| eq_item(l, r, eq_item_kind))
 +                    },
 +                    (ModKind::Unloaded, ModKind::Unloaded) => true,
 +                    _ => false,
 +                }
 +        },
 +        (ForeignMod(l), ForeignMod(r)) => {
 +            both(&l.abi, &r.abi, eq_str_lit) && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
 +        },
 +        (
 +            TyAlias(box ast::TyAlias {
 +                defaultness: ld,
 +                generics: lg,
 +                bounds: lb,
 +                ty: lt,
 +                ..
 +            }),
 +            TyAlias(box ast::TyAlias {
 +                defaultness: rd,
 +                generics: rg,
 +                bounds: rb,
 +                ty: rt,
 +                ..
 +            }),
 +        ) => {
 +            eq_defaultness(*ld, *rd)
 +                && eq_generics(lg, rg)
 +                && over(lb, rb, eq_generic_bound)
 +                && both(lt, rt, |l, r| eq_ty(l, r))
 +        },
 +        (Enum(le, lg), Enum(re, rg)) => over(&le.variants, &re.variants, eq_variant) && eq_generics(lg, rg),
 +        (Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => {
 +            eq_variant_data(lv, rv) && eq_generics(lg, rg)
 +        },
 +        (
 +            Trait(box ast::Trait {
 +                is_auto: la,
 +                unsafety: lu,
 +                generics: lg,
 +                bounds: lb,
 +                items: li,
 +            }),
 +            Trait(box ast::Trait {
 +                is_auto: ra,
 +                unsafety: ru,
 +                generics: rg,
 +                bounds: rb,
 +                items: ri,
 +            }),
 +        ) => {
 +            la == ra
 +                && matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
 +                && eq_generics(lg, rg)
 +                && over(lb, rb, eq_generic_bound)
 +                && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
 +        },
 +        (TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, eq_generic_bound),
 +        (
 +            Impl(box ast::Impl {
 +                unsafety: lu,
 +                polarity: lp,
 +                defaultness: ld,
 +                constness: lc,
 +                generics: lg,
 +                of_trait: lot,
 +                self_ty: lst,
 +                items: li,
 +            }),
 +            Impl(box ast::Impl {
 +                unsafety: ru,
 +                polarity: rp,
 +                defaultness: rd,
 +                constness: rc,
 +                generics: rg,
 +                of_trait: rot,
 +                self_ty: rst,
 +                items: ri,
 +            }),
 +        ) => {
 +            matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
 +                && matches!(lp, ImplPolarity::Positive) == matches!(rp, ImplPolarity::Positive)
 +                && eq_defaultness(*ld, *rd)
 +                && matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No)
 +                && eq_generics(lg, rg)
 +                && both(lot, rot, |l, r| eq_path(&l.path, &r.path))
 +                && eq_ty(lst, rst)
 +                && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
 +        },
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        (MacroDef(l), MacroDef(r)) => l.macro_rules == r.macro_rules && eq_mac_args(&l.body, &r.body),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
 +    use ForeignItemKind::*;
 +    match (l, r) {
 +        (Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
 +        (
 +            Fn(box ast::Fn {
 +                defaultness: ld,
 +                sig: lf,
 +                generics: lg,
 +                body: lb,
 +            }),
 +            Fn(box ast::Fn {
 +                defaultness: rd,
 +                sig: rf,
 +                generics: rg,
 +                body: rb,
 +            }),
 +        ) => {
 +            eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
 +        },
 +        (
 +            TyAlias(box ast::TyAlias {
 +                defaultness: ld,
 +                generics: lg,
 +                bounds: lb,
 +                ty: lt,
 +                ..
 +            }),
 +            TyAlias(box ast::TyAlias {
 +                defaultness: rd,
 +                generics: rg,
 +                bounds: rb,
 +                ty: rt,
 +                ..
 +            }),
 +        ) => {
 +            eq_defaultness(*ld, *rd)
 +                && eq_generics(lg, rg)
 +                && over(lb, rb, eq_generic_bound)
 +                && both(lt, rt, |l, r| eq_ty(l, r))
 +        },
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
 +    use AssocItemKind::*;
 +    match (l, r) {
 +        (Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
 +        (
 +            Fn(box ast::Fn {
 +                defaultness: ld,
 +                sig: lf,
 +                generics: lg,
 +                body: lb,
 +            }),
 +            Fn(box ast::Fn {
 +                defaultness: rd,
 +                sig: rf,
 +                generics: rg,
 +                body: rb,
 +            }),
 +        ) => {
 +            eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
 +        },
 +        (
 +            TyAlias(box ast::TyAlias {
 +                defaultness: ld,
 +                generics: lg,
 +                bounds: lb,
 +                ty: lt,
 +                ..
 +            }),
 +            TyAlias(box ast::TyAlias {
 +                defaultness: rd,
 +                generics: rg,
 +                bounds: rb,
 +                ty: rt,
 +                ..
 +            }),
 +        ) => {
 +            eq_defaultness(*ld, *rd)
 +                && eq_generics(lg, rg)
 +                && over(lb, rb, eq_generic_bound)
 +                && both(lt, rt, |l, r| eq_ty(l, r))
 +        },
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_variant(l: &Variant, r: &Variant) -> bool {
 +    l.is_placeholder == r.is_placeholder
 +        && over(&l.attrs, &r.attrs, eq_attr)
 +        && eq_vis(&l.vis, &r.vis)
 +        && eq_id(l.ident, r.ident)
 +        && eq_variant_data(&l.data, &r.data)
 +        && both(&l.disr_expr, &r.disr_expr, |l, r| eq_expr(&l.value, &r.value))
 +}
 +
 +pub fn eq_variant_data(l: &VariantData, r: &VariantData) -> bool {
 +    use VariantData::*;
 +    match (l, r) {
 +        (Unit(_), Unit(_)) => true,
 +        (Struct(l, _), Struct(r, _)) | (Tuple(l, _), Tuple(r, _)) => over(l, r, eq_struct_field),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_struct_field(l: &FieldDef, r: &FieldDef) -> bool {
 +    l.is_placeholder == r.is_placeholder
 +        && over(&l.attrs, &r.attrs, eq_attr)
 +        && eq_vis(&l.vis, &r.vis)
 +        && both(&l.ident, &r.ident, |l, r| eq_id(*l, *r))
 +        && eq_ty(&l.ty, &r.ty)
 +}
 +
 +pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool {
 +    eq_fn_decl(&l.decl, &r.decl) && eq_fn_header(&l.header, &r.header)
 +}
 +
 +pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
 +    matches!(l.unsafety, Unsafe::No) == matches!(r.unsafety, Unsafe::No)
 +        && l.asyncness.is_async() == r.asyncness.is_async()
 +        && matches!(l.constness, Const::No) == matches!(r.constness, Const::No)
 +        && eq_ext(&l.ext, &r.ext)
 +}
 +
 +pub fn eq_generics(l: &Generics, r: &Generics) -> bool {
 +    over(&l.params, &r.params, eq_generic_param)
 +        && over(&l.where_clause.predicates, &r.where_clause.predicates, |l, r| {
 +            eq_where_predicate(l, r)
 +        })
 +}
 +
 +pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool {
 +    use WherePredicate::*;
 +    match (l, r) {
 +        (BoundPredicate(l), BoundPredicate(r)) => {
 +            over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
 +                eq_generic_param(l, r)
 +            }) && eq_ty(&l.bounded_ty, &r.bounded_ty)
 +                && over(&l.bounds, &r.bounds, eq_generic_bound)
 +        },
 +        (RegionPredicate(l), RegionPredicate(r)) => {
 +            eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, eq_generic_bound)
 +        },
 +        (EqPredicate(l), EqPredicate(r)) => eq_ty(&l.lhs_ty, &r.lhs_ty) && eq_ty(&l.rhs_ty, &r.rhs_ty),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_use_tree(l: &UseTree, r: &UseTree) -> bool {
 +    eq_path(&l.prefix, &r.prefix) && eq_use_tree_kind(&l.kind, &r.kind)
 +}
 +
 +pub fn eq_anon_const(l: &AnonConst, r: &AnonConst) -> bool {
 +    eq_expr(&l.value, &r.value)
 +}
 +
 +pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
 +    use UseTreeKind::*;
 +    match (l, r) {
 +        (Glob, Glob) => true,
 +        (Simple(l, _, _), Simple(r, _, _)) => both(l, r, |l, r| eq_id(*l, *r)),
 +        (Nested(l), Nested(r)) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool {
 +    matches!(
 +        (l, r),
 +        (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_))
 +    )
 +}
 +
 +pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool {
 +    use VisibilityKind::*;
 +    match (&l.kind, &r.kind) {
 +        (Public, Public) | (Inherited, Inherited) => true,
 +        (Restricted { path: l, .. }, Restricted { path: r, .. }) => eq_path(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_fn_decl(l: &FnDecl, r: &FnDecl) -> bool {
 +    eq_fn_ret_ty(&l.output, &r.output)
 +        && over(&l.inputs, &r.inputs, |l, r| {
 +            l.is_placeholder == r.is_placeholder
 +                && eq_pat(&l.pat, &r.pat)
 +                && eq_ty(&l.ty, &r.ty)
 +                && over(&l.attrs, &r.attrs, eq_attr)
 +        })
 +}
 +
 +pub fn eq_closure_binder(l: &ClosureBinder, r: &ClosureBinder) -> bool {
 +    match (l, r) {
 +        (ClosureBinder::NotPresent, ClosureBinder::NotPresent) => true,
-         (Explicit(l,_), Explicit(r,_)) => eq_str_lit(l, r),
++        (ClosureBinder::For { generic_params: lp, .. }, ClosureBinder::For { generic_params: rp, .. }) => {
++            lp.len() == rp.len() && std::iter::zip(lp.iter(), rp.iter()).all(|(l, r)| eq_generic_param(l, r))
++        },
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_fn_ret_ty(l: &FnRetTy, r: &FnRetTy) -> bool {
 +    match (l, r) {
 +        (FnRetTy::Default(_), FnRetTy::Default(_)) => true,
 +        (FnRetTy::Ty(l), FnRetTy::Ty(r)) => eq_ty(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
 +    use TyKind::*;
 +    match (&l.kind, &r.kind) {
 +        (Paren(l), _) => eq_ty(l, r),
 +        (_, Paren(r)) => eq_ty(l, r),
 +        (Never, Never) | (Infer, Infer) | (ImplicitSelf, ImplicitSelf) | (Err, Err) | (CVarArgs, CVarArgs) => true,
 +        (Slice(l), Slice(r)) => eq_ty(l, r),
 +        (Array(le, ls), Array(re, rs)) => eq_ty(le, re) && eq_expr(&ls.value, &rs.value),
 +        (Ptr(l), Ptr(r)) => l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty),
 +        (Rptr(ll, l), Rptr(rl, r)) => {
 +            both(ll, rl, |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty)
 +        },
 +        (BareFn(l), BareFn(r)) => {
 +            l.unsafety == r.unsafety
 +                && eq_ext(&l.ext, &r.ext)
 +                && over(&l.generic_params, &r.generic_params, eq_generic_param)
 +                && eq_fn_decl(&l.decl, &r.decl)
 +        },
 +        (Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)),
 +        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
 +        (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, eq_generic_bound),
 +        (ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, eq_generic_bound),
 +        (Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value),
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_ext(l: &Extern, r: &Extern) -> bool {
 +    use Extern::*;
 +    match (l, r) {
 +        (None, None) | (Implicit(_), Implicit(_)) => true,
++        (Explicit(l, _), Explicit(r, _)) => eq_str_lit(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_str_lit(l: &StrLit, r: &StrLit) -> bool {
 +    l.style == r.style && l.symbol == r.symbol && l.suffix == r.suffix
 +}
 +
 +pub fn eq_poly_ref_trait(l: &PolyTraitRef, r: &PolyTraitRef) -> bool {
 +    eq_path(&l.trait_ref.path, &r.trait_ref.path)
 +        && over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
 +            eq_generic_param(l, r)
 +        })
 +}
 +
 +pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool {
 +    use GenericParamKind::*;
 +    l.is_placeholder == r.is_placeholder
 +        && eq_id(l.ident, r.ident)
 +        && over(&l.bounds, &r.bounds, eq_generic_bound)
 +        && match (&l.kind, &r.kind) {
 +            (Lifetime, Lifetime) => true,
 +            (Type { default: l }, Type { default: r }) => both(l, r, |l, r| eq_ty(l, r)),
 +            (
 +                Const {
 +                    ty: lt,
 +                    kw_span: _,
 +                    default: ld,
 +                },
 +                Const {
 +                    ty: rt,
 +                    kw_span: _,
 +                    default: rd,
 +                },
 +            ) => eq_ty(lt, rt) && both(ld, rd, eq_anon_const),
 +            _ => false,
 +        }
 +        && over(&l.attrs, &r.attrs, eq_attr)
 +}
 +
 +pub fn eq_generic_bound(l: &GenericBound, r: &GenericBound) -> bool {
 +    use GenericBound::*;
 +    match (l, r) {
 +        (Trait(ptr1, tbm1), Trait(ptr2, tbm2)) => tbm1 == tbm2 && eq_poly_ref_trait(ptr1, ptr2),
 +        (Outlives(l), Outlives(r)) => eq_id(l.ident, r.ident),
 +        _ => false,
 +    }
 +}
 +
 +fn eq_term(l: &Term, r: &Term) -> bool {
 +    match (l, r) {
 +        (Term::Ty(l), Term::Ty(r)) => eq_ty(l, r),
 +        (Term::Const(l), Term::Const(r)) => eq_anon_const(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_assoc_constraint(l: &AssocConstraint, r: &AssocConstraint) -> bool {
 +    use AssocConstraintKind::*;
 +    eq_id(l.ident, r.ident)
 +        && match (&l.kind, &r.kind) {
 +            (Equality { term: l }, Equality { term: r }) => eq_term(l, r),
 +            (Bound { bounds: l }, Bound { bounds: r }) => over(l, r, eq_generic_bound),
 +            _ => false,
 +        }
 +}
 +
 +pub fn eq_mac_call(l: &MacCall, r: &MacCall) -> bool {
 +    eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args)
 +}
 +
 +pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool {
 +    use AttrKind::*;
 +    l.style == r.style
 +        && match (&l.kind, &r.kind) {
 +            (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2,
 +            (Normal(l, _), Normal(r, _)) => eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args),
 +            _ => false,
 +        }
 +}
 +
 +pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool {
 +    use MacArgs::*;
 +    match (l, r) {
 +        (Empty, Empty) => true,
 +        (Delimited(_, ld, lts), Delimited(_, rd, rts)) => ld == rd && lts.eq_unspanned(rts),
 +        (Eq(_, MacArgsEq::Ast(le)), Eq(_, MacArgsEq::Ast(re))) => eq_expr(le, re),
 +        (Eq(_, MacArgsEq::Hir(ll)), Eq(_, MacArgsEq::Hir(rl))) => ll.kind == rl.kind,
 +        _ => false,
 +    }
 +}
index 1a8e8c996316ff75a9e8df8b74905794b2f1aa9d,0000000000000000000000000000000000000000..77c974582ecb0e9b604f3e8ac432399a215efbe6
mode 100644,000000..100644
--- /dev/null
@@@ -1,1056 -1,0 +1,1056 @@@
-     ArrayLen, BinOpKind, Closure, Block, BodyId, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId,
-     InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, PathSegment, QPath, Stmt,
-     StmtKind, Ty, TyKind, TypeBinding,
 +use crate::consts::constant_simple;
 +use crate::macros::macro_backtrace;
 +use crate::source::snippet_opt;
 +use rustc_ast::ast::InlineAsmTemplatePiece;
 +use rustc_data_structures::fx::FxHasher;
 +use rustc_hir::def::Res;
 +use rustc_hir::HirIdMap;
 +use rustc_hir::{
-             (&StmtKind::Local(l, ), &StmtKind::Local(r, )) => {
++    ArrayLen, BinOpKind, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard,
++    HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, PathSegment, QPath,
++    Stmt, StmtKind, Ty, TyKind, TypeBinding,
 +};
 +use rustc_lexer::{tokenize, TokenKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::TypeckResults;
 +use rustc_span::{sym, Symbol};
 +use std::hash::{Hash, Hasher};
 +
 +/// Callback that is called when two expressions are not equal in the sense of `SpanlessEq`, but
 +/// other conditions would make them equal.
 +type SpanlessEqCallback<'a> = dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a;
 +
 +/// Type used to check whether two ast are the same. This is different from the
 +/// operator `==` on ast types as this operator would compare true equality with
 +/// ID and span.
 +///
 +/// Note that some expressions kinds are not considered but could be added.
 +pub struct SpanlessEq<'a, 'tcx> {
 +    /// Context used to evaluate constant expressions.
 +    cx: &'a LateContext<'tcx>,
 +    maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>,
 +    allow_side_effects: bool,
 +    expr_fallback: Option<Box<SpanlessEqCallback<'a>>>,
 +}
 +
 +impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
 +    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            cx,
 +            maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)),
 +            allow_side_effects: true,
 +            expr_fallback: None,
 +        }
 +    }
 +
 +    /// Consider expressions containing potential side effects as not equal.
 +    #[must_use]
 +    pub fn deny_side_effects(self) -> Self {
 +        Self {
 +            allow_side_effects: false,
 +            ..self
 +        }
 +    }
 +
 +    #[must_use]
 +    pub fn expr_fallback(self, expr_fallback: impl FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self {
 +        Self {
 +            expr_fallback: Some(Box::new(expr_fallback)),
 +            ..self
 +        }
 +    }
 +
 +    /// Use this method to wrap comparisons that may involve inter-expression context.
 +    /// See `self.locals`.
 +    pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> {
 +        HirEqInterExpr {
 +            inner: self,
 +            locals: HirIdMap::default(),
 +        }
 +    }
 +
 +    pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
 +        self.inter_expr().eq_block(left, right)
 +    }
 +
 +    pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +        self.inter_expr().eq_expr(left, right)
 +    }
 +
 +    pub fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
 +        self.inter_expr().eq_path(left, right)
 +    }
 +
 +    pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
 +        self.inter_expr().eq_path_segment(left, right)
 +    }
 +
 +    pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
 +        self.inter_expr().eq_path_segments(left, right)
 +    }
 +}
 +
 +pub struct HirEqInterExpr<'a, 'b, 'tcx> {
 +    inner: &'a mut SpanlessEq<'b, 'tcx>,
 +
 +    // When binding are declared, the binding ID in the left expression is mapped to the one on the
 +    // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`,
 +    // these blocks are considered equal since `x` is mapped to `y`.
 +    pub locals: HirIdMap<HirId>,
 +}
 +
 +impl HirEqInterExpr<'_, '_, '_> {
 +    pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
 +        match (&left.kind, &right.kind) {
-             StmtKind::Local(local, ) => {
++            (&StmtKind::Local(l), &StmtKind::Local(r)) => {
 +                // This additional check ensures that the type of the locals are equivalent even if the init
 +                // expression or type have some inferred parts.
 +                if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
 +                    let l_ty = typeck_lhs.pat_ty(l.pat);
 +                    let r_ty = typeck_rhs.pat_ty(r.pat);
 +                    if l_ty != r_ty {
 +                        return false;
 +                    }
 +                }
 +
 +                // eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that
 +                // these only get added if the init and type is equal.
 +                both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
 +                    && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r))
 +                    && both(&l.els, &r.els, |l, r| self.eq_block(l, r))
 +                    && self.eq_pat(l.pat, r.pat)
 +            },
 +            (&StmtKind::Expr(l), &StmtKind::Expr(r)) | (&StmtKind::Semi(l), &StmtKind::Semi(r)) => self.eq_expr(l, r),
 +            _ => false,
 +        }
 +    }
 +
 +    /// Checks whether two blocks are the same.
 +    fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
 +        if self.cannot_be_compared_block(left) || self.cannot_be_compared_block(right) {
 +            return false;
 +        }
 +        match (left.stmts, left.expr, right.stmts, right.expr) {
 +            ([], None, [], None) => {
 +                // For empty blocks, check to see if the tokens are equal. This will catch the case where a macro
 +                // expanded to nothing, or the cfg attribute was used.
 +                let (left, right) = match (
 +                    snippet_opt(self.inner.cx, left.span),
 +                    snippet_opt(self.inner.cx, right.span),
 +                ) {
 +                    (Some(left), Some(right)) => (left, right),
 +                    _ => return true,
 +                };
 +                let mut left_pos = 0;
 +                let left = tokenize(&left)
 +                    .map(|t| {
 +                        let end = left_pos + t.len;
 +                        let s = &left[left_pos..end];
 +                        left_pos = end;
 +                        (t, s)
 +                    })
 +                    .filter(|(t, _)| {
 +                        !matches!(
 +                            t.kind,
 +                            TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
 +                        )
 +                    })
 +                    .map(|(_, s)| s);
 +                let mut right_pos = 0;
 +                let right = tokenize(&right)
 +                    .map(|t| {
 +                        let end = right_pos + t.len;
 +                        let s = &right[right_pos..end];
 +                        right_pos = end;
 +                        (t, s)
 +                    })
 +                    .filter(|(t, _)| {
 +                        !matches!(
 +                            t.kind,
 +                            TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
 +                        )
 +                    })
 +                    .map(|(_, s)| s);
 +                left.eq(right)
 +            },
 +            _ => {
 +                over(left.stmts, right.stmts, |l, r| self.eq_stmt(l, r))
 +                    && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r))
 +            },
 +        }
 +    }
 +
 +    fn cannot_be_compared_block(&mut self, block: &Block<'_>) -> bool {
 +        if block.stmts.last().map_or(false, |stmt| {
 +            matches!(
 +                stmt.kind,
 +                StmtKind::Semi(semi_expr) if self.should_ignore(semi_expr)
 +            )
 +        }) {
 +            return true;
 +        }
 +
 +        if let Some(block_expr) = block.expr
 +            && self.should_ignore(block_expr)
 +        {
 +            return true
 +        }
 +
 +        false
 +    }
 +
 +    fn should_ignore(&mut self, expr: &Expr<'_>) -> bool {
 +        if macro_backtrace(expr.span).last().map_or(false, |macro_call| {
 +            matches!(
 +                &self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id),
 +                Some(sym::todo_macro | sym::unimplemented_macro)
 +            )
 +        }) {
 +            return true;
 +        }
 +
 +        false
 +    }
 +
 +    pub fn eq_array_length(&mut self, left: ArrayLen, right: ArrayLen) -> bool {
 +        match (left, right) {
 +            (ArrayLen::Infer(..), ArrayLen::Infer(..)) => true,
 +            (ArrayLen::Body(l_ct), ArrayLen::Body(r_ct)) => self.eq_body(l_ct.body, r_ct.body),
 +            (_, _) => false,
 +        }
 +    }
 +
 +    pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool {
 +        // swap out TypeckResults when hashing a body
 +        let old_maybe_typeck_results = self.inner.maybe_typeck_results.replace((
 +            self.inner.cx.tcx.typeck_body(left),
 +            self.inner.cx.tcx.typeck_body(right),
 +        ));
 +        let res = self.eq_expr(
 +            &self.inner.cx.tcx.hir().body(left).value,
 +            &self.inner.cx.tcx.hir().body(right).value,
 +        );
 +        self.inner.maybe_typeck_results = old_maybe_typeck_results;
 +        res
 +    }
 +
 +    #[expect(clippy::similar_names)]
 +    pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +        if !self.inner.allow_side_effects && left.span.ctxt() != right.span.ctxt() {
 +            return false;
 +        }
 +
 +        if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
 +            if let (Some(l), Some(r)) = (
 +                constant_simple(self.inner.cx, typeck_lhs, left),
 +                constant_simple(self.inner.cx, typeck_rhs, right),
 +            ) {
 +                if l == r {
 +                    return true;
 +                }
 +            }
 +        }
 +
 +        let is_eq = match (
 +            reduce_exprkind(self.inner.cx, &left.kind),
 +            reduce_exprkind(self.inner.cx, &right.kind),
 +        ) {
 +            (&ExprKind::AddrOf(lb, l_mut, le), &ExprKind::AddrOf(rb, r_mut, re)) => {
 +                lb == rb && l_mut == r_mut && self.eq_expr(le, re)
 +            },
 +            (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => {
 +                both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
 +            },
 +            (&ExprKind::Assign(ll, lr, _), &ExprKind::Assign(rl, rr, _)) => {
 +                self.inner.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +            },
 +            (&ExprKind::AssignOp(ref lo, ll, lr), &ExprKind::AssignOp(ref ro, rl, rr)) => {
 +                self.inner.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +            },
 +            (&ExprKind::Block(l, _), &ExprKind::Block(r, _)) => self.eq_block(l, r),
 +            (&ExprKind::Binary(l_op, ll, lr), &ExprKind::Binary(r_op, rl, rr)) => {
 +                l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +                    || swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| {
 +                        l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +                    })
 +            },
 +            (&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => {
 +                both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
 +                    && both(le, re, |l, r| self.eq_expr(l, r))
 +            },
 +            (&ExprKind::Box(l), &ExprKind::Box(r)) => self.eq_expr(l, r),
 +            (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => {
 +                self.inner.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
 +            },
 +            (&ExprKind::Cast(lx, lt), &ExprKind::Cast(rx, rt)) | (&ExprKind::Type(lx, lt), &ExprKind::Type(rx, rt)) => {
 +                self.eq_expr(lx, rx) && self.eq_ty(lt, rt)
 +            },
 +            (&ExprKind::Field(l_f_exp, ref l_f_ident), &ExprKind::Field(r_f_exp, ref r_f_ident)) => {
 +                l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp)
 +            },
 +            (&ExprKind::Index(la, li), &ExprKind::Index(ra, ri)) => self.eq_expr(la, ra) && self.eq_expr(li, ri),
 +            (&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => {
 +                self.eq_expr(lc, rc) && self.eq_expr(lt, rt) && both(le, re, |l, r| self.eq_expr(l, r))
 +            },
 +            (&ExprKind::Let(l), &ExprKind::Let(r)) => {
 +                self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init)
 +            },
 +            (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node,
 +            (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => {
 +                lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name)
 +            },
 +            (&ExprKind::Match(le, la, ref ls), &ExprKind::Match(re, ra, ref rs)) => {
 +                ls == rs
 +                    && self.eq_expr(le, re)
 +                    && over(la, ra, |l, r| {
 +                        self.eq_pat(l.pat, r.pat)
 +                            && both(&l.guard, &r.guard, |l, r| self.eq_guard(l, r))
 +                            && self.eq_expr(l.body, r.body)
 +                    })
 +            },
 +            (&ExprKind::MethodCall(l_path, l_args, _), &ExprKind::MethodCall(r_path, r_args, _)) => {
 +                self.inner.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args)
 +            },
 +            (&ExprKind::Repeat(le, ll), &ExprKind::Repeat(re, rl)) => {
 +                self.eq_expr(le, re) && self.eq_array_length(ll, rl)
 +            },
 +            (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)),
 +            (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r),
 +            (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => {
 +                self.eq_qpath(l_path, r_path)
 +                    && both(lo, ro, |l, r| self.eq_expr(l, r))
 +                    && over(lf, rf, |l, r| self.eq_expr_field(l, r))
 +            },
 +            (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
 +            (&ExprKind::Unary(l_op, le), &ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re),
 +            (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
 +            (&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re),
 +            _ => false,
 +        };
 +        is_eq || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right))
 +    }
 +
 +    fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
 +        over(left, right, |l, r| self.eq_expr(l, r))
 +    }
 +
 +    fn eq_expr_field(&mut self, left: &ExprField<'_>, right: &ExprField<'_>) -> bool {
 +        left.ident.name == right.ident.name && self.eq_expr(left.expr, right.expr)
 +    }
 +
 +    fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool {
 +        match (left, right) {
 +            (Guard::If(l), Guard::If(r)) => self.eq_expr(l, r),
 +            (Guard::IfLet(l), Guard::IfLet(r)) => {
 +                self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init)
 +            },
 +            _ => false,
 +        }
 +    }
 +
 +    fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool {
 +        match (left, right) {
 +            (GenericArg::Const(l), GenericArg::Const(r)) => self.eq_body(l.value.body, r.value.body),
 +            (GenericArg::Lifetime(l_lt), GenericArg::Lifetime(r_lt)) => Self::eq_lifetime(l_lt, r_lt),
 +            (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty, r_ty),
 +            (GenericArg::Infer(l_inf), GenericArg::Infer(r_inf)) => self.eq_ty(&l_inf.to_ty(), &r_inf.to_ty()),
 +            _ => false,
 +        }
 +    }
 +
 +    fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool {
 +        left.name == right.name
 +    }
 +
 +    fn eq_pat_field(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool {
 +        let (PatField { ident: li, pat: lp, .. }, PatField { ident: ri, pat: rp, .. }) = (&left, &right);
 +        li.name == ri.name && self.eq_pat(lp, rp)
 +    }
 +
 +    /// Checks whether two patterns are the same.
 +    fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
 +        match (&left.kind, &right.kind) {
 +            (&PatKind::Box(l), &PatKind::Box(r)) => self.eq_pat(l, r),
 +            (&PatKind::Struct(ref lp, la, ..), &PatKind::Struct(ref rp, ra, ..)) => {
 +                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r))
 +            },
 +            (&PatKind::TupleStruct(ref lp, la, ls), &PatKind::TupleStruct(ref rp, ra, rs)) => {
 +                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
 +            },
 +            (&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => {
 +                let eq = lb == rb && both(lp, rp, |l, r| self.eq_pat(l, r));
 +                if eq {
 +                    self.locals.insert(li, ri);
 +                }
 +                eq
 +            },
 +            (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r),
 +            (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r),
 +            (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)),
 +            (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => {
 +                both(ls, rs, |a, b| self.eq_expr(a, b)) && both(le, re, |a, b| self.eq_expr(a, b)) && (li == ri)
 +            },
 +            (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re),
 +            (&PatKind::Slice(ls, ref li, le), &PatKind::Slice(rs, ref ri, re)) => {
 +                over(ls, rs, |l, r| self.eq_pat(l, r))
 +                    && over(le, re, |l, r| self.eq_pat(l, r))
 +                    && both(li, ri, |l, r| self.eq_pat(l, r))
 +            },
 +            (&PatKind::Wild, &PatKind::Wild) => true,
 +            _ => false,
 +        }
 +    }
 +
 +    #[expect(clippy::similar_names)]
 +    fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool {
 +        match (left, right) {
 +            (&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => {
 +                both(lty, rty, |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath)
 +            },
 +            (&QPath::TypeRelative(lty, lseg), &QPath::TypeRelative(rty, rseg)) => {
 +                self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
 +            },
 +            (&QPath::LangItem(llang_item, ..), &QPath::LangItem(rlang_item, ..)) => llang_item == rlang_item,
 +            _ => false,
 +        }
 +    }
 +
 +    pub fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
 +        match (left.res, right.res) {
 +            (Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r),
 +            (Res::Local(_), _) | (_, Res::Local(_)) => false,
 +            _ => over(left.segments, right.segments, |l, r| self.eq_path_segment(l, r)),
 +        }
 +    }
 +
 +    fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool {
 +        if !(left.parenthesized || right.parenthesized) {
 +            over(left.args, right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work
 +                && over(left.bindings, right.bindings, |l, r| self.eq_type_binding(l, r))
 +        } else if left.parenthesized && right.parenthesized {
 +            over(left.inputs(), right.inputs(), |l, r| self.eq_ty(l, r))
 +                && both(&Some(&left.bindings[0].ty()), &Some(&right.bindings[0].ty()), |l, r| {
 +                    self.eq_ty(l, r)
 +                })
 +        } else {
 +            false
 +        }
 +    }
 +
 +    pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
 +        left.len() == right.len() && left.iter().zip(right).all(|(l, r)| self.eq_path_segment(l, r))
 +    }
 +
 +    pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
 +        // The == of idents doesn't work with different contexts,
 +        // we have to be explicit about hygiene
 +        left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
 +    }
 +
 +    pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
 +        match (&left.kind, &right.kind) {
 +            (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec),
 +            (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl),
 +            (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => {
 +                l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty)
 +            },
 +            (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => {
 +                l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty)
 +            },
 +            (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r),
 +            (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)),
 +            (&TyKind::Infer, &TyKind::Infer) => true,
 +            _ => false,
 +        }
 +    }
 +
 +    fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool {
 +        left.ident.name == right.ident.name && self.eq_ty(left.ty(), right.ty())
 +    }
 +}
 +
 +/// Some simple reductions like `{ return }` => `return`
 +fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'hir ExprKind<'hir> {
 +    if let ExprKind::Block(block, _) = kind {
 +        match (block.stmts, block.expr) {
 +            // From an `if let` expression without an `else` block. The arm for the implicit wild pattern is an empty
 +            // block with an empty span.
 +            ([], None) if block.span.is_empty() => &ExprKind::Tup(&[]),
 +            // `{}` => `()`
 +            ([], None) => match snippet_opt(cx, block.span) {
 +                // Don't reduce if there are any tokens contained in the braces
 +                Some(snip)
 +                    if tokenize(&snip)
 +                        .map(|t| t.kind)
 +                        .filter(|t| {
 +                            !matches!(
 +                                t,
 +                                TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
 +                            )
 +                        })
 +                        .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) =>
 +                {
 +                    kind
 +                },
 +                _ => &ExprKind::Tup(&[]),
 +            },
 +            ([], Some(expr)) => match expr.kind {
 +                // `{ return .. }` => `return ..`
 +                ExprKind::Ret(..) => &expr.kind,
 +                _ => kind,
 +            },
 +            ([stmt], None) => match stmt.kind {
 +                StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind {
 +                    // `{ return ..; }` => `return ..`
 +                    ExprKind::Ret(..) => &expr.kind,
 +                    _ => kind,
 +                },
 +                _ => kind,
 +            },
 +            _ => kind,
 +        }
 +    } else {
 +        kind
 +    }
 +}
 +
 +fn swap_binop<'a>(
 +    binop: BinOpKind,
 +    lhs: &'a Expr<'a>,
 +    rhs: &'a Expr<'a>,
 +) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> {
 +    match binop {
 +        BinOpKind::Add | BinOpKind::Eq | BinOpKind::Ne | BinOpKind::BitAnd | BinOpKind::BitXor | BinOpKind::BitOr => {
 +            Some((binop, rhs, lhs))
 +        },
 +        BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)),
 +        BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)),
 +        BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)),
 +        BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)),
 +        BinOpKind::Mul // Not always commutative, e.g. with matrices. See issue #5698
 +        | BinOpKind::Shl
 +        | BinOpKind::Shr
 +        | BinOpKind::Rem
 +        | BinOpKind::Sub
 +        | BinOpKind::Div
 +        | BinOpKind::And
 +        | BinOpKind::Or => None,
 +    }
 +}
 +
 +/// Checks if the two `Option`s are both `None` or some equal values as per
 +/// `eq_fn`.
 +pub fn both<X>(l: &Option<X>, r: &Option<X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
 +    l.as_ref()
 +        .map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y)))
 +}
 +
 +/// Checks if two slices are equal as per `eq_fn`.
 +pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
 +    left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
 +}
 +
 +/// Counts how many elements of the slices are equal as per `eq_fn`.
 +pub fn count_eq<X: Sized>(
 +    left: &mut dyn Iterator<Item = X>,
 +    right: &mut dyn Iterator<Item = X>,
 +    mut eq_fn: impl FnMut(&X, &X) -> bool,
 +) -> usize {
 +    left.zip(right).take_while(|(l, r)| eq_fn(l, r)).count()
 +}
 +
 +/// Checks if two expressions evaluate to the same value, and don't contain any side effects.
 +pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +    SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right)
 +}
 +
 +/// Type used to hash an ast element. This is different from the `Hash` trait
 +/// on ast types as this
 +/// trait would consider IDs and spans.
 +///
 +/// All expressions kind are hashed, but some might have a weaker hash.
 +pub struct SpanlessHash<'a, 'tcx> {
 +    /// Context used to evaluate constant expressions.
 +    cx: &'a LateContext<'tcx>,
 +    maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
 +    s: FxHasher,
 +}
 +
 +impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
 +    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            cx,
 +            maybe_typeck_results: cx.maybe_typeck_results(),
 +            s: FxHasher::default(),
 +        }
 +    }
 +
 +    pub fn finish(self) -> u64 {
 +        self.s.finish()
 +    }
 +
 +    pub fn hash_block(&mut self, b: &Block<'_>) {
 +        for s in b.stmts {
 +            self.hash_stmt(s);
 +        }
 +
 +        if let Some(e) = b.expr {
 +            self.hash_expr(e);
 +        }
 +
 +        std::mem::discriminant(&b.rules).hash(&mut self.s);
 +    }
 +
 +    #[expect(clippy::too_many_lines)]
 +    pub fn hash_expr(&mut self, e: &Expr<'_>) {
 +        let simple_const = self
 +            .maybe_typeck_results
 +            .and_then(|typeck_results| constant_simple(self.cx, typeck_results, e));
 +
 +        // const hashing may result in the same hash as some unrelated node, so add a sort of
 +        // discriminant depending on which path we're choosing next
 +        simple_const.hash(&mut self.s);
 +        if simple_const.is_some() {
 +            return;
 +        }
 +
 +        std::mem::discriminant(&e.kind).hash(&mut self.s);
 +
 +        match e.kind {
 +            ExprKind::AddrOf(kind, m, e) => {
 +                std::mem::discriminant(&kind).hash(&mut self.s);
 +                m.hash(&mut self.s);
 +                self.hash_expr(e);
 +            },
 +            ExprKind::Continue(i) => {
 +                if let Some(i) = i.label {
 +                    self.hash_name(i.ident.name);
 +                }
 +            },
 +            ExprKind::Assign(l, r, _) => {
 +                self.hash_expr(l);
 +                self.hash_expr(r);
 +            },
 +            ExprKind::AssignOp(ref o, l, r) => {
 +                std::mem::discriminant(&o.node).hash(&mut self.s);
 +                self.hash_expr(l);
 +                self.hash_expr(r);
 +            },
 +            ExprKind::Block(b, _) => {
 +                self.hash_block(b);
 +            },
 +            ExprKind::Binary(op, l, r) => {
 +                std::mem::discriminant(&op.node).hash(&mut self.s);
 +                self.hash_expr(l);
 +                self.hash_expr(r);
 +            },
 +            ExprKind::Break(i, ref j) => {
 +                if let Some(i) = i.label {
 +                    self.hash_name(i.ident.name);
 +                }
 +                if let Some(j) = *j {
 +                    self.hash_expr(j);
 +                }
 +            },
 +            ExprKind::Box(e) | ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => {
 +                self.hash_expr(e);
 +            },
 +            ExprKind::Call(fun, args) => {
 +                self.hash_expr(fun);
 +                self.hash_exprs(args);
 +            },
 +            ExprKind::Cast(e, ty) | ExprKind::Type(e, ty) => {
 +                self.hash_expr(e);
 +                self.hash_ty(ty);
 +            },
 +            ExprKind::Closure(&Closure {
 +                capture_clause, body, ..
 +            }) => {
 +                std::mem::discriminant(&capture_clause).hash(&mut self.s);
 +                // closures inherit TypeckResults
 +                self.hash_expr(&self.cx.tcx.hir().body(body).value);
 +            },
 +            ExprKind::Field(e, ref f) => {
 +                self.hash_expr(e);
 +                self.hash_name(f.name);
 +            },
 +            ExprKind::Index(a, i) => {
 +                self.hash_expr(a);
 +                self.hash_expr(i);
 +            },
 +            ExprKind::InlineAsm(asm) => {
 +                for piece in asm.template {
 +                    match piece {
 +                        InlineAsmTemplatePiece::String(s) => s.hash(&mut self.s),
 +                        InlineAsmTemplatePiece::Placeholder {
 +                            operand_idx,
 +                            modifier,
 +                            span: _,
 +                        } => {
 +                            operand_idx.hash(&mut self.s);
 +                            modifier.hash(&mut self.s);
 +                        },
 +                    }
 +                }
 +                asm.options.hash(&mut self.s);
 +                for (op, _op_sp) in asm.operands {
 +                    match op {
 +                        InlineAsmOperand::In { reg, expr } => {
 +                            reg.hash(&mut self.s);
 +                            self.hash_expr(expr);
 +                        },
 +                        InlineAsmOperand::Out { reg, late, expr } => {
 +                            reg.hash(&mut self.s);
 +                            late.hash(&mut self.s);
 +                            if let Some(expr) = expr {
 +                                self.hash_expr(expr);
 +                            }
 +                        },
 +                        InlineAsmOperand::InOut { reg, late, expr } => {
 +                            reg.hash(&mut self.s);
 +                            late.hash(&mut self.s);
 +                            self.hash_expr(expr);
 +                        },
 +                        InlineAsmOperand::SplitInOut {
 +                            reg,
 +                            late,
 +                            in_expr,
 +                            out_expr,
 +                        } => {
 +                            reg.hash(&mut self.s);
 +                            late.hash(&mut self.s);
 +                            self.hash_expr(in_expr);
 +                            if let Some(out_expr) = out_expr {
 +                                self.hash_expr(out_expr);
 +                            }
 +                        },
 +                        InlineAsmOperand::Const { anon_const } | InlineAsmOperand::SymFn { anon_const } => {
 +                            self.hash_body(anon_const.body);
 +                        },
 +                        InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path),
 +                    }
 +                }
 +            },
 +            ExprKind::Let(Let { pat, init, ty, .. }) => {
 +                self.hash_expr(init);
 +                if let Some(ty) = ty {
 +                    self.hash_ty(ty);
 +                }
 +                self.hash_pat(pat);
 +            },
 +            ExprKind::Err => {},
 +            ExprKind::Lit(ref l) => {
 +                l.node.hash(&mut self.s);
 +            },
 +            ExprKind::Loop(b, ref i, ..) => {
 +                self.hash_block(b);
 +                if let Some(i) = *i {
 +                    self.hash_name(i.ident.name);
 +                }
 +            },
 +            ExprKind::If(cond, then, ref else_opt) => {
 +                self.hash_expr(cond);
 +                self.hash_expr(then);
 +                if let Some(e) = *else_opt {
 +                    self.hash_expr(e);
 +                }
 +            },
 +            ExprKind::Match(e, arms, ref s) => {
 +                self.hash_expr(e);
 +
 +                for arm in arms {
 +                    self.hash_pat(arm.pat);
 +                    if let Some(ref e) = arm.guard {
 +                        self.hash_guard(e);
 +                    }
 +                    self.hash_expr(arm.body);
 +                }
 +
 +                s.hash(&mut self.s);
 +            },
 +            ExprKind::MethodCall(path, args, ref _fn_span) => {
 +                self.hash_name(path.ident.name);
 +                self.hash_exprs(args);
 +            },
 +            ExprKind::ConstBlock(ref l_id) => {
 +                self.hash_body(l_id.body);
 +            },
 +            ExprKind::Repeat(e, len) => {
 +                self.hash_expr(e);
 +                self.hash_array_length(len);
 +            },
 +            ExprKind::Ret(ref e) => {
 +                if let Some(e) = *e {
 +                    self.hash_expr(e);
 +                }
 +            },
 +            ExprKind::Path(ref qpath) => {
 +                self.hash_qpath(qpath);
 +            },
 +            ExprKind::Struct(path, fields, ref expr) => {
 +                self.hash_qpath(path);
 +
 +                for f in fields {
 +                    self.hash_name(f.ident.name);
 +                    self.hash_expr(f.expr);
 +                }
 +
 +                if let Some(e) = *expr {
 +                    self.hash_expr(e);
 +                }
 +            },
 +            ExprKind::Tup(tup) => {
 +                self.hash_exprs(tup);
 +            },
 +            ExprKind::Array(v) => {
 +                self.hash_exprs(v);
 +            },
 +            ExprKind::Unary(lop, le) => {
 +                std::mem::discriminant(&lop).hash(&mut self.s);
 +                self.hash_expr(le);
 +            },
 +        }
 +    }
 +
 +    pub fn hash_exprs(&mut self, e: &[Expr<'_>]) {
 +        for e in e {
 +            self.hash_expr(e);
 +        }
 +    }
 +
 +    pub fn hash_name(&mut self, n: Symbol) {
 +        n.hash(&mut self.s);
 +    }
 +
 +    pub fn hash_qpath(&mut self, p: &QPath<'_>) {
 +        match *p {
 +            QPath::Resolved(_, path) => {
 +                self.hash_path(path);
 +            },
 +            QPath::TypeRelative(_, path) => {
 +                self.hash_name(path.ident.name);
 +            },
 +            QPath::LangItem(lang_item, ..) => {
 +                std::mem::discriminant(&lang_item).hash(&mut self.s);
 +            },
 +        }
 +        // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
 +    }
 +
 +    pub fn hash_pat(&mut self, pat: &Pat<'_>) {
 +        std::mem::discriminant(&pat.kind).hash(&mut self.s);
 +        match pat.kind {
 +            PatKind::Binding(ann, _, _, pat) => {
 +                std::mem::discriminant(&ann).hash(&mut self.s);
 +                if let Some(pat) = pat {
 +                    self.hash_pat(pat);
 +                }
 +            },
 +            PatKind::Box(pat) => self.hash_pat(pat),
 +            PatKind::Lit(expr) => self.hash_expr(expr),
 +            PatKind::Or(pats) => {
 +                for pat in pats {
 +                    self.hash_pat(pat);
 +                }
 +            },
 +            PatKind::Path(ref qpath) => self.hash_qpath(qpath),
 +            PatKind::Range(s, e, i) => {
 +                if let Some(s) = s {
 +                    self.hash_expr(s);
 +                }
 +                if let Some(e) = e {
 +                    self.hash_expr(e);
 +                }
 +                std::mem::discriminant(&i).hash(&mut self.s);
 +            },
 +            PatKind::Ref(pat, mu) => {
 +                self.hash_pat(pat);
 +                std::mem::discriminant(&mu).hash(&mut self.s);
 +            },
 +            PatKind::Slice(l, m, r) => {
 +                for pat in l {
 +                    self.hash_pat(pat);
 +                }
 +                if let Some(pat) = m {
 +                    self.hash_pat(pat);
 +                }
 +                for pat in r {
 +                    self.hash_pat(pat);
 +                }
 +            },
 +            PatKind::Struct(ref qpath, fields, e) => {
 +                self.hash_qpath(qpath);
 +                for f in fields {
 +                    self.hash_name(f.ident.name);
 +                    self.hash_pat(f.pat);
 +                }
 +                e.hash(&mut self.s);
 +            },
 +            PatKind::Tuple(pats, e) => {
 +                for pat in pats {
 +                    self.hash_pat(pat);
 +                }
 +                e.hash(&mut self.s);
 +            },
 +            PatKind::TupleStruct(ref qpath, pats, e) => {
 +                self.hash_qpath(qpath);
 +                for pat in pats {
 +                    self.hash_pat(pat);
 +                }
 +                e.hash(&mut self.s);
 +            },
 +            PatKind::Wild => {},
 +        }
 +    }
 +
 +    pub fn hash_path(&mut self, path: &Path<'_>) {
 +        match path.res {
 +            // constant hash since equality is dependant on inter-expression context
 +            // e.g. The expressions `if let Some(x) = foo() {}` and `if let Some(y) = foo() {}` are considered equal
 +            // even though the binding names are different and they have different `HirId`s.
 +            Res::Local(_) => 1_usize.hash(&mut self.s),
 +            _ => {
 +                for seg in path.segments {
 +                    self.hash_name(seg.ident.name);
 +                    self.hash_generic_args(seg.args().args);
 +                }
 +            },
 +        }
 +    }
 +
 +    pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
 +        std::mem::discriminant(&b.kind).hash(&mut self.s);
 +
 +        match &b.kind {
++            StmtKind::Local(local) => {
 +                self.hash_pat(local.pat);
 +                if let Some(init) = local.init {
 +                    self.hash_expr(init);
 +                }
 +                if let Some(els) = local.els {
 +                    self.hash_block(els);
 +                }
 +            },
 +            StmtKind::Item(..) => {},
 +            StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
 +                self.hash_expr(expr);
 +            },
 +        }
 +    }
 +
 +    pub fn hash_guard(&mut self, g: &Guard<'_>) {
 +        match g {
 +            Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => {
 +                self.hash_expr(expr);
 +            },
 +        }
 +    }
 +
 +    pub fn hash_lifetime(&mut self, lifetime: Lifetime) {
 +        std::mem::discriminant(&lifetime.name).hash(&mut self.s);
 +        if let LifetimeName::Param(param_id, ref name) = lifetime.name {
 +            std::mem::discriminant(name).hash(&mut self.s);
 +            param_id.hash(&mut self.s);
 +            match name {
 +                ParamName::Plain(ref ident) => {
 +                    ident.name.hash(&mut self.s);
 +                },
 +                ParamName::Fresh | ParamName::Error => {},
 +            }
 +        }
 +    }
 +
 +    pub fn hash_ty(&mut self, ty: &Ty<'_>) {
 +        std::mem::discriminant(&ty.kind).hash(&mut self.s);
 +        self.hash_tykind(&ty.kind);
 +    }
 +
 +    pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
 +        match ty {
 +            TyKind::Slice(ty) => {
 +                self.hash_ty(ty);
 +            },
 +            &TyKind::Array(ty, len) => {
 +                self.hash_ty(ty);
 +                self.hash_array_length(len);
 +            },
 +            TyKind::Ptr(ref mut_ty) => {
 +                self.hash_ty(mut_ty.ty);
 +                mut_ty.mutbl.hash(&mut self.s);
 +            },
 +            TyKind::Rptr(lifetime, ref mut_ty) => {
 +                self.hash_lifetime(*lifetime);
 +                self.hash_ty(mut_ty.ty);
 +                mut_ty.mutbl.hash(&mut self.s);
 +            },
 +            TyKind::BareFn(bfn) => {
 +                bfn.unsafety.hash(&mut self.s);
 +                bfn.abi.hash(&mut self.s);
 +                for arg in bfn.decl.inputs {
 +                    self.hash_ty(arg);
 +                }
 +                std::mem::discriminant(&bfn.decl.output).hash(&mut self.s);
 +                match bfn.decl.output {
 +                    FnRetTy::DefaultReturn(_) => {},
 +                    FnRetTy::Return(ty) => {
 +                        self.hash_ty(ty);
 +                    },
 +                }
 +                bfn.decl.c_variadic.hash(&mut self.s);
 +            },
 +            TyKind::Tup(ty_list) => {
 +                for ty in *ty_list {
 +                    self.hash_ty(ty);
 +                }
 +            },
 +            TyKind::Path(ref qpath) => self.hash_qpath(qpath),
 +            TyKind::OpaqueDef(_, arg_list) => {
 +                self.hash_generic_args(arg_list);
 +            },
 +            TyKind::TraitObject(_, lifetime, _) => {
 +                self.hash_lifetime(*lifetime);
 +            },
 +            TyKind::Typeof(anon_const) => {
 +                self.hash_body(anon_const.body);
 +            },
 +            TyKind::Err | TyKind::Infer | TyKind::Never => {},
 +        }
 +    }
 +
 +    pub fn hash_array_length(&mut self, length: ArrayLen) {
 +        match length {
 +            ArrayLen::Infer(..) => {},
 +            ArrayLen::Body(anon_const) => self.hash_body(anon_const.body),
 +        }
 +    }
 +
 +    pub fn hash_body(&mut self, body_id: BodyId) {
 +        // swap out TypeckResults when hashing a body
 +        let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body_id));
 +        self.hash_expr(&self.cx.tcx.hir().body(body_id).value);
 +        self.maybe_typeck_results = old_maybe_typeck_results;
 +    }
 +
 +    fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) {
 +        for arg in arg_list {
 +            match *arg {
 +                GenericArg::Lifetime(l) => self.hash_lifetime(l),
 +                GenericArg::Type(ref ty) => self.hash_ty(ty),
 +                GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
 +                GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()),
 +            }
 +        }
 +    }
 +}
 +
 +pub fn hash_stmt(cx: &LateContext<'_>, s: &Stmt<'_>) -> u64 {
 +    let mut h = SpanlessHash::new(cx);
 +    h.hash_stmt(s);
 +    h.finish()
 +}
 +
 +pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 {
 +    let mut h = SpanlessHash::new(cx);
 +    h.hash_expr(e);
 +    h.finish()
 +}
index 0089e1b64006fa3d4967484faf3f284f86efb429,0000000000000000000000000000000000000000..2fdda9fac1629703edbca57d3327843de120d863
mode 100644,000000..100644
--- /dev/null
@@@ -1,2263 -1,0 +1,2303 @@@
- use rustc_middle::ty::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture};
 +#![feature(box_patterns)]
 +#![feature(control_flow_enum)]
 +#![feature(let_else)]
 +#![cfg_attr(bootstrap, feature(let_chains))]
 +#![feature(lint_reasons)]
 +#![feature(once_cell)]
 +#![feature(rustc_private)]
 +#![recursion_limit = "512"]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
 +// warn on the same lints as `clippy_lints`
 +#![warn(trivial_casts, trivial_numeric_casts)]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_ast;
 +extern crate rustc_ast_pretty;
 +extern crate rustc_attr;
 +extern crate rustc_data_structures;
 +extern crate rustc_errors;
 +extern crate rustc_hir;
 +extern crate rustc_infer;
 +extern crate rustc_lexer;
 +extern crate rustc_lint;
 +extern crate rustc_middle;
 +extern crate rustc_session;
 +extern crate rustc_span;
 +extern crate rustc_target;
 +extern crate rustc_trait_selection;
 +extern crate rustc_typeck;
 +
 +#[macro_use]
 +pub mod sym_helper;
 +
 +pub mod ast_utils;
 +pub mod attrs;
 +pub mod comparisons;
 +pub mod consts;
 +pub mod diagnostics;
 +pub mod eager_or_lazy;
 +pub mod higher;
 +mod hir_utils;
 +pub mod macros;
 +pub mod msrvs;
 +pub mod numeric_literal;
 +pub mod paths;
 +pub mod ptr;
 +pub mod qualify_min_const_fn;
 +pub mod source;
 +pub mod str_utils;
 +pub mod sugg;
 +pub mod ty;
 +pub mod usage;
 +pub mod visitors;
 +
 +pub use self::attrs::*;
 +pub use self::hir_utils::{
 +    both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
 +};
 +
 +use std::collections::hash_map::Entry;
 +use std::hash::BuildHasherDefault;
 +use std::sync::OnceLock;
 +use std::sync::{Mutex, MutexGuard};
 +
 +use if_chain::if_chain;
 +use rustc_ast::ast::{self, LitKind};
 +use rustc_ast::Attribute;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_data_structures::unhash::UnhashMap;
 +use rustc_hir as hir;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_ID};
 +use rustc_hir::hir_id::{HirIdMap, HirIdSet};
 +use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 +use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
 +use rustc_hir::{
 +    def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination, Expr,
 +    ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource,
 +    Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind,
 +    TraitRef, TyKind, UnOp,
 +};
 +use rustc_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 crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type};
++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::sym;
 +use rustc_span::symbol::{kw, Symbol};
 +use rustc_span::{Span, DUMMY_SP};
 +use rustc_target::abi::Integer;
 +
 +use crate::consts::{constant, Constant};
-                 ExprKind::Assign(lhs, ..) | ExprKind::Assign(_, lhs, _) if lhs.hir_id == child_id => {
++use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
 +use crate::visitors::expr_visitor_no_bodies;
 +
 +pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
 +    if let Ok(version) = RustcVersion::parse(msrv) {
 +        return Some(version);
 +    } else if let Some(sess) = sess {
 +        if let Some(span) = span {
 +            sess.span_err(span, &format!("`{}` is not a valid Rust version", msrv));
 +        }
 +    }
 +    None
 +}
 +
 +pub fn meets_msrv(msrv: Option<RustcVersion>, lint_msrv: RustcVersion) -> bool {
 +    msrv.map_or(true, |msrv| msrv.meets(lint_msrv))
 +}
 +
 +#[macro_export]
 +macro_rules! extract_msrv_attr {
 +    ($context:ident) => {
 +        fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
 +            let sess = rustc_lint::LintContext::sess(cx);
 +            match $crate::get_unique_inner_attr(sess, attrs, "msrv") {
 +                Some(msrv_attr) => {
 +                    if let Some(msrv) = msrv_attr.value_str() {
 +                        self.msrv = $crate::parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
 +                    } else {
 +                        sess.span_err(msrv_attr.span, "bad clippy attribute");
 +                    }
 +                },
 +                _ => (),
 +            }
 +        }
 +    };
 +}
 +
 +/// If the given expression is a local binding, find the initializer expression.
 +/// If that initializer expression is another local binding, find its initializer again.
 +/// This process repeats as long as possible (but usually no more than once). Initializer
 +/// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
 +/// instead.
 +///
 +/// Examples:
 +/// ```
 +/// let abc = 1;
 +/// //        ^ output
 +/// let def = abc;
 +/// dbg!(def);
 +/// //   ^^^ input
 +///
 +/// // or...
 +/// let abc = 1;
 +/// let def = abc + 2;
 +/// //        ^^^^^^^ output
 +/// dbg!(def);
 +/// //   ^^^ input
 +/// ```
 +pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
 +    while let Some(init) = path_to_local(expr)
 +        .and_then(|id| find_binding_init(cx, id))
 +        .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
 +    {
 +        expr = init;
 +    }
 +    expr
 +}
 +
 +/// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
 +/// By only considering immutable bindings, we guarantee that the returned expression represents the
 +/// value of the binding wherever it is referenced.
 +///
 +/// Example: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned.
 +/// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the
 +/// canonical binding `HirId`.
 +pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
 +    let hir = cx.tcx.hir();
 +    if_chain! {
 +        if let Some(Node::Pat(pat)) = hir.find(hir_id);
 +        if matches!(pat.kind, PatKind::Binding(BindingAnnotation::Unannotated, ..));
 +        let parent = hir.get_parent_node(hir_id);
 +        if let Some(Node::Local(local)) = hir.find(parent);
 +        then {
 +            return local.init;
 +        }
 +    }
 +    None
 +}
 +
 +/// Returns `true` if the given `NodeId` is inside a constant context
 +///
 +/// # Example
 +///
 +/// ```rust,ignore
 +/// if in_constant(cx, expr.hir_id) {
 +///     // Do something
 +/// }
 +/// ```
 +pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
 +    let parent_id = cx.tcx.hir().get_parent_item(id);
 +    match cx.tcx.hir().get_by_def_id(parent_id) {
 +        Node::Item(&Item {
 +            kind: ItemKind::Const(..) | ItemKind::Static(..),
 +            ..
 +        })
 +        | Node::TraitItem(&TraitItem {
 +            kind: TraitItemKind::Const(..),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Const(..),
 +            ..
 +        })
 +        | Node::AnonConst(_) => true,
 +        Node::Item(&Item {
 +            kind: ItemKind::Fn(ref sig, ..),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Fn(ref sig, _),
 +            ..
 +        }) => sig.header.constness == Constness::Const,
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if a `QPath` resolves to a constructor of a `LangItem`.
 +/// For example, use this to check whether a function call or a pattern is `Some(..)`.
 +pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem) -> bool {
 +    if let QPath::Resolved(_, path) = qpath {
 +        if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
 +            if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) {
 +                return cx.tcx.parent(ctor_id) == item_id;
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
 +    matches!(
 +        expr.kind,
 +        ExprKind::Block(
 +            Block {
 +                stmts: [],
 +                expr: None,
 +                ..
 +            },
 +            _
 +        ) | ExprKind::Tup([])
 +    )
 +}
 +
 +/// Checks if given pattern is a wildcard (`_`)
 +pub fn is_wild(pat: &Pat<'_>) -> bool {
 +    matches!(pat.kind, PatKind::Wild)
 +}
 +
 +/// Checks if the method call given in `expr` belongs to the given trait.
 +/// This is a deprecated function, consider using [`is_trait_method`].
 +pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
 +    let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
 +    let trt_id = cx.tcx.trait_of_item(def_id);
 +    trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
 +}
 +
 +/// Checks if a method is defined in an impl of a diagnostic item
 +pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
 +    if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
 +        if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
 +            return cx.tcx.is_diagnostic_item(diag_item, adt.did());
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks if a method is in a diagnostic item trait
 +pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
 +    if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
 +        return cx.tcx.is_diagnostic_item(diag_item, trait_did);
 +    }
 +    false
 +}
 +
 +/// Checks if the method call given in `expr` belongs to the given trait.
 +pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
 +    cx.typeck_results()
 +        .type_dependent_def_id(expr.hir_id)
 +        .map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
 +}
 +
 +/// Checks if the given expression is a path referring an item on the trait
 +/// that is marked with the given diagnostic item.
 +///
 +/// For checking method call expressions instead of path expressions, use
 +/// [`is_trait_method`].
 +///
 +/// For example, this can be used to find if an expression like `u64::default`
 +/// refers to an item of the trait `Default`, which is associated with the
 +/// `diag_item` of `sym::Default`.
 +pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
 +    if let hir::ExprKind::Path(ref qpath) = expr.kind {
 +        cx.qpath_res(qpath, expr.hir_id)
 +            .opt_def_id()
 +            .map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item))
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
 +    match *path {
 +        QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
 +        QPath::TypeRelative(_, seg) => seg,
 +        QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
 +    }
 +}
 +
 +pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
 +    last_path_segment(qpath)
 +        .args
 +        .map_or(&[][..], |a| a.args)
 +        .iter()
 +        .filter_map(|a| match a {
 +            hir::GenericArg::Type(ty) => Some(ty),
 +            _ => None,
 +        })
 +}
 +
 +/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
 +/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
 +/// `QPath::Resolved.1.res.opt_def_id()`.
 +///
 +/// Matches a `QPath` against a slice of segment string literals.
 +///
 +/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
 +/// `rustc_hir::QPath`.
 +///
 +/// # Examples
 +/// ```rust,ignore
 +/// match_qpath(path, &["std", "rt", "begin_unwind"])
 +/// ```
 +pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
 +    match *path {
 +        QPath::Resolved(_, path) => match_path(path, segments),
 +        QPath::TypeRelative(ty, segment) => match ty.kind {
 +            TyKind::Path(ref inner_path) => {
 +                if let [prefix @ .., end] = segments {
 +                    if match_qpath(inner_path, prefix) {
 +                        return segment.ident.name.as_str() == *end;
 +                    }
 +                }
 +                false
 +            },
 +            _ => false,
 +        },
 +        QPath::LangItem(..) => false,
 +    }
 +}
 +
 +/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
 +///
 +/// Please use `is_expr_diagnostic_item` if the target is a diagnostic item.
 +pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
 +    path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments))
 +}
 +
 +/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given
 +/// diagnostic item.
 +pub fn is_expr_diagnostic_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
 +    path_def_id(cx, expr).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
 +}
 +
 +/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
 +/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
 +/// `QPath::Resolved.1.res.opt_def_id()`.
 +///
 +/// Matches a `Path` against a slice of segment string literals.
 +///
 +/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
 +/// `rustc_hir::Path`.
 +///
 +/// # Examples
 +///
 +/// ```rust,ignore
 +/// if match_path(&trait_ref.path, &paths::HASH) {
 +///     // This is the `std::hash::Hash` trait.
 +/// }
 +///
 +/// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
 +///     // This is a `rustc_middle::lint::Lint`.
 +/// }
 +/// ```
 +pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
 +    path.segments
 +        .iter()
 +        .rev()
 +        .zip(segments.iter().rev())
 +        .all(|(a, b)| a.ident.name.as_str() == *b)
 +}
 +
 +/// If the expression is a path to a local, returns the canonical `HirId` of the local.
 +pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
 +    if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
 +        if let Res::Local(id) = path.res {
 +            return Some(id);
 +        }
 +    }
 +    None
 +}
 +
 +/// Returns true if the expression is a path to a local with the specified `HirId`.
 +/// Use this function to see if an expression matches a function argument or a match binding.
 +pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
 +    path_to_local(expr) == Some(id)
 +}
 +
 +pub trait MaybePath<'hir> {
 +    fn hir_id(&self) -> HirId;
 +    fn qpath_opt(&self) -> Option<&QPath<'hir>>;
 +}
 +
 +macro_rules! maybe_path {
 +    ($ty:ident, $kind:ident) => {
 +        impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
 +            fn hir_id(&self) -> HirId {
 +                self.hir_id
 +            }
 +            fn qpath_opt(&self) -> Option<&QPath<'hir>> {
 +                match &self.kind {
 +                    hir::$kind::Path(qpath) => Some(qpath),
 +                    _ => None,
 +                }
 +            }
 +        }
 +    };
 +}
 +maybe_path!(Expr, ExprKind);
 +maybe_path!(Pat, PatKind);
 +maybe_path!(Ty, TyKind);
 +
 +/// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err`
 +pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
 +    match maybe_path.qpath_opt() {
 +        None => Res::Err,
 +        Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
 +    }
 +}
 +
 +/// If `maybe_path` is a path node which resolves to an item, retrieves the item ID
 +pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
 +    path_res(cx, maybe_path).opt_def_id()
 +}
 +
 +/// Resolves a def path like `std::vec::Vec`.
 +/// This function is expensive and should be used sparingly.
 +pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
 +    fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Option<Res> {
 +        match tcx.def_kind(def_id) {
 +            DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
 +                .module_children(def_id)
 +                .iter()
 +                .find(|item| item.ident.name.as_str() == name)
 +                .map(|child| child.res.expect_non_local()),
 +            DefKind::Impl => tcx
 +                .associated_item_def_ids(def_id)
 +                .iter()
 +                .copied()
 +                .find(|assoc_def_id| tcx.item_name(*assoc_def_id).as_str() == name)
 +                .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)),
 +            _ => None,
 +        }
 +    }
 +    fn find_primitive<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
 +        let single = |ty| tcx.incoherent_impls(ty).iter().copied();
 +        let empty = || [].iter().copied();
 +        match name {
 +            "bool" => single(BoolSimplifiedType),
 +            "char" => single(CharSimplifiedType),
 +            "str" => single(StrSimplifiedType),
 +            "array" => single(ArraySimplifiedType),
 +            "slice" => single(SliceSimplifiedType),
 +            // FIXME: rustdoc documents these two using just `pointer`.
 +            //
 +            // Maybe this is something we should do here too.
 +            "const_ptr" => single(PtrSimplifiedType(Mutability::Not)),
 +            "mut_ptr" => single(PtrSimplifiedType(Mutability::Mut)),
 +            "isize" => single(IntSimplifiedType(IntTy::Isize)),
 +            "i8" => single(IntSimplifiedType(IntTy::I8)),
 +            "i16" => single(IntSimplifiedType(IntTy::I16)),
 +            "i32" => single(IntSimplifiedType(IntTy::I32)),
 +            "i64" => single(IntSimplifiedType(IntTy::I64)),
 +            "i128" => single(IntSimplifiedType(IntTy::I128)),
 +            "usize" => single(UintSimplifiedType(UintTy::Usize)),
 +            "u8" => single(UintSimplifiedType(UintTy::U8)),
 +            "u16" => single(UintSimplifiedType(UintTy::U16)),
 +            "u32" => single(UintSimplifiedType(UintTy::U32)),
 +            "u64" => single(UintSimplifiedType(UintTy::U64)),
 +            "u128" => single(UintSimplifiedType(UintTy::U128)),
 +            "f32" => single(FloatSimplifiedType(FloatTy::F32)),
 +            "f64" => single(FloatSimplifiedType(FloatTy::F64)),
 +            _ => empty(),
 +        }
 +    }
 +    fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> {
 +        tcx.crates(())
 +            .iter()
 +            .copied()
 +            .find(|&num| tcx.crate_name(num).as_str() == name)
 +            .map(CrateNum::as_def_id)
 +    }
 +
 +    let (base, first, path) = match *path {
 +        [base, first, ref path @ ..] => (base, first, path),
 +        [primitive] => {
 +            return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy);
 +        },
 +        _ => return Res::Err,
 +    };
 +    let tcx = cx.tcx;
 +    let starts = find_primitive(tcx, base)
 +        .chain(find_crate(tcx, base))
 +        .filter_map(|id| item_child_by_name(tcx, id, first));
 +
 +    for first in starts {
 +        let last = path
 +            .iter()
 +            .copied()
 +            // for each segment, find the child item
 +            .try_fold(first, |res, segment| {
 +                let def_id = res.def_id();
 +                if let Some(item) = item_child_by_name(tcx, def_id, segment) {
 +                    Some(item)
 +                } else if matches!(res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
 +                    // it is not a child item so check inherent impl items
 +                    tcx.inherent_impls(def_id)
 +                        .iter()
 +                        .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment))
 +                } else {
 +                    None
 +                }
 +            });
 +
 +        if let Some(last) = last {
 +            return last;
 +        }
 +    }
 +
 +    Res::Err
 +}
 +
 +/// Convenience function to get the `DefId` of a trait by path.
 +/// It could be a trait or trait alias.
 +pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
 +    match def_path_res(cx, path) {
 +        Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
 +        _ => None,
 +    }
 +}
 +
 +/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
 +///
 +/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
 +///
 +/// ```rust
 +/// struct Point(isize, isize);
 +///
 +/// impl std::ops::Add for Point {
 +///     type Output = Self;
 +///
 +///     fn add(self, other: Self) -> Self {
 +///         Point(0, 0)
 +///     }
 +/// }
 +/// ```
 +pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
 +    // Get the implemented trait for the current function
 +    let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
 +    let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
 +    if_chain! {
 +        if parent_impl != CRATE_DEF_ID;
 +        if let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent_impl);
 +        if let hir::ItemKind::Impl(impl_) = &item.kind;
 +        then {
 +            return impl_.of_trait.as_ref();
 +        }
 +    }
 +    None
 +}
 +
 +/// This method will return tuple of projection stack and root of the expression,
 +/// used in `can_mut_borrow_both`.
 +///
 +/// For example, if `e` represents the `v[0].a.b[x]`
 +/// this method will return a tuple, composed of a `Vec`
 +/// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]`
 +/// and an `Expr` for root of them, `v`
 +fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
 +    let mut result = vec![];
 +    let root = loop {
 +        match e.kind {
 +            ExprKind::Index(ep, _) | ExprKind::Field(ep, _) => {
 +                result.push(e);
 +                e = ep;
 +            },
 +            _ => break e,
 +        };
 +    };
 +    result.reverse();
 +    (result, root)
 +}
 +
 +/// Gets the mutability of the custom deref adjustment, if any.
 +pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
 +    cx.typeck_results()
 +        .expr_adjustments(e)
 +        .iter()
 +        .find_map(|a| match a.kind {
 +            Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
 +            Adjust::Deref(None) => None,
 +            _ => Some(None),
 +        })
 +        .and_then(|x| x)
 +}
 +
 +/// Checks if two expressions can be mutably borrowed simultaneously
 +/// and they aren't dependent on borrowing same thing twice
 +pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
 +    let (s1, r1) = projection_stack(e1);
 +    let (s2, r2) = projection_stack(e2);
 +    if !eq_expr_value(cx, r1, r2) {
 +        return true;
 +    }
 +    if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
 +        return false;
 +    }
 +
 +    for (x1, x2) in s1.iter().zip(s2.iter()) {
 +        if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
 +            return false;
 +        }
 +
 +        match (&x1.kind, &x2.kind) {
 +            (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
 +                if i1 != i2 {
 +                    return true;
 +                }
 +            },
 +            (ExprKind::Index(_, i1), ExprKind::Index(_, i2)) => {
 +                if !eq_expr_value(cx, i1, i2) {
 +                    return false;
 +                }
 +            },
 +            _ => return false,
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"
 +/// constructor from the std library
 +fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
 +    let std_types_symbols = &[
 +        sym::String,
 +        sym::Vec,
 +        sym::VecDeque,
 +        sym::LinkedList,
 +        sym::HashMap,
 +        sym::BTreeMap,
 +        sym::HashSet,
 +        sym::BTreeSet,
 +        sym::BinaryHeap,
 +    ];
 +
 +    if let QPath::TypeRelative(_, method) = path {
 +        if method.ident.name == sym::new {
 +            if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
 +                if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
 +                    return std_types_symbols
 +                        .iter()
 +                        .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did()));
 +                }
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +/// Return true if the expr is equal to `Default::default` when evaluated.
 +pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool {
 +    if_chain! {
 +        if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
 +        if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
 +        if is_diag_trait_item(cx, repl_def_id, sym::Default)
 +            || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
 +        then { true } else { false }
 +    }
 +}
 +
 +/// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
 +/// It doesn't cover all cases, for example indirect function calls (some of std
 +/// functions are supported) but it is the best we have.
 +pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    match &e.kind {
 +        ExprKind::Lit(lit) => match lit.node {
 +            LitKind::Bool(false) | LitKind::Int(0, _) => true,
 +            LitKind::Str(s, _) => s.is_empty(),
 +            _ => false,
 +        },
 +        ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
 +        ExprKind::Repeat(x, ArrayLen::Body(len)) => if_chain! {
 +            if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind;
 +            if let LitKind::Int(v, _) = const_lit.node;
 +            if v <= 32 && is_default_equivalent(cx, x);
 +            then {
 +                true
 +            }
 +            else {
 +                false
 +            }
 +        },
 +        ExprKind::Call(repl_func, _) => is_default_equivalent_call(cx, repl_func),
 +        ExprKind::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone),
 +        ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the top level expression can be moved into a closure as is.
 +/// Currently checks for:
 +/// * Break/Continue outside the given loop HIR ids.
 +/// * Yield/Return statements.
 +/// * Inline assembly.
 +/// * Usages of a field of a local where the type of the local can be partially moved.
 +///
 +/// For example, given the following function:
 +///
 +/// ```
 +/// fn f<'a>(iter: &mut impl Iterator<Item = (usize, &'a mut String)>) {
 +///     for item in iter {
 +///         let s = item.1;
 +///         if item.0 > 10 {
 +///             continue;
 +///         } else {
 +///             s.clear();
 +///         }
 +///     }
 +/// }
 +/// ```
 +///
 +/// When called on the expression `item.0` this will return false unless the local `item` is in the
 +/// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it
 +/// isn't always safe to move into a closure when only a single field is needed.
 +///
 +/// When called on the `continue` expression this will return false unless the outer loop expression
 +/// is in the `loop_ids` set.
 +///
 +/// Note that this check is not recursive, so passing the `if` expression will always return true
 +/// even though sub-expressions might return false.
 +pub fn can_move_expr_to_closure_no_visit<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    loop_ids: &[HirId],
 +    ignore_locals: &HirIdSet,
 +) -> bool {
 +    match expr.kind {
 +        ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
 +        | ExprKind::Continue(Destination { target_id: Ok(id), .. })
 +            if loop_ids.contains(&id) =>
 +        {
 +            true
 +        },
 +        ExprKind::Break(..)
 +        | ExprKind::Continue(_)
 +        | ExprKind::Ret(_)
 +        | ExprKind::Yield(..)
 +        | ExprKind::InlineAsm(_) => false,
 +        // Accessing a field of a local value can only be done if the type isn't
 +        // partially moved.
 +        ExprKind::Field(
 +            &Expr {
 +                hir_id,
 +                kind:
 +                    ExprKind::Path(QPath::Resolved(
 +                        _,
 +                        Path {
 +                            res: Res::Local(local_id),
 +                            ..
 +                        },
 +                    )),
 +                ..
 +            },
 +            _,
 +        ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
 +            // TODO: check if the local has been partially moved. Assume it has for now.
 +            false
 +        },
 +        _ => true,
 +    }
 +}
 +
 +/// How a local is captured by a closure
 +#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 +pub enum CaptureKind {
 +    Value,
 +    Ref(Mutability),
 +}
 +impl CaptureKind {
 +    pub fn is_imm_ref(self) -> bool {
 +        self == Self::Ref(Mutability::Not)
 +    }
 +}
 +impl std::ops::BitOr for CaptureKind {
 +    type Output = Self;
 +    fn bitor(self, rhs: Self) -> Self::Output {
 +        match (self, rhs) {
 +            (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
 +            (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
 +            | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
 +            (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
 +        }
 +    }
 +}
 +impl std::ops::BitOrAssign for CaptureKind {
 +    fn bitor_assign(&mut self, rhs: Self) {
 +        *self = *self | rhs;
 +    }
 +}
 +
 +/// Given an expression referencing a local, determines how it would be captured in a closure.
 +/// Note as this will walk up to parent expressions until the capture can be determined it should
 +/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
 +/// function argument (other than a receiver).
 +pub fn capture_local_usage<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind {
 +    fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
 +        let mut capture = CaptureKind::Ref(Mutability::Not);
 +        pat.each_binding_or_first(&mut |_, id, span, _| match cx
 +            .typeck_results()
 +            .extract_binding_mode(cx.sess(), id, span)
 +            .unwrap()
 +        {
 +            BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => {
 +                capture = CaptureKind::Value;
 +            },
 +            BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => {
 +                capture = CaptureKind::Ref(Mutability::Mut);
 +            },
 +            _ => (),
 +        });
 +        capture
 +    }
 +
 +    debug_assert!(matches!(
 +        e.kind,
 +        ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
 +    ));
 +
 +    let mut child_id = e.hir_id;
 +    let mut capture = CaptureKind::Value;
 +    let mut capture_expr_ty = e;
 +
 +    for (parent_id, parent) in cx.tcx.hir().parent_iter(e.hir_id) {
 +        if let [
 +            Adjustment {
 +                kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
 +                target,
 +            },
 +            ref adjust @ ..,
 +        ] = *cx
 +            .typeck_results()
 +            .adjustments()
 +            .get(child_id)
 +            .map_or(&[][..], |x| &**x)
 +        {
 +            if let rustc_ty::RawPtr(TypeAndMut { mutbl: mutability, .. }) | rustc_ty::Ref(_, _, mutability) =
 +                *adjust.last().map_or(target, |a| a.target).kind()
 +            {
 +                return CaptureKind::Ref(mutability);
 +            }
 +        }
 +
 +        match parent {
 +            Node::Expr(e) => match e.kind {
 +                ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
 +                ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
-     v.allow_closure.then(|| v.captures)
++                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).to_def_id();
 +                    for capture in self.cx.typeck_results().closure_min_captures_flattened(closure_id) {
 +                        let local_id = match capture.place.base {
 +                            PlaceBase::Local(id) => id,
 +                            PlaceBase::Upvar(var) => var.var_path.hir_id,
 +                            _ => continue,
 +                        };
 +                        if !self.locals.contains(&local_id) {
 +                            let capture = match capture.info.capture_kind {
 +                                UpvarCapture::ByValue => CaptureKind::Value,
 +                                UpvarCapture::ByRef(kind) => match kind {
 +                                    BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
 +                                    BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
 +                                        CaptureKind::Ref(Mutability::Mut)
 +                                    },
 +                                },
 +                            };
 +                            self.captures
 +                                .entry(local_id)
 +                                .and_modify(|e| *e |= capture)
 +                                .or_insert(capture);
 +                        }
 +                    }
 +                },
 +                ExprKind::Loop(b, ..) => {
 +                    self.loops.push(e.hir_id);
 +                    self.visit_block(b);
 +                    self.loops.pop();
 +                },
 +                _ => {
 +                    self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
 +                    walk_expr(self, e);
 +                },
 +            }
 +        }
 +
 +        fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
 +            p.each_binding_or_first(&mut |_, id, _, _| {
 +                self.locals.insert(id);
 +            });
 +        }
 +    }
 +
 +    let mut v = V {
 +        cx,
 +        allow_closure: true,
 +        loops: Vec::new(),
 +        locals: HirIdSet::default(),
 +        captures: HirIdMap::default(),
 +    };
 +    v.visit_expr(expr);
- pub fn get_enclosing_loop_or_closure<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
-     for (_, node) in tcx.hir().parent_iter(expr.hir_id) {
++    v.allow_closure.then_some(v.captures)
 +}
 +
 +/// Returns the method names and argument list of nested method call expressions that make up
 +/// `expr`. method/span lists are sorted with the most recent call first.
 +pub fn method_calls<'tcx>(
 +    expr: &'tcx Expr<'tcx>,
 +    max_depth: usize,
 +) -> (Vec<Symbol>, Vec<&'tcx [Expr<'tcx>]>, Vec<Span>) {
 +    let mut method_names = Vec::with_capacity(max_depth);
 +    let mut arg_lists = Vec::with_capacity(max_depth);
 +    let mut spans = Vec::with_capacity(max_depth);
 +
 +    let mut current = expr;
 +    for _ in 0..max_depth {
 +        if let ExprKind::MethodCall(path, args, _) = &current.kind {
 +            if args.iter().any(|e| e.span.from_expansion()) {
 +                break;
 +            }
 +            method_names.push(path.ident.name);
 +            arg_lists.push(&**args);
 +            spans.push(path.ident.span);
 +            current = &args[0];
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    (method_names, arg_lists, spans)
 +}
 +
 +/// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
 +///
 +/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
 +/// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec`
 +/// containing the `Expr`s for
 +/// `.bar()` and `.baz()`
 +pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<&'a [Expr<'a>]>> {
 +    let mut current = expr;
 +    let mut matched = Vec::with_capacity(methods.len());
 +    for method_name in methods.iter().rev() {
 +        // method chains are stored last -> first
 +        if let ExprKind::MethodCall(path, args, _) = current.kind {
 +            if path.ident.name.as_str() == *method_name {
 +                if args.iter().any(|e| e.span.from_expansion()) {
 +                    return None;
 +                }
 +                matched.push(args); // build up `matched` backwards
 +                current = &args[0]; // go to parent expression
 +            } else {
 +                return None;
 +            }
 +        } else {
 +            return None;
 +        }
 +    }
 +    // Reverse `matched` so that it is in the same order as `methods`.
 +    matched.reverse();
 +    Some(matched)
 +}
 +
 +/// Returns `true` if the provided `def_id` is an entrypoint to a program.
 +pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    cx.tcx
 +        .entry_fn(())
 +        .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id)
 +}
 +
 +/// Returns `true` if the expression is in the program's `#[panic_handler]`.
 +pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    let parent = cx.tcx.hir().get_parent_item(e.hir_id);
 +    Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
 +}
 +
 +/// Gets the name of the item the expression is in, if available.
 +pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
 +    let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
 +    match cx.tcx.hir().find_by_def_id(parent_id) {
 +        Some(
 +            Node::Item(Item { ident, .. })
 +            | Node::TraitItem(TraitItem { ident, .. })
 +            | Node::ImplItem(ImplItem { ident, .. }),
 +        ) => Some(ident.name),
 +        _ => None,
 +    }
 +}
 +
 +pub struct ContainsName {
 +    pub name: Symbol,
 +    pub result: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for ContainsName {
 +    fn visit_name(&mut self, _: Span, name: Symbol) {
 +        if self.name == name {
 +            self.result = true;
 +        }
 +    }
 +}
 +
 +/// Checks if an `Expr` contains a certain name.
 +pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
 +    let mut cn = ContainsName { name, result: false };
 +    cn.visit_expr(expr);
 +    cn.result
 +}
 +
 +/// Returns `true` if `expr` contains a return expression
 +pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
 +    let mut found = false;
 +    expr_visitor_no_bodies(|expr| {
 +        if !found {
 +            if let hir::ExprKind::Ret(..) = &expr.kind {
 +                found = true;
 +            }
 +        }
 +        !found
 +    })
 +    .visit_expr(expr);
 +    found
 +}
 +
 +/// Extends the span to the beginning of the spans line, incl. whitespaces.
 +///
 +/// ```rust
 +///        let x = ();
 +/// //             ^^
 +/// // will be converted to
 +///        let x = ();
 +/// // ^^^^^^^^^^^^^^
 +/// ```
 +fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
 +    let span = original_sp(span, DUMMY_SP);
 +    let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap();
 +    let line_no = source_map_and_line.line;
 +    let line_start = source_map_and_line.sf.lines(|lines| lines[line_no]);
 +    span.with_lo(line_start)
 +}
 +
 +/// Gets the parent node, if any.
 +pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
 +    tcx.hir().parent_iter(id).next().map(|(_, node)| node)
 +}
 +
 +/// Gets the parent expression, if any –- this is useful to constrain a lint.
 +pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    get_parent_expr_for_hir(cx, e.hir_id)
 +}
 +
 +/// This retrieves the parent for the given `HirId` if it's an expression. This is useful for
 +/// constraint lints
 +pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> {
 +    match get_parent_node(cx.tcx, hir_id) {
 +        Some(Node::Expr(parent)) => Some(parent),
 +        _ => None,
 +    }
 +}
 +
 +pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
 +    let map = &cx.tcx.hir();
 +    let enclosing_node = map
 +        .get_enclosing_scope(hir_id)
 +        .and_then(|enclosing_id| map.find(enclosing_id));
 +    enclosing_node.and_then(|node| match node {
 +        Node::Block(block) => Some(block),
 +        Node::Item(&Item {
 +            kind: ItemKind::Fn(_, _, eid),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Fn(_, eid),
 +            ..
 +        }) => match cx.tcx.hir().body(eid).value.kind {
 +            ExprKind::Block(block, _) => Some(block),
 +            _ => None,
 +        },
 +        _ => None,
 +    })
 +}
 +
 +/// Gets the loop or closure enclosing the given expression, if any.
-             Node::Expr(
-                 e @ Expr {
-                     kind: ExprKind::Loop(..) | ExprKind::Closure { .. },
-                     ..
++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 {
-             ) => return Some(e),
-             Node::Expr(_) | Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
++            Node::Expr(e) => match e.kind {
++                ExprKind::Closure { .. } => {
++                    if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
++                        && subs.as_closure().kind() == ClosureKind::FnOnce
++                    {
++                        continue;
++                    }
++                    let is_once = walk_to_expr_usage(cx, e, |node, id| {
++                        let Node::Expr(e) = node else {
++                            return None;
++                        };
++                        match e.kind {
++                            ExprKind::Call(f, _) if f.hir_id == id => Some(()),
++                            ExprKind::Call(f, args) => {
++                                let i = args.iter().position(|arg| arg.hir_id == id)?;
++                                let sig = expr_sig(cx, f)?;
++                                let predicates = sig
++                                    .predicates_id()
++                                    .map_or(cx.param_env, |id| cx.tcx.param_env(id))
++                                    .caller_bounds();
++                                sig.input(i).and_then(|ty| {
++                                    ty_is_fn_once_param(cx.tcx, ty.skip_binder(), predicates).then_some(())
++                                })
++                            },
++                            ExprKind::MethodCall(_, args, _) => {
++                                let i = args.iter().position(|arg| arg.hir_id == id)?;
++                                let id = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
++                                let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
++                                ty_is_fn_once_param(cx.tcx, ty, cx.tcx.param_env(id).caller_bounds()).then_some(())
++                            },
++                            _ => None,
++                        }
++                    })
++                    .is_some();
++                    if !is_once {
++                        return Some(e);
++                    }
 +                },
++                ExprKind::Loop(..) => return Some(e),
++                _ => (),
++            },
++            Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
 +            _ => break,
 +        }
 +    }
 +    None
 +}
 +
 +/// Gets the parent node if it's an impl block.
 +pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
 +    match tcx.hir().parent_iter(id).next() {
 +        Some((
 +            _,
 +            Node::Item(Item {
 +                kind: ItemKind::Impl(imp),
 +                ..
 +            }),
 +        )) => Some(imp),
 +        _ => None,
 +    }
 +}
 +
 +/// Removes blocks around an expression, only if the block contains just one expression
 +/// and no statements. Unsafe blocks are not removed.
 +///
 +/// Examples:
 +///  * `{}`               -> `{}`
 +///  * `{ x }`            -> `x`
 +///  * `{{ x }}`          -> `x`
 +///  * `{ x; }`           -> `{ x; }`
 +///  * `{ x; y }`         -> `{ x; y }`
 +///  * `{ unsafe { x } }` -> `unsafe { x }`
 +pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
 +    while let ExprKind::Block(
 +        Block {
 +            stmts: [],
 +            expr: Some(inner),
 +            rules: BlockCheckMode::DefaultBlock,
 +            ..
 +        },
 +        _,
 +    ) = expr.kind
 +    {
 +        expr = inner;
 +    }
 +    expr
 +}
 +
 +/// Removes blocks around an expression, only if the block contains just one expression
 +/// or just one expression statement with a semicolon. Unsafe blocks are not removed.
 +///
 +/// Examples:
 +///  * `{}`               -> `{}`
 +///  * `{ x }`            -> `x`
 +///  * `{ x; }`           -> `x`
 +///  * `{{ x; }}`         -> `x`
 +///  * `{ x; y }`         -> `{ x; y }`
 +///  * `{ unsafe { x } }` -> `unsafe { x }`
 +pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
 +    while let ExprKind::Block(
 +        Block {
 +            stmts: [],
 +            expr: Some(inner),
 +            rules: BlockCheckMode::DefaultBlock,
 +            ..
 +        }
 +        | Block {
 +            stmts:
 +                [
 +                    Stmt {
 +                        kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
 +                        ..
 +                    },
 +                ],
 +            expr: None,
 +            rules: BlockCheckMode::DefaultBlock,
 +            ..
 +        },
 +        _,
 +    ) = expr.kind
 +    {
 +        expr = inner;
 +    }
 +    expr
 +}
 +
 +/// Checks if the given expression is the else clause of either an `if` or `if let` expression.
 +pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    let mut iter = tcx.hir().parent_iter(expr.hir_id);
 +    match iter.next() {
 +        Some((
 +            _,
 +            Node::Expr(Expr {
 +                kind: ExprKind::If(_, _, Some(else_expr)),
 +                ..
 +            }),
 +        )) => else_expr.hir_id == expr.hir_id,
 +        _ => false,
 +    }
 +}
 +
 +/// Checks whether the given expression is a constant integer of the given value.
 +/// unlike `is_integer_literal`, this version does const folding
 +pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
 +    if is_integer_literal(e, value) {
 +        return true;
 +    }
 +    let enclosing_body = cx.tcx.hir().local_def_id(cx.tcx.hir().enclosing_body_owner(e.hir_id));
 +    if let Some((Constant::Int(v), _)) = constant(cx, cx.tcx.typeck(enclosing_body), e) {
 +        return value == v;
 +    }
 +    false
 +}
 +
 +/// Checks whether the given expression is a constant literal of the given value.
 +pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
 +    // FIXME: use constant folding
 +    if let ExprKind::Lit(ref spanned) = expr.kind {
 +        if let LitKind::Int(v, _) = spanned.node {
 +            return v == value;
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns `true` if the given `Expr` has been coerced before.
 +///
 +/// Examples of coercions can be found in the Nomicon at
 +/// <https://doc.rust-lang.org/nomicon/coercions.html>.
 +///
 +/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_typeck::check::coercion` for more
 +/// information on adjustments and coercions.
 +pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    cx.typeck_results().adjustments().get(e.hir_id).is_some()
 +}
 +
 +/// Returns the pre-expansion span if this comes from an expansion of the
 +/// macro `name`.
 +/// See also [`is_direct_expn_of`].
 +#[must_use]
 +pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
 +    loop {
 +        if span.from_expansion() {
 +            let data = span.ctxt().outer_expn_data();
 +            let new_span = data.call_site;
 +
 +            if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
 +                if mac_name.as_str() == name {
 +                    return Some(new_span);
 +                }
 +            }
 +
 +            span = new_span;
 +        } else {
 +            return None;
 +        }
 +    }
 +}
 +
 +/// Returns the pre-expansion span if the span directly comes from an expansion
 +/// of the macro `name`.
 +/// The difference with [`is_expn_of`] is that in
 +/// ```rust
 +/// # macro_rules! foo { ($name:tt!$args:tt) => { $name!$args } }
 +/// # macro_rules! bar { ($e:expr) => { $e } }
 +/// foo!(bar!(42));
 +/// ```
 +/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
 +/// from `bar!` by `is_direct_expn_of`.
 +#[must_use]
 +pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
 +    if span.from_expansion() {
 +        let data = span.ctxt().outer_expn_data();
 +        let new_span = data.call_site;
 +
 +        if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
 +            if mac_name.as_str() == name {
 +                return Some(new_span);
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +/// Convenience function to get the return type of a function.
 +pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
 +    let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
 +    let ret_ty = cx.tcx.fn_sig(fn_def_id).output();
 +    cx.tcx.erase_late_bound_regions(ret_ty)
 +}
 +
 +/// Convenience function to get the nth argument type of a function.
 +pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId, nth: usize) -> Ty<'tcx> {
 +    let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
 +    let arg = cx.tcx.fn_sig(fn_def_id).input(nth);
 +    cx.tcx.erase_late_bound_regions(arg)
 +}
 +
 +/// Checks if an expression is constructing a tuple-like enum variant or struct
 +pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Call(fun, _) = expr.kind {
 +        if let ExprKind::Path(ref qp) = fun.kind {
 +            let res = cx.qpath_res(qp, fun.hir_id);
 +            return match res {
 +                def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
 +                def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
 +                _ => false,
 +            };
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns `true` if a pattern is refutable.
 +// TODO: should be implemented using rustc/mir_build/thir machinery
 +pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
 +    fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
 +        matches!(
 +            cx.qpath_res(qpath, id),
 +            def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
 +        )
 +    }
 +
 +    fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
 +        i.into_iter().any(|pat| is_refutable(cx, pat))
 +    }
 +
 +    match pat.kind {
 +        PatKind::Wild => false,
 +        PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
 +        PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
 +        PatKind::Lit(..) | PatKind::Range(..) => true,
 +        PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
 +        PatKind::Or(pats) => {
 +            // TODO: should be the honest check, that pats is exhaustive set
 +            are_refutable(cx, pats)
 +        },
 +        PatKind::Tuple(pats, _) => are_refutable(cx, pats),
 +        PatKind::Struct(ref qpath, fields, _) => {
 +            is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
 +        },
 +        PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
 +        PatKind::Slice(head, middle, tail) => {
 +            match &cx.typeck_results().node_type(pat.hir_id).kind() {
 +                rustc_ty::Slice(..) => {
 +                    // [..] is the only irrefutable slice pattern.
 +                    !head.is_empty() || middle.is_none() || !tail.is_empty()
 +                },
 +                rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
 +                _ => {
 +                    // unreachable!()
 +                    true
 +                },
 +            }
 +        },
 +    }
 +}
 +
 +/// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call
 +/// the function once on the given pattern.
 +pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
 +    if let PatKind::Or(pats) = pat.kind {
 +        pats.iter().for_each(f);
 +    } else {
 +        f(pat);
 +    }
 +}
 +
 +pub fn is_self(slf: &Param<'_>) -> bool {
 +    if let PatKind::Binding(.., name, _) = slf.pat.kind {
 +        name.name == kw::SelfLower
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
 +    if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind {
 +        if let Res::SelfTy { .. } = path.res {
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
 +    (0..decl.inputs.len()).map(move |i| &body.params[i])
 +}
 +
 +/// Checks if a given expression is a match expression expanded from the `?`
 +/// operator or the `try` macro.
 +pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +    fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +        if_chain! {
 +            if let PatKind::TupleStruct(ref path, pat, None) = arm.pat.kind;
 +            if is_lang_ctor(cx, path, ResultOk);
 +            if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
 +            if path_to_local_id(arm.body, hir_id);
 +            then {
 +                return true;
 +            }
 +        }
 +        false
 +    }
 +
 +    fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +        if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
 +            is_lang_ctor(cx, path, ResultErr)
 +        } else {
 +            false
 +        }
 +    }
 +
 +    if let ExprKind::Match(_, arms, ref source) = expr.kind {
 +        // desugared from a `?` operator
 +        if *source == MatchSource::TryDesugar {
 +            return Some(expr);
 +        }
 +
 +        if_chain! {
 +            if arms.len() == 2;
 +            if arms[0].guard.is_none();
 +            if arms[1].guard.is_none();
 +            if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
 +            then {
 +                return Some(expr);
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +/// Returns `true` if the lint is allowed in the current context. This is useful for
 +/// skipping long running code when it's unnecessary
 +///
 +/// This function should check the lint level for the same node, that the lint will
 +/// be emitted at. If the information is buffered to be emitted at a later point, please
 +/// make sure to use `span_lint_hir` functions to emit the lint. This ensures that
 +/// expectations at the checked nodes will be fulfilled.
 +pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
 +    cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
 +}
 +
 +pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
 +    while let PatKind::Ref(subpat, _) = pat.kind {
 +        pat = subpat;
 +    }
 +    pat
 +}
 +
 +pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 {
 +    Integer::from_int_ty(&tcx, ity).size().bits()
 +}
 +
 +#[expect(clippy::cast_possible_wrap)]
 +/// Turn a constant int byte representation into an i128
 +pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 {
 +    let amt = 128 - int_bits(tcx, ity);
 +    ((u as i128) << amt) >> amt
 +}
 +
 +#[expect(clippy::cast_sign_loss)]
 +/// clip unused bytes
 +pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 {
 +    let amt = 128 - int_bits(tcx, ity);
 +    ((u as u128) << amt) >> amt
 +}
 +
 +/// clip unused bytes
 +pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 {
 +    let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
 +    let amt = 128 - bits;
 +    (u << amt) >> amt
 +}
 +
 +pub fn has_attr(attrs: &[ast::Attribute], symbol: Symbol) -> bool {
 +    attrs.iter().any(|attr| attr.has_name(symbol))
 +}
 +
 +pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
 +    let map = &tcx.hir();
 +    let mut prev_enclosing_node = None;
 +    let mut enclosing_node = node;
 +    while Some(enclosing_node) != prev_enclosing_node {
 +        if has_attr(map.attrs(enclosing_node), symbol) {
 +            return true;
 +        }
 +        prev_enclosing_node = Some(enclosing_node);
 +        enclosing_node = map.local_def_id_to_hir_id(map.get_parent_item(enclosing_node));
 +    }
 +
 +    false
 +}
 +
 +pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
 +    any_parent_has_attr(tcx, node, sym::automatically_derived)
 +}
 +
 +/// Matches a function call with the given path and returns the arguments.
 +///
 +/// Usage:
 +///
 +/// ```rust,ignore
 +/// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX);
 +/// ```
 +pub fn match_function_call<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    path: &[&str],
 +) -> Option<&'tcx [Expr<'tcx>]> {
 +    if_chain! {
 +        if let ExprKind::Call(fun, args) = expr.kind;
 +        if let ExprKind::Path(ref qpath) = fun.kind;
 +        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
 +        if match_def_path(cx, fun_def_id, path);
 +        then {
 +            return Some(args);
 +        }
 +    };
 +    None
 +}
 +
 +/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
 +/// any.
 +///
 +/// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items.
 +pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
 +    let search_path = cx.get_def_path(did);
 +    paths
 +        .iter()
 +        .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
 +}
 +
 +/// Checks if the given `DefId` matches the path.
 +pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
 +    // We should probably move to Symbols in Clippy as well rather than interning every time.
 +    let path = cx.get_def_path(did);
 +    syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
 +}
 +
 +/// Checks if the given `DefId` matches the `libc` item.
 +pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
 +    let path = cx.get_def_path(did);
 +    // libc is meant to be used as a flat list of names, but they're all actually defined in different
 +    // modules based on the target platform. Ignore everything but crate name and the item name.
 +    path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name)
 +}
 +
 +/// Returns the list of condition expressions and the list of blocks in a
 +/// sequence of `if/else`.
 +/// E.g., this returns `([a, b], [c, d, e])` for the expression
 +/// `if a { c } else if b { d } else { e }`.
 +pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
 +    let mut conds = Vec::new();
 +    let mut blocks: Vec<&Block<'_>> = Vec::new();
 +
 +    while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
 +        conds.push(cond);
 +        if let ExprKind::Block(block, _) = then.kind {
 +            blocks.push(block);
 +        } else {
 +            panic!("ExprKind::If node is not an ExprKind::Block");
 +        }
 +
 +        if let Some(else_expr) = r#else {
 +            expr = else_expr;
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    // final `else {..}`
 +    if !blocks.is_empty() {
 +        if let ExprKind::Block(block, _) = expr.kind {
 +            blocks.push(block);
 +        }
 +    }
 +
 +    (conds, blocks)
 +}
 +
 +/// Checks if the given function kind is an async function.
 +pub fn is_async_fn(kind: FnKind<'_>) -> bool {
 +    matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness == IsAsync::Async)
 +}
 +
 +/// Peels away all the compiler generated code surrounding the body of an async function,
 +pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    if let ExprKind::Call(
 +        _,
 +        &[
 +            Expr {
 +                kind: ExprKind::Closure(&Closure { body, .. }),
 +                ..
 +            },
 +        ],
 +    ) = body.value.kind
 +    {
 +        if let ExprKind::Block(
 +            Block {
 +                stmts: [],
 +                expr:
 +                    Some(Expr {
 +                        kind: ExprKind::DropTemps(expr),
 +                        ..
 +                    }),
 +                ..
 +            },
 +            _,
 +        ) = tcx.hir().body(body).value.kind
 +        {
 +            return Some(expr);
 +        }
 +    };
 +    None
 +}
 +
 +// check if expr is calling method or function with #[must_use] attribute
 +pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    let did = match expr.kind {
 +        ExprKind::Call(path, _) => if_chain! {
 +            if let ExprKind::Path(ref qpath) = path.kind;
 +            if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id);
 +            then {
 +                Some(did)
 +            } else {
 +                None
 +            }
 +        },
 +        ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
 +        _ => None,
 +    };
 +
 +    did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use))
 +}
 +
 +/// Checks if an expression represents the identity function
 +/// Only examines closures and `std::convert::identity`
 +pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    /// Checks if a function's body represents the identity function. Looks for bodies of the form:
 +    /// * `|x| x`
 +    /// * `|x| return x`
 +    /// * `|x| { return x }`
 +    /// * `|x| { return x; }`
 +    fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
 +        let id = if_chain! {
 +            if let [param] = func.params;
 +            if let PatKind::Binding(_, id, _, _) = param.pat.kind;
 +            then {
 +                id
 +            } else {
 +                return false;
 +            }
 +        };
 +
 +        let mut expr = &func.value;
 +        loop {
 +            match expr.kind {
 +                #[rustfmt::skip]
 +                ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
 +                | ExprKind::Ret(Some(e)) => expr = e,
 +                #[rustfmt::skip]
 +                ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
 +                    if_chain! {
 +                        if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
 +                        if let ExprKind::Ret(Some(ret_val)) = e.kind;
 +                        then {
 +                            expr = ret_val;
 +                        } else {
 +                            return false;
 +                        }
 +                    }
 +                },
 +                _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
 +            }
 +        }
 +    }
 +
 +    match expr.kind {
 +        ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)),
 +        _ => path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, &paths::CONVERT_IDENTITY)),
 +    }
 +}
 +
 +/// Gets the node where an expression is either used, or it's type is unified with another branch.
 +/// Returns both the node and the `HirId` of the closest child node.
 +pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
 +    let mut child_id = expr.hir_id;
 +    let mut iter = tcx.hir().parent_iter(child_id);
 +    loop {
 +        match iter.next() {
 +            None => break None,
 +            Some((id, Node::Block(_))) => child_id = id,
 +            Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
 +            Some((_, Node::Expr(expr))) => match expr.kind {
 +                ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
 +                ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
 +                ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
 +                _ => break Some((Node::Expr(expr), child_id)),
 +            },
 +            Some((_, node)) => break Some((node, child_id)),
 +        }
 +    }
 +}
 +
 +/// Checks if the result of an expression is used, or it's type is unified with another branch.
 +pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    !matches!(
 +        get_expr_use_or_unification_node(tcx, expr),
 +        None | Some((
 +            Node::Stmt(Stmt {
 +                kind: StmtKind::Expr(_)
 +                    | StmtKind::Semi(_)
 +                    | StmtKind::Local(Local {
 +                        pat: Pat {
 +                            kind: PatKind::Wild,
 +                            ..
 +                        },
 +                        ..
 +                    }),
 +                ..
 +            }),
 +            _
 +        ))
 +    )
 +}
 +
 +/// Checks if the expression is the final expression returned from a block.
 +pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
 +}
 +
 +pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
 +    if !is_no_std_crate(cx) {
 +        Some("std")
 +    } else if !is_no_core_crate(cx) {
 +        Some("core")
 +    } else {
 +        None
 +    }
 +}
 +
 +pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
 +    cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
 +        if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
 +            attr.path == sym::no_std
 +        } else {
 +            false
 +        }
 +    })
 +}
 +
 +pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
 +    cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
 +        if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
 +            attr.path == sym::no_core
 +        } else {
 +            false
 +        }
 +    })
 +}
 +
 +/// Check if parent of a hir node is a trait implementation block.
 +/// For example, `f` in
 +/// ```rust
 +/// # struct S;
 +/// # trait Trait { fn f(); }
 +/// impl Trait for S {
 +///     fn f() {}
 +/// }
 +/// ```
 +pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
 +    if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
 +        matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Check if it's even possible to satisfy the `where` clause for the item.
 +///
 +/// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example:
 +///
 +/// ```ignore
 +/// fn foo() where i32: Iterator {
 +///     for _ in 2i32 {}
 +/// }
 +/// ```
 +pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
 +    use rustc_trait_selection::traits;
 +    let predicates = cx
 +        .tcx
 +        .predicates_of(did)
 +        .predicates
 +        .iter()
 +        .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
 +    traits::impossible_predicates(
 +        cx.tcx,
 +        traits::elaborate_predicates(cx.tcx, predicates)
 +            .map(|o| o.predicate)
 +            .collect::<Vec<_>>(),
 +    )
 +}
 +
 +/// Returns the `DefId` of the callee if the given expression is a function or method call.
 +pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
 +    match &expr.kind {
 +        ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
 +        ExprKind::Call(
 +            Expr {
 +                kind: ExprKind::Path(qpath),
 +                hir_id: path_hir_id,
 +                ..
 +            },
 +            ..,
 +        ) => {
 +            // 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>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
 +    let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
 +    let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
 +    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
 +}
 +
 +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 b09c929f76e2267cbc6145d0e95402346a9e0542,0000000000000000000000000000000000000000..9e238c6f1ac0ef84a8883d8d6c84d695ccd28cfd
mode 100644,000000..100644
--- /dev/null
@@@ -1,38 -1,0 +1,39 @@@
 +use rustc_semver::RustcVersion;
 +
 +macro_rules! msrv_aliases {
 +    ($($major:literal,$minor:literal,$patch:literal {
 +        $($name:ident),* $(,)?
 +    })*) => {
 +        $($(
 +        pub const $name: RustcVersion = RustcVersion::new($major, $minor, $patch);
 +        )*)*
 +    };
 +}
 +
 +// names may refer to stabilized feature flags or library items
 +msrv_aliases! {
++    1,62,0 { BOOL_THEN_SOME }
 +    1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN }
 +    1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
 +    1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS }
 +    1,50,0 { BOOL_THEN }
 +    1,47,0 { TAU }
 +    1,46,0 { CONST_IF_MATCH }
 +    1,45,0 { STR_STRIP_PREFIX }
 +    1,43,0 { LOG2_10, LOG10_2 }
 +    1,42,0 { MATCHES_MACRO, SLICE_PATTERNS, PTR_SLICE_RAW_PARTS }
 +    1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
 +    1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
 +    1,38,0 { POINTER_CAST, REM_EUCLID }
 +    1,37,0 { TYPE_ALIAS_ENUM_VARIANTS }
 +    1,36,0 { ITERATOR_COPIED }
 +    1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
 +    1,34,0 { TRY_FROM }
 +    1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
 +    1,28,0 { FROM_BOOL }
 +    1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN }
 +    1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN }
 +    1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
 +    1,16,0 { STR_REPEAT }
 +    1,24,0 { IS_ASCII_DIGIT }
 +}
index 6542e77113b459e156eb0072fce4391869881b1b,0000000000000000000000000000000000000000..05429d05d9ebe8285698298b598daeaab5eb1347
mode 100644,000000..100644
--- /dev/null
@@@ -1,195 -1,0 +1,196 @@@
 +//! This module contains paths to types and functions Clippy needs to know
 +//! about.
 +//!
 +//! Whenever possible, please consider diagnostic items over hardcoded paths.
 +//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
 +
 +#[cfg(feature = "internal")]
 +pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
 +#[cfg(feature = "internal")]
 +pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
 +    ["rustc_lint_defs", "Applicability", "Unspecified"],
 +    ["rustc_lint_defs", "Applicability", "HasPlaceholders"],
 +    ["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
 +    ["rustc_lint_defs", "Applicability", "MachineApplicable"],
 +];
 +#[cfg(feature = "internal")]
 +pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];
 +pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
 +pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
 +pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
 +pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
 +pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
 +pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
 +pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"];
 +pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
 +pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
 +pub const CORE_ITER_COLLECT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "collect"];
 +pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"];
 +pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"];
 +pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"];
 +pub const CORE_ITER_INTO_ITER: [&str; 6] = ["core", "iter", "traits", "collect", "IntoIterator", "into_iter"];
 +pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"];
 +pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
 +pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
 +/// Preferably use the diagnostic item `sym::deref_method` where possible
 +pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
 +pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"];
 +pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
 +#[cfg(feature = "internal")]
 +pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
 +#[cfg(feature = "internal")]
 +pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"];
 +pub const EXIT: [&str; 3] = ["std", "process", "exit"];
 +pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
 +pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
 +pub const FILE: [&str; 3] = ["std", "fs", "File"];
 +pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
 +pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
 +pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
 +pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"];
 +pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"];
 +pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"];
 +pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
 +pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
 +pub const HASHSET_ITER: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "iter"];
 +#[cfg(feature = "internal")]
 +pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
 +#[cfg(feature = "internal")]
 +pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
 +pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
 +pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
 +pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
 +pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
 +pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
 +pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
 +pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
 +pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
 +pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
 +pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
 +#[cfg(feature = "internal")]
 +pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
 +#[cfg(feature = "internal")]
 +pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
 +#[cfg(feature = "internal")]
 +pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"];
 +#[cfg(feature = "internal")]
 +pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
 +pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"];
 +pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"];
 +pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
 +/// Preferably use the diagnostic item `sym::Option` where possible
 +pub const OPTION: [&str; 3] = ["core", "option", "Option"];
 +pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"];
 +pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
 +pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
 +pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
 +pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
 +pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"];
 +pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"];
 +pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"];
 +pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
 +pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
 +pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
 +#[cfg_attr(not(unix), allow(clippy::invalid_paths))]
 +pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
 +pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
 +pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"];
 +pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"];
 +pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"];
 +pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
 +pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
 +pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"];
 +pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"];
 +pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"];
 +pub const PTR_READ: [&str; 3] = ["core", "ptr", "read"];
 +pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"];
 +pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"];
 +pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"];
 +pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"];
 +pub const PTR_UNALIGNED_VOLATILE_LOAD: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_load"];
 +pub const PTR_UNALIGNED_VOLATILE_STORE: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_store"];
 +pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"];
 +pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
 +pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"];
 +pub const PTR_WRITE_VOLATILE: [&str; 3] = ["core", "ptr", "write_volatile"];
 +pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
 +pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
 +pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
 +pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"];
 +pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
 +/// Preferably use the diagnostic item `sym::Result` where possible
 +pub const RESULT: [&str; 3] = ["core", "result", "Result"];
 +pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
 +pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
 +#[cfg(feature = "internal")]
 +pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"];
 +pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"];
 +pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
 +pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
 +pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
 +pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
 +pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"];
 +pub const SLICE_GET: [&str; 4] = ["core", "slice", "<impl [T]>", "get"];
 +pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
 +pub const SLICE_INTO: [&str; 4] = ["core", "slice", "<impl [T]>", "iter"];
 +pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
 +pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
 +pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
 +pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
 +pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
 +pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
 +pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
 +pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];
 +pub const STR_BYTES: [&str; 4] = ["core", "str", "<impl str>", "bytes"];
 +pub const STR_CHARS: [&str; 4] = ["core", "str", "<impl str>", "chars"];
 +pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
 +pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
++pub const STR_FROM_UTF8_UNCHECKED: [&str; 4] = ["core", "str", "converts", "from_utf8_unchecked"];
 +pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
 +pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
 +#[cfg(feature = "internal")]
 +pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
 +#[cfg(feature = "internal")]
 +pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
 +pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
 +pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"];
 +pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
 +pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
 +pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
 +pub const VEC_DEQUE_ITER: [&str; 5] = ["alloc", "collections", "vec_deque", "VecDeque", "iter"];
 +pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
 +pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
 +pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
 +pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
 +pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
 +pub const PTR_NON_NULL: [&str; 4] = ["core", "ptr", "non_null", "NonNull"];
index f88a92fb11c111146851e4213a6508c0ec26b2ac,0000000000000000000000000000000000000000..1197fe914de46853dd3dcdde12daec9a6097fbcc
mode 100644,000000..100644
--- /dev/null
@@@ -1,508 -1,0 +1,508 @@@
-     (outer_span.ctxt() == outer).then(|| outer_span)
 +//! Utils for extracting, inspecting or transforming source code
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use crate::line_span;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_span::hygiene;
 +use rustc_span::source_map::SourceMap;
 +use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext};
 +use std::borrow::Cow;
 +
 +/// Checks if the span starts with the given text. This will return false if the span crosses
 +/// multiple files or if source is not available.
 +///
 +/// This is used to check for proc macros giving unhelpful spans to things.
 +pub fn span_starts_with<T: LintContext>(cx: &T, span: Span, text: &str) -> bool {
 +    fn helper(sm: &SourceMap, span: Span, text: &str) -> bool {
 +        let pos = sm.lookup_byte_offset(span.lo());
 +        let Some(ref src) = pos.sf.src else {
 +            return false;
 +        };
 +        let end = span.hi() - pos.sf.start_pos;
 +        src.get(pos.pos.0 as usize..end.0 as usize)
 +            // Expression spans can include wrapping parenthesis. Remove them first.
 +            .map_or(false, |s| s.trim_start_matches('(').starts_with(text))
 +    }
 +    helper(cx.sess().source_map(), span, text)
 +}
 +
 +/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
 +/// Also takes an `Option<String>` which can be put inside the braces.
 +pub fn expr_block<'a, T: LintContext>(
 +    cx: &T,
 +    expr: &Expr<'_>,
 +    option: Option<String>,
 +    default: &'a str,
 +    indent_relative_to: Option<Span>,
 +) -> Cow<'a, str> {
 +    let code = snippet_block(cx, expr.span, default, indent_relative_to);
 +    let string = option.unwrap_or_default();
 +    if expr.span.from_expansion() {
 +        Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default)))
 +    } else if let ExprKind::Block(_, _) = expr.kind {
 +        Cow::Owned(format!("{}{}", code, string))
 +    } else if string.is_empty() {
 +        Cow::Owned(format!("{{ {} }}", code))
 +    } else {
 +        Cow::Owned(format!("{{\n{};\n{}\n}}", code, string))
 +    }
 +}
 +
 +/// Returns a new Span that extends the original Span to the first non-whitespace char of the first
 +/// line.
 +///
 +/// ```rust,ignore
 +///     let x = ();
 +/// //          ^^
 +/// // will be converted to
 +///     let x = ();
 +/// //  ^^^^^^^^^^
 +/// ```
 +pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
 +    first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
 +}
 +
 +fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
 +    let line_span = line_span(cx, span);
 +    snippet_opt(cx, line_span).and_then(|snip| {
 +        snip.find(|c: char| !c.is_whitespace())
 +            .map(|pos| line_span.lo() + BytePos::from_usize(pos))
 +    })
 +}
 +
 +/// Returns the indentation of the line of a span
 +///
 +/// ```rust,ignore
 +/// let x = ();
 +/// //      ^^ -- will return 0
 +///     let x = ();
 +/// //          ^^ -- will return 4
 +/// ```
 +pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
 +    snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
 +}
 +
 +/// Gets a snippet of the indentation of the line of a span
 +pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> {
 +    snippet_opt(cx, line_span(cx, span)).map(|mut s| {
 +        let len = s.len() - s.trim_start().len();
 +        s.truncate(len);
 +        s
 +    })
 +}
 +
 +// If the snippet is empty, it's an attribute that was inserted during macro
 +// expansion and we want to ignore those, because they could come from external
 +// sources that the user has no control over.
 +// For some reason these attributes don't have any expansion info on them, so
 +// we have to check it this way until there is a better way.
 +pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
 +    if let Some(snippet) = snippet_opt(cx, span) {
 +        if snippet.is_empty() {
 +            return false;
 +        }
 +    }
 +    true
 +}
 +
 +/// Returns the position just before rarrow
 +///
 +/// ```rust,ignore
 +/// fn into(self) -> () {}
 +///              ^
 +/// // in case of unformatted code
 +/// fn into2(self)-> () {}
 +///               ^
 +/// fn into3(self)   -> () {}
 +///               ^
 +/// ```
 +pub fn position_before_rarrow(s: &str) -> Option<usize> {
 +    s.rfind("->").map(|rpos| {
 +        let mut rpos = rpos;
 +        let chars: Vec<char> = s.chars().collect();
 +        while rpos > 1 {
 +            if let Some(c) = chars.get(rpos - 1) {
 +                if c.is_whitespace() {
 +                    rpos -= 1;
 +                    continue;
 +                }
 +            }
 +            break;
 +        }
 +        rpos
 +    })
 +}
 +
 +/// Reindent a multiline string with possibility of ignoring the first line.
 +#[expect(clippy::needless_pass_by_value)]
 +pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
 +    let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' ');
 +    let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t');
 +    reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into()
 +}
 +
 +fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String {
 +    let x = s
 +        .lines()
 +        .skip(usize::from(ignore_first))
 +        .filter_map(|l| {
 +            if l.is_empty() {
 +                None
 +            } else {
 +                // ignore empty lines
 +                Some(l.char_indices().find(|&(_, x)| x != ch).unwrap_or((l.len(), ch)).0)
 +            }
 +        })
 +        .min()
 +        .unwrap_or(0);
 +    let indent = indent.unwrap_or(0);
 +    s.lines()
 +        .enumerate()
 +        .map(|(i, l)| {
 +            if (ignore_first && i == 0) || l.is_empty() {
 +                l.to_owned()
 +            } else if x > indent {
 +                l.split_at(x - indent).1.to_owned()
 +            } else {
 +                " ".repeat(indent - x) + l
 +            }
 +        })
 +        .collect::<Vec<String>>()
 +        .join("\n")
 +}
 +
 +/// Converts a span to a code snippet if available, otherwise returns the default.
 +///
 +/// This is useful if you want to provide suggestions for your lint or more generally, if you want
 +/// to convert a given `Span` to a `str`. To create suggestions consider using
 +/// [`snippet_with_applicability`] to ensure that the applicability stays correct.
 +///
 +/// # Example
 +/// ```rust,ignore
 +/// // Given two spans one for `value` and one for the `init` expression.
 +/// let value = Vec::new();
 +/// //  ^^^^^   ^^^^^^^^^^
 +/// //  span1   span2
 +///
 +/// // The snipped call would return the corresponding code snippet
 +/// snippet(cx, span1, "..") // -> "value"
 +/// snippet(cx, span2, "..") // -> "Vec::new()"
 +/// ```
 +pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
 +    snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from)
 +}
 +
 +/// Same as [`snippet`], but it adapts the applicability level by following rules:
 +///
 +/// - Applicability level `Unspecified` will never be changed.
 +/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
 +/// - If the default value is used and the applicability level is `MachineApplicable`, change it to
 +/// `HasPlaceholders`
 +pub fn snippet_with_applicability<'a, T: LintContext>(
 +    cx: &T,
 +    span: Span,
 +    default: &'a str,
 +    applicability: &mut Applicability,
 +) -> Cow<'a, str> {
 +    if *applicability != Applicability::Unspecified && span.from_expansion() {
 +        *applicability = Applicability::MaybeIncorrect;
 +    }
 +    snippet_opt(cx, span).map_or_else(
 +        || {
 +            if *applicability == Applicability::MachineApplicable {
 +                *applicability = Applicability::HasPlaceholders;
 +            }
 +            Cow::Borrowed(default)
 +        },
 +        From::from,
 +    )
 +}
 +
 +/// Same as `snippet`, but should only be used when it's clear that the input span is
 +/// not a macro argument.
 +pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
 +    snippet(cx, span.source_callsite(), default)
 +}
 +
 +/// Converts a span to a code snippet. Returns `None` if not available.
 +pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
 +    cx.sess().source_map().span_to_snippet(span).ok()
 +}
 +
 +/// Converts a span (from a block) to a code snippet if available, otherwise use default.
 +///
 +/// This trims the code of indentation, except for the first line. Use it for blocks or block-like
 +/// things which need to be printed as such.
 +///
 +/// The `indent_relative_to` arg can be used, to provide a span, where the indentation of the
 +/// resulting snippet of the given span.
 +///
 +/// # Example
 +///
 +/// ```rust,ignore
 +/// snippet_block(cx, block.span, "..", None)
 +/// // where, `block` is the block of the if expr
 +///     if x {
 +///         y;
 +///     }
 +/// // will return the snippet
 +/// {
 +///     y;
 +/// }
 +/// ```
 +///
 +/// ```rust,ignore
 +/// snippet_block(cx, block.span, "..", Some(if_expr.span))
 +/// // where, `block` is the block of the if expr
 +///     if x {
 +///         y;
 +///     }
 +/// // will return the snippet
 +/// {
 +///         y;
 +///     } // aligned with `if`
 +/// ```
 +/// Note that the first line of the snippet always has 0 indentation.
 +pub fn snippet_block<'a, T: LintContext>(
 +    cx: &T,
 +    span: Span,
 +    default: &'a str,
 +    indent_relative_to: Option<Span>,
 +) -> Cow<'a, str> {
 +    let snip = snippet(cx, span, default);
 +    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
 +    reindent_multiline(snip, true, indent)
 +}
 +
 +/// Same as `snippet_block`, but adapts the applicability level by the rules of
 +/// `snippet_with_applicability`.
 +pub fn snippet_block_with_applicability<'a, T: LintContext>(
 +    cx: &T,
 +    span: Span,
 +    default: &'a str,
 +    indent_relative_to: Option<Span>,
 +    applicability: &mut Applicability,
 +) -> Cow<'a, str> {
 +    let snip = snippet_with_applicability(cx, span, default, applicability);
 +    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
 +    reindent_multiline(snip, true, indent)
 +}
 +
 +/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This
 +/// will result in the macro call, rather then the expansion, if the span is from a child context.
 +/// If the span is not from a child context, it will be used directly instead.
 +///
 +/// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node
 +/// would result in `box []`. If given the context of the address of expression, this function will
 +/// correctly get a snippet of `vec![]`.
 +///
 +/// This will also return whether or not the snippet is a macro call.
 +pub fn snippet_with_context<'a>(
 +    cx: &LateContext<'_>,
 +    span: Span,
 +    outer: SyntaxContext,
 +    default: &'a str,
 +    applicability: &mut Applicability,
 +) -> (Cow<'a, str>, bool) {
 +    let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else(
 +        || {
 +            // The span is from a macro argument, and the outer context is the macro using the argument
 +            if *applicability != Applicability::Unspecified {
 +                *applicability = Applicability::MaybeIncorrect;
 +            }
 +            // TODO: get the argument span.
 +            (span, false)
 +        },
 +        |outer_span| (outer_span, span.ctxt() != outer),
 +    );
 +
 +    (
 +        snippet_with_applicability(cx, span, default, applicability),
 +        is_macro_call,
 +    )
 +}
 +
 +/// Walks the span up to the target context, thereby returning the macro call site if the span is
 +/// inside a macro expansion, or the original span if it is not. Note this will return `None` in the
 +/// case of the span being in a macro expansion, but the target context is from expanding a macro
 +/// argument.
 +///
 +/// Given the following
 +///
 +/// ```rust,ignore
 +/// macro_rules! m { ($e:expr) => { f($e) }; }
 +/// g(m!(0))
 +/// ```
 +///
 +/// If called with a span of the call to `f` and a context of the call to `g` this will return a
 +/// span containing `m!(0)`. However, if called with a span of the literal `0` this will give a span
 +/// containing `0` as the context is the same as the outer context.
 +///
 +/// This will traverse through multiple macro calls. Given the following:
 +///
 +/// ```rust,ignore
 +/// macro_rules! m { ($e:expr) => { n!($e, 0) }; }
 +/// macro_rules! n { ($e:expr, $f:expr) => { f($e, $f) }; }
 +/// g(m!(0))
 +/// ```
 +///
 +/// If called with a span of the call to `f` and a context of the call to `g` this will return a
 +/// span containing `m!(0)`.
 +pub fn walk_span_to_context(span: Span, outer: SyntaxContext) -> Option<Span> {
 +    let outer_span = hygiene::walk_chain(span, outer);
++    (outer_span.ctxt() == outer).then_some(outer_span)
 +}
 +
 +/// Removes block comments from the given `Vec` of lines.
 +///
 +/// # Examples
 +///
 +/// ```rust,ignore
 +/// without_block_comments(vec!["/*", "foo", "*/"]);
 +/// // => vec![]
 +///
 +/// without_block_comments(vec!["bar", "/*", "foo", "*/"]);
 +/// // => vec!["bar"]
 +/// ```
 +pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> {
 +    let mut without = vec![];
 +
 +    let mut nest_level = 0;
 +
 +    for line in lines {
 +        if line.contains("/*") {
 +            nest_level += 1;
 +            continue;
 +        } else if line.contains("*/") {
 +            nest_level -= 1;
 +            continue;
 +        }
 +
 +        if nest_level == 0 {
 +            without.push(line);
 +        }
 +    }
 +
 +    without
 +}
 +
 +/// Trims the whitespace from the start and the end of the span.
 +pub fn trim_span(sm: &SourceMap, span: Span) -> Span {
 +    let data = span.data();
 +    let sf: &_ = &sm.lookup_source_file(data.lo);
 +    let Some(src) = sf.src.as_deref() else {
 +        return span;
 +    };
 +    let Some(snip) = &src.get((data.lo - sf.start_pos).to_usize()..(data.hi - sf.start_pos).to_usize()) else {
 +        return span;
 +    };
 +    let trim_start = snip.len() - snip.trim_start().len();
 +    let trim_end = snip.len() - snip.trim_end().len();
 +    SpanData {
 +        lo: data.lo + BytePos::from_usize(trim_start),
 +        hi: data.hi - BytePos::from_usize(trim_end),
 +        ctxt: data.ctxt,
 +        parent: data.parent,
 +    }
 +    .span()
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use super::{reindent_multiline, without_block_comments};
 +
 +    #[test]
 +    fn test_reindent_multiline_single_line() {
 +        assert_eq!("", reindent_multiline("".into(), false, None));
 +        assert_eq!("...", reindent_multiline("...".into(), false, None));
 +        assert_eq!("...", reindent_multiline("    ...".into(), false, None));
 +        assert_eq!("...", reindent_multiline("\t...".into(), false, None));
 +        assert_eq!("...", reindent_multiline("\t\t...".into(), false, None));
 +    }
 +
 +    #[test]
 +    #[rustfmt::skip]
 +    fn test_reindent_multiline_block() {
 +        assert_eq!("\
 +    if x {
 +        y
 +    } else {
 +        z
 +    }", reindent_multiline("    if x {
 +            y
 +        } else {
 +            z
 +        }".into(), false, None));
 +        assert_eq!("\
 +    if x {
 +    \ty
 +    } else {
 +    \tz
 +    }", reindent_multiline("    if x {
 +        \ty
 +        } else {
 +        \tz
 +        }".into(), false, None));
 +    }
 +
 +    #[test]
 +    #[rustfmt::skip]
 +    fn test_reindent_multiline_empty_line() {
 +        assert_eq!("\
 +    if x {
 +        y
 +
 +    } else {
 +        z
 +    }", reindent_multiline("    if x {
 +            y
 +
 +        } else {
 +            z
 +        }".into(), false, None));
 +    }
 +
 +    #[test]
 +    #[rustfmt::skip]
 +    fn test_reindent_multiline_lines_deeper() {
 +        assert_eq!("\
 +        if x {
 +            y
 +        } else {
 +            z
 +        }", reindent_multiline("\
 +    if x {
 +        y
 +    } else {
 +        z
 +    }".into(), true, Some(8)));
 +    }
 +
 +    #[test]
 +    fn test_without_block_comments_lines_without_block_comments() {
 +        let result = without_block_comments(vec!["/*", "", "*/"]);
 +        println!("result: {:?}", result);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]);
 +        assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]);
 +
 +        let result = without_block_comments(vec!["/* rust", "", "*/"]);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["/* one-line comment */"]);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["foo", "bar", "baz"]);
 +        assert_eq!(result, vec!["foo", "bar", "baz"]);
 +    }
 +}
index 4326a103d44b70aad43661e1ec6bfa9f69643b96,0000000000000000000000000000000000000000..bad291dfc2513f6a0318437107dff89432b03e55
mode 100644,000000..100644
--- /dev/null
@@@ -1,1085 -1,0 +1,1099 @@@
- use std::iter;
 +//! Contains utility functions to generate suggestions.
 +#![deny(clippy::missing_docs_in_private_items)]
 +
 +use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite};
++use crate::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_infer::infer::TyCtxtInferExt;
 +use rustc_lint::{EarlyContext, LateContext, LintContext};
 +use rustc_middle::hir::place::ProjectionKind;
 +use rustc_middle::mir::{FakeReadCause, Mutability};
 +use rustc_middle::ty;
 +use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext};
 +use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 +use std::borrow::Cow;
 +use std::fmt::{Display, Write as _};
-         let (call_args, inputs) = match parent_expr.kind {
 +use std::ops::{Add, Neg, Not, Sub};
 +
 +/// A helper type to build suggestion correctly handling parentheses.
 +#[derive(Clone, PartialEq)]
 +pub enum Sugg<'a> {
 +    /// An expression that never needs parentheses such as `1337` or `[0; 42]`.
 +    NonParen(Cow<'a, str>),
 +    /// An expression that does not fit in other variants.
 +    MaybeParen(Cow<'a, str>),
 +    /// A binary operator expression, including `as`-casts and explicit type
 +    /// coercion.
 +    BinOp(AssocOp, Cow<'a, str>, Cow<'a, str>),
 +}
 +
 +/// Literal constant `0`, for convenience.
 +pub const ZERO: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("0"));
 +/// Literal constant `1`, for convenience.
 +pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
 +/// a constant represents an empty string, for convenience.
 +pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed(""));
 +
 +impl Display for Sugg<'_> {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
 +        match *self {
 +            Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) => s.fmt(f),
 +            Sugg::BinOp(op, ref lhs, ref rhs) => binop_to_string(op, lhs, rhs).fmt(f),
 +        }
 +    }
 +}
 +
 +#[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 {
 +            let snip = snippet_with_applicability(cx, expr.span, default, applicability);
 +            Sugg::NonParen(snip)
 +        }
 +    }
 +
 +    /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
 +    /// function variants of `Sugg`, since these use different snippet functions.
 +    fn hir_from_snippet(expr: &hir::Expr<'_>, get_snippet: impl Fn(Span) -> Cow<'a, str>) -> Self {
 +        if let Some(range) = higher::Range::hir(expr) {
 +            let op = match range.limits {
 +                ast::RangeLimits::HalfOpen => AssocOp::DotDot,
 +                ast::RangeLimits::Closed => AssocOp::DotDotEq,
 +            };
 +            let start = range.start.map_or("".into(), |expr| get_snippet(expr.span));
 +            let end = range.end.map_or("".into(), |expr| get_snippet(expr.span));
 +
 +            return Sugg::BinOp(op, start, end);
 +        }
 +
 +        match expr.kind {
 +            hir::ExprKind::AddrOf(..)
 +            | hir::ExprKind::Box(..)
 +            | hir::ExprKind::If(..)
 +            | hir::ExprKind::Let(..)
 +            | hir::ExprKind::Closure { .. }
 +            | hir::ExprKind::Unary(..)
 +            | hir::ExprKind::Match(..) => Sugg::MaybeParen(get_snippet(expr.span)),
 +            hir::ExprKind::Continue(..)
 +            | hir::ExprKind::Yield(..)
 +            | hir::ExprKind::Array(..)
 +            | hir::ExprKind::Block(..)
 +            | hir::ExprKind::Break(..)
 +            | hir::ExprKind::Call(..)
 +            | hir::ExprKind::Field(..)
 +            | hir::ExprKind::Index(..)
 +            | hir::ExprKind::InlineAsm(..)
 +            | hir::ExprKind::ConstBlock(..)
 +            | hir::ExprKind::Lit(..)
 +            | hir::ExprKind::Loop(..)
 +            | hir::ExprKind::MethodCall(..)
 +            | hir::ExprKind::Path(..)
 +            | hir::ExprKind::Repeat(..)
 +            | hir::ExprKind::Ret(..)
 +            | hir::ExprKind::Struct(..)
 +            | hir::ExprKind::Tup(..)
 +            | hir::ExprKind::DropTemps(_)
 +            | hir::ExprKind::Err => Sugg::NonParen(get_snippet(expr.span)),
 +            hir::ExprKind::Assign(lhs, rhs, _) => {
 +                Sugg::BinOp(AssocOp::Assign, get_snippet(lhs.span), get_snippet(rhs.span))
 +            },
 +            hir::ExprKind::AssignOp(op, lhs, rhs) => {
 +                Sugg::BinOp(hirbinop2assignop(op), get_snippet(lhs.span), get_snippet(rhs.span))
 +            },
 +            hir::ExprKind::Binary(op, lhs, rhs) => Sugg::BinOp(
 +                AssocOp::from_ast_binop(op.node.into()),
 +                get_snippet(lhs.span),
 +                get_snippet(rhs.span),
 +            ),
 +            hir::ExprKind::Cast(lhs, ty) => Sugg::BinOp(AssocOp::As, get_snippet(lhs.span), get_snippet(ty.span)),
 +            hir::ExprKind::Type(lhs, ty) => Sugg::BinOp(AssocOp::Colon, get_snippet(lhs.span), get_snippet(ty.span)),
 +        }
 +    }
 +
 +    /// Prepare a suggestion from an expression.
 +    pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
 +        use rustc_ast::ast::RangeLimits;
 +
 +        let get_whole_snippet = || {
 +            if expr.span.from_expansion() {
 +                snippet_with_macro_callsite(cx, expr.span, default)
 +            } else {
 +                snippet(cx, expr.span, default)
 +            }
 +        };
 +
 +        match expr.kind {
 +            ast::ExprKind::AddrOf(..)
 +            | ast::ExprKind::Box(..)
 +            | ast::ExprKind::Closure { .. }
 +            | ast::ExprKind::If(..)
 +            | ast::ExprKind::Let(..)
 +            | ast::ExprKind::Unary(..)
 +            | ast::ExprKind::Match(..) => Sugg::MaybeParen(get_whole_snippet()),
 +            ast::ExprKind::Async(..)
 +            | ast::ExprKind::Block(..)
 +            | ast::ExprKind::Break(..)
 +            | ast::ExprKind::Call(..)
 +            | ast::ExprKind::Continue(..)
 +            | ast::ExprKind::Yield(..)
 +            | ast::ExprKind::Field(..)
 +            | ast::ExprKind::ForLoop(..)
 +            | ast::ExprKind::Index(..)
 +            | ast::ExprKind::InlineAsm(..)
 +            | ast::ExprKind::ConstBlock(..)
 +            | ast::ExprKind::Lit(..)
 +            | ast::ExprKind::Loop(..)
 +            | ast::ExprKind::MacCall(..)
 +            | ast::ExprKind::MethodCall(..)
 +            | ast::ExprKind::Paren(..)
 +            | ast::ExprKind::Underscore
 +            | ast::ExprKind::Path(..)
 +            | ast::ExprKind::Repeat(..)
 +            | ast::ExprKind::Ret(..)
 +            | ast::ExprKind::Yeet(..)
 +            | ast::ExprKind::Struct(..)
 +            | ast::ExprKind::Try(..)
 +            | ast::ExprKind::TryBlock(..)
 +            | ast::ExprKind::Tup(..)
 +            | ast::ExprKind::Array(..)
 +            | ast::ExprKind::While(..)
 +            | ast::ExprKind::Await(..)
 +            | ast::ExprKind::Err => Sugg::NonParen(get_whole_snippet()),
 +            ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
 +                AssocOp::DotDot,
 +                lhs.as_ref().map_or("".into(), |lhs| snippet(cx, lhs.span, default)),
 +                rhs.as_ref().map_or("".into(), |rhs| snippet(cx, rhs.span, default)),
 +            ),
 +            ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp(
 +                AssocOp::DotDotEq,
 +                lhs.as_ref().map_or("".into(), |lhs| snippet(cx, lhs.span, default)),
 +                rhs.as_ref().map_or("".into(), |rhs| snippet(cx, rhs.span, default)),
 +            ),
 +            ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp(
 +                AssocOp::Assign,
 +                snippet(cx, lhs.span, default),
 +                snippet(cx, rhs.span, default),
 +            ),
 +            ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
 +                astbinop2assignop(op),
 +                snippet(cx, lhs.span, default),
 +                snippet(cx, rhs.span, default),
 +            ),
 +            ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
 +                AssocOp::from_ast_binop(op.node),
 +                snippet(cx, lhs.span, default),
 +                snippet(cx, rhs.span, default),
 +            ),
 +            ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp(
 +                AssocOp::As,
 +                snippet(cx, lhs.span, default),
 +                snippet(cx, ty.span, default),
 +            ),
 +            ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
 +                AssocOp::Colon,
 +                snippet(cx, lhs.span, default),
 +                snippet(cx, ty.span, default),
 +            ),
 +        }
 +    }
 +
 +    /// Convenience method to create the `<lhs> && <rhs>` suggestion.
 +    pub fn and(self, rhs: &Self) -> Sugg<'static> {
 +        make_binop(ast::BinOpKind::And, &self, rhs)
 +    }
 +
 +    /// Convenience method to create the `<lhs> & <rhs>` suggestion.
 +    pub fn bit_and(self, rhs: &Self) -> Sugg<'static> {
 +        make_binop(ast::BinOpKind::BitAnd, &self, rhs)
 +    }
 +
 +    /// Convenience method to create the `<lhs> as <rhs>` suggestion.
 +    pub fn as_ty<R: Display>(self, rhs: R) -> Sugg<'static> {
 +        make_assoc(AssocOp::As, &self, &Sugg::NonParen(rhs.to_string().into()))
 +    }
 +
 +    /// Convenience method to create the `&<expr>` suggestion.
 +    pub fn addr(self) -> Sugg<'static> {
 +        make_unop("&", self)
 +    }
 +
 +    /// Convenience method to create the `&mut <expr>` suggestion.
 +    pub fn mut_addr(self) -> Sugg<'static> {
 +        make_unop("&mut ", self)
 +    }
 +
 +    /// Convenience method to create the `*<expr>` suggestion.
 +    pub fn deref(self) -> Sugg<'static> {
 +        make_unop("*", self)
 +    }
 +
 +    /// Convenience method to create the `&*<expr>` suggestion. Currently this
 +    /// is needed because `sugg.deref().addr()` produces an unnecessary set of
 +    /// parentheses around the deref.
 +    pub fn addr_deref(self) -> Sugg<'static> {
 +        make_unop("&*", self)
 +    }
 +
 +    /// Convenience method to create the `&mut *<expr>` suggestion. Currently
 +    /// this is needed because `sugg.deref().mut_addr()` produces an unnecessary
 +    /// set of parentheses around the deref.
 +    pub fn mut_addr_deref(self) -> Sugg<'static> {
 +        make_unop("&mut *", self)
 +    }
 +
 +    /// Convenience method to transform suggestion into a return call
 +    pub fn make_return(self) -> Sugg<'static> {
 +        Sugg::NonParen(Cow::Owned(format!("return {}", self)))
 +    }
 +
 +    /// Convenience method to transform suggestion into a block
 +    /// where the suggestion is a trailing expression
 +    pub fn blockify(self) -> Sugg<'static> {
 +        Sugg::NonParen(Cow::Owned(format!("{{ {} }}", self)))
 +    }
 +
 +    /// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>`
 +    /// suggestion.
 +    pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> {
 +        match limit {
 +            ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, end),
 +            ast::RangeLimits::Closed => make_assoc(AssocOp::DotDotEq, &self, end),
 +        }
 +    }
 +
 +    /// Adds parentheses to any expression that might need them. Suitable to the
 +    /// `self` argument of a method call
 +    /// (e.g., to build `bar.foo()` or `(1 + 2).foo()`).
 +    #[must_use]
 +    pub fn maybe_par(self) -> Self {
 +        match self {
 +            Sugg::NonParen(..) => self,
 +            // `(x)` and `(x).y()` both don't need additional parens.
 +            Sugg::MaybeParen(sugg) => {
 +                if has_enclosing_paren(&sugg) {
 +                    Sugg::MaybeParen(sugg)
 +                } else {
 +                    Sugg::NonParen(format!("({})", sugg).into())
 +                }
 +            },
 +            Sugg::BinOp(op, lhs, rhs) => {
 +                let sugg = binop_to_string(op, &lhs, &rhs);
 +                Sugg::NonParen(format!("({})", sugg).into())
 +            },
 +        }
 +    }
 +}
 +
 +/// Generates a string from the operator and both sides.
 +fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String {
 +    match op {
 +        AssocOp::Add
 +        | AssocOp::Subtract
 +        | AssocOp::Multiply
 +        | AssocOp::Divide
 +        | AssocOp::Modulus
 +        | AssocOp::LAnd
 +        | AssocOp::LOr
 +        | AssocOp::BitXor
 +        | AssocOp::BitAnd
 +        | AssocOp::BitOr
 +        | AssocOp::ShiftLeft
 +        | AssocOp::ShiftRight
 +        | AssocOp::Equal
 +        | AssocOp::Less
 +        | AssocOp::LessEqual
 +        | AssocOp::NotEqual
 +        | AssocOp::Greater
 +        | AssocOp::GreaterEqual => format!(
 +            "{} {} {}",
 +            lhs,
 +            op.to_ast_binop().expect("Those are AST ops").to_string(),
 +            rhs
 +        ),
 +        AssocOp::Assign => format!("{} = {}", lhs, rhs),
 +        AssocOp::AssignOp(op) => {
 +            format!("{} {}= {}", lhs, token_kind_to_string(&token::BinOp(op)), rhs)
 +        },
 +        AssocOp::As => format!("{} as {}", lhs, rhs),
 +        AssocOp::DotDot => format!("{}..{}", lhs, rhs),
 +        AssocOp::DotDotEq => format!("{}..={}", lhs, rhs),
 +        AssocOp::Colon => format!("{}: {}", lhs, rhs),
 +    }
 +}
 +
 +/// Return `true` if `sugg` is enclosed in parenthesis.
 +pub fn has_enclosing_paren(sugg: impl AsRef<str>) -> bool {
 +    let mut chars = sugg.as_ref().chars();
 +    if chars.next() == Some('(') {
 +        let mut depth = 1;
 +        for c in &mut chars {
 +            if c == '(' {
 +                depth += 1;
 +            } else if c == ')' {
 +                depth -= 1;
 +            }
 +            if depth == 0 {
 +                break;
 +            }
 +        }
 +        chars.next().is_none()
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Copied from the rust standard library, and then edited
 +macro_rules! forward_binop_impls_to_ref {
 +    (impl $imp:ident, $method:ident for $t:ty, type Output = $o:ty) => {
 +        impl $imp<$t> for &$t {
 +            type Output = $o;
 +
 +            fn $method(self, other: $t) -> $o {
 +                $imp::$method(self, &other)
 +            }
 +        }
 +
 +        impl $imp<&$t> for $t {
 +            type Output = $o;
 +
 +            fn $method(self, other: &$t) -> $o {
 +                $imp::$method(&self, other)
 +            }
 +        }
 +
 +        impl $imp for $t {
 +            type Output = $o;
 +
 +            fn $method(self, other: $t) -> $o {
 +                $imp::$method(&self, &other)
 +            }
 +        }
 +    };
 +}
 +
 +impl Add for &Sugg<'_> {
 +    type Output = Sugg<'static>;
 +    fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> {
 +        make_binop(ast::BinOpKind::Add, self, rhs)
 +    }
 +}
 +
 +impl Sub for &Sugg<'_> {
 +    type Output = Sugg<'static>;
 +    fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> {
 +        make_binop(ast::BinOpKind::Sub, self, rhs)
 +    }
 +}
 +
 +forward_binop_impls_to_ref!(impl Add, add for Sugg<'_>, type Output = Sugg<'static>);
 +forward_binop_impls_to_ref!(impl Sub, sub for Sugg<'_>, type Output = Sugg<'static>);
 +
 +impl Neg for Sugg<'_> {
 +    type Output = Sugg<'static>;
 +    fn neg(self) -> Sugg<'static> {
 +        make_unop("-", self)
 +    }
 +}
 +
 +impl<'a> Not for Sugg<'a> {
 +    type Output = Sugg<'a>;
 +    fn not(self) -> Sugg<'a> {
 +        use AssocOp::{Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual};
 +
 +        if let Sugg::BinOp(op, lhs, rhs) = self {
 +            let to_op = match op {
 +                Equal => NotEqual,
 +                NotEqual => Equal,
 +                Less => GreaterEqual,
 +                GreaterEqual => Less,
 +                Greater => LessEqual,
 +                LessEqual => Greater,
 +                _ => return make_unop("!", Sugg::BinOp(op, lhs, rhs)),
 +            };
 +            Sugg::BinOp(to_op, lhs, rhs)
 +        } else {
 +            make_unop("!", self)
 +        }
 +    }
 +}
 +
 +/// Helper type to display either `foo` or `(foo)`.
 +struct ParenHelper<T> {
 +    /// `true` if parentheses are needed.
 +    paren: bool,
 +    /// The main thing to display.
 +    wrapped: T,
 +}
 +
 +impl<T> ParenHelper<T> {
 +    /// Builds a `ParenHelper`.
 +    fn new(paren: bool, wrapped: T) -> Self {
 +        Self { paren, wrapped }
 +    }
 +}
 +
 +impl<T: Display> Display for ParenHelper<T> {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
 +        if self.paren {
 +            write!(f, "({})", self.wrapped)
 +        } else {
 +            self.wrapped.fmt(f)
 +        }
 +    }
 +}
 +
 +/// Builds the string for `<op><expr>` adding parenthesis when necessary.
 +///
 +/// For convenience, the operator is taken as a string because all unary
 +/// operators have the same
 +/// precedence.
 +pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> {
 +    Sugg::MaybeParen(format!("{}{}", op, expr.maybe_par()).into())
 +}
 +
 +/// Builds the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
 +///
 +/// Precedence of shift operator relative to other arithmetic operation is
 +/// often confusing so
 +/// parenthesis will always be added for a mix of these.
 +pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
 +    /// Returns `true` if the operator is a shift operator `<<` or `>>`.
 +    fn is_shift(op: AssocOp) -> bool {
 +        matches!(op, AssocOp::ShiftLeft | AssocOp::ShiftRight)
 +    }
 +
 +    /// Returns `true` if the operator is an arithmetic operator
 +    /// (i.e., `+`, `-`, `*`, `/`, `%`).
 +    fn is_arith(op: AssocOp) -> bool {
 +        matches!(
 +            op,
 +            AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus
 +        )
 +    }
 +
 +    /// Returns `true` if the operator `op` needs parenthesis with the operator
 +    /// `other` in the direction `dir`.
 +    fn needs_paren(op: AssocOp, other: AssocOp, dir: Associativity) -> bool {
 +        other.precedence() < op.precedence()
 +            || (other.precedence() == op.precedence()
 +                && ((op != other && associativity(op) != dir)
 +                    || (op == other && associativity(op) != Associativity::Both)))
 +            || is_shift(op) && is_arith(other)
 +            || is_shift(other) && is_arith(op)
 +    }
 +
 +    let lhs_paren = if let Sugg::BinOp(lop, _, _) = *lhs {
 +        needs_paren(op, lop, Associativity::Left)
 +    } else {
 +        false
 +    };
 +
 +    let rhs_paren = if let Sugg::BinOp(rop, _, _) = *rhs {
 +        needs_paren(op, rop, Associativity::Right)
 +    } else {
 +        false
 +    };
 +
 +    let lhs = ParenHelper::new(lhs_paren, lhs).to_string();
 +    let rhs = ParenHelper::new(rhs_paren, rhs).to_string();
 +    Sugg::BinOp(op, lhs.into(), rhs.into())
 +}
 +
 +/// Convenience wrapper around `make_assoc` and `AssocOp::from_ast_binop`.
 +pub fn make_binop(op: ast::BinOpKind, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
 +    make_assoc(AssocOp::from_ast_binop(op), lhs, rhs)
 +}
 +
 +#[derive(PartialEq, Eq, Clone, Copy)]
 +/// Operator associativity.
 +enum Associativity {
 +    /// The operator is both left-associative and right-associative.
 +    Both,
 +    /// The operator is left-associative.
 +    Left,
 +    /// The operator is not associative.
 +    None,
 +    /// The operator is right-associative.
 +    Right,
 +}
 +
 +/// Returns the associativity/fixity of an operator. The difference with
 +/// `AssocOp::fixity` is that an operator can be both left and right associative
 +/// (such as `+`: `a + b + c == (a + b) + c == a + (b + c)`.
 +///
 +/// Chained `as` and explicit `:` type coercion never need inner parenthesis so
 +/// they are considered
 +/// associative.
 +#[must_use]
 +fn associativity(op: AssocOp) -> Associativity {
 +    use rustc_ast::util::parser::AssocOp::{
 +        Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Colon, Divide, DotDot, DotDotEq, Equal, Greater,
 +        GreaterEqual, LAnd, LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract,
 +    };
 +
 +    match op {
 +        Assign | AssignOp(_) => Associativity::Right,
 +        Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As | Colon => Associativity::Both,
 +        Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight
 +        | Subtract => Associativity::Left,
 +        DotDot | DotDotEq => Associativity::None,
 +    }
 +}
 +
 +/// Converts a `hir::BinOp` to the corresponding assigning binary operator.
 +fn hirbinop2assignop(op: hir::BinOp) -> AssocOp {
 +    use rustc_ast::token::BinOpToken::{And, Caret, Minus, Or, Percent, Plus, Shl, Shr, Slash, Star};
 +
 +    AssocOp::AssignOp(match op.node {
 +        hir::BinOpKind::Add => Plus,
 +        hir::BinOpKind::BitAnd => And,
 +        hir::BinOpKind::BitOr => Or,
 +        hir::BinOpKind::BitXor => Caret,
 +        hir::BinOpKind::Div => Slash,
 +        hir::BinOpKind::Mul => Star,
 +        hir::BinOpKind::Rem => Percent,
 +        hir::BinOpKind::Shl => Shl,
 +        hir::BinOpKind::Shr => Shr,
 +        hir::BinOpKind::Sub => Minus,
 +
 +        hir::BinOpKind::And
 +        | hir::BinOpKind::Eq
 +        | hir::BinOpKind::Ge
 +        | hir::BinOpKind::Gt
 +        | hir::BinOpKind::Le
 +        | hir::BinOpKind::Lt
 +        | hir::BinOpKind::Ne
 +        | hir::BinOpKind::Or => panic!("This operator does not exist"),
 +    })
 +}
 +
 +/// Converts an `ast::BinOp` to the corresponding assigning binary operator.
 +fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
 +    use rustc_ast::ast::BinOpKind::{
 +        Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
 +    };
 +    use rustc_ast::token::BinOpToken;
 +
 +    AssocOp::AssignOp(match op.node {
 +        Add => BinOpToken::Plus,
 +        BitAnd => BinOpToken::And,
 +        BitOr => BinOpToken::Or,
 +        BitXor => BinOpToken::Caret,
 +        Div => BinOpToken::Slash,
 +        Mul => BinOpToken::Star,
 +        Rem => BinOpToken::Percent,
 +        Shl => BinOpToken::Shl,
 +        Shr => BinOpToken::Shr,
 +        Sub => BinOpToken::Minus,
 +        And | Eq | Ge | Gt | Le | Lt | Ne | Or => panic!("This operator does not exist"),
 +    })
 +}
 +
 +/// Returns the indentation before `span` if there are nothing but `[ \t]`
 +/// before it on its line.
 +fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> {
 +    let lo = cx.sess().source_map().lookup_char_pos(span.lo());
 +    lo.file
 +        .get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */)
 +        .and_then(|line| {
 +            if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
 +                // We can mix char and byte positions here because we only consider `[ \t]`.
 +                if lo.col == CharPos(pos) {
 +                    Some(line[..pos].into())
 +                } else {
 +                    None
 +                }
 +            } else {
 +                None
 +            }
 +        })
 +}
 +
 +/// Convenience extension trait for `Diagnostic`.
 +pub trait DiagnosticExt<T: LintContext> {
 +    /// Suggests to add an attribute to an item.
 +    ///
 +    /// Correctly handles indentation of the attribute and item.
 +    ///
 +    /// # Example
 +    ///
 +    /// ```rust,ignore
 +    /// diag.suggest_item_with_attr(cx, item, "#[derive(Default)]");
 +    /// ```
 +    fn suggest_item_with_attr<D: Display + ?Sized>(
 +        &mut self,
 +        cx: &T,
 +        item: Span,
 +        msg: &str,
 +        attr: &D,
 +        applicability: Applicability,
 +    );
 +
 +    /// Suggest to add an item before another.
 +    ///
 +    /// The item should not be indented (except for inner indentation).
 +    ///
 +    /// # Example
 +    ///
 +    /// ```rust,ignore
 +    /// diag.suggest_prepend_item(cx, item,
 +    /// "fn foo() {
 +    ///     bar();
 +    /// }");
 +    /// ```
 +    fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability);
 +
 +    /// Suggest to completely remove an item.
 +    ///
 +    /// This will remove an item and all following whitespace until the next non-whitespace
 +    /// character. This should work correctly if item is on the same indentation level as the
 +    /// following item.
 +    ///
 +    /// # Example
 +    ///
 +    /// ```rust,ignore
 +    /// diag.suggest_remove_item(cx, item, "remove this")
 +    /// ```
 +    fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability);
 +}
 +
 +impl<T: LintContext> DiagnosticExt<T> for rustc_errors::Diagnostic {
 +    fn suggest_item_with_attr<D: Display + ?Sized>(
 +        &mut self,
 +        cx: &T,
 +        item: Span,
 +        msg: &str,
 +        attr: &D,
 +        applicability: Applicability,
 +    ) {
 +        if let Some(indent) = indentation(cx, item) {
 +            let span = item.with_hi(item.lo());
 +
 +            self.span_suggestion(span, msg, format!("{}\n{}", attr, indent), applicability);
 +        }
 +    }
 +
 +    fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability) {
 +        if let Some(indent) = indentation(cx, item) {
 +            let span = item.with_hi(item.lo());
 +
 +            let mut first = true;
 +            let new_item = new_item
 +                .lines()
 +                .map(|l| {
 +                    if first {
 +                        first = false;
 +                        format!("{}\n", l)
 +                    } else {
 +                        format!("{}{}\n", indent, l)
 +                    }
 +                })
 +                .collect::<String>();
 +
 +            self.span_suggestion(span, msg, format!("{}\n{}", new_item, indent), applicability);
 +        }
 +    }
 +
 +    fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability) {
 +        let mut remove_span = item;
 +        let hi = cx.sess().source_map().next_point(remove_span).hi();
 +        let fmpos = cx.sess().source_map().lookup_byte_offset(hi);
 +
 +        if let Some(ref src) = fmpos.sf.src {
 +            let non_whitespace_offset = src[fmpos.pos.to_usize()..].find(|c| c != ' ' && c != '\t' && c != '\n');
 +
 +            if let Some(non_whitespace_offset) = non_whitespace_offset {
 +                remove_span = remove_span
 +                    .with_hi(remove_span.hi() + BytePos(non_whitespace_offset.try_into().expect("offset too large")));
 +            }
 +        }
 +
 +        self.span_suggestion(remove_span, msg, "", 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);
 +        cx.tcx.infer_ctxt().enter(|infcx| {
 +            ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results())
 +                .consume_body(closure_body);
 +        });
 +
 +        if !visitor.suggestion_start.is_empty() {
 +            return Some(DerefClosure {
 +                applicability: visitor.applicability,
 +                suggestion: visitor.finish(),
 +            });
 +        }
 +    }
 +    None
 +}
 +
 +/// Visitor struct used for tracking down
 +/// dereferencing and borrowing of closure's args
 +struct DerefDelegate<'a, 'tcx> {
 +    /// The late context of the lint
 +    cx: &'a LateContext<'tcx>,
 +    /// The span of the input closure to adapt
 +    closure_span: Span,
 +    /// Indicates if the arg of the closure is a type annotated double reference
 +    closure_arg_is_type_annotated_double_ref: bool,
 +    /// last position of the span to gradually build the suggestion
 +    next_pos: BytePos,
 +    /// starting part of the gradually built suggestion
 +    suggestion_start: String,
 +    /// confidence on the built suggestion
 +    applicability: Applicability,
 +}
 +
 +impl<'tcx> DerefDelegate<'_, 'tcx> {
 +    /// build final suggestion:
 +    /// - create the ending part of suggestion
 +    /// - concatenate starting and ending parts
 +    /// - potentially remove needless borrowing
 +    pub fn finish(&mut self) -> String {
 +        let end_span = Span::new(self.next_pos, self.closure_span.hi(), self.closure_span.ctxt(), None);
 +        let end_snip = snippet_with_applicability(self.cx, end_span, "..", &mut self.applicability);
 +        let sugg = format!("{}{}", self.suggestion_start, end_snip);
 +        if self.closure_arg_is_type_annotated_double_ref {
 +            sugg.replacen('&', "", 1)
 +        } else {
 +            sugg
 +        }
 +    }
 +
 +    /// indicates whether the function from `parent_expr` takes its args by double reference
 +    fn func_takes_arg_by_double_ref(&self, parent_expr: &'tcx hir::Expr<'_>, cmt_hir_id: HirId) -> bool {
-                 if let Some(method_did) = self.cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) {
-                     (call_args, self.cx.tcx.fn_sig(method_did).skip_binder().inputs())
++        let ty = match parent_expr.kind {
 +            ExprKind::MethodCall(_, call_args, _) => {
-                 let typ = self.cx.typeck_results().expr_ty(func);
-                 (call_args, typ.fn_sig(self.cx.tcx).skip_binder().inputs())
++                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())
++                {
++                    call_args
++                        .iter()
++                        .position(|arg| arg.hir_id == cmt_hir_id)
++                        .map(|i| sig.inputs()[i])
 +                } else {
 +                    return false;
 +                }
 +            },
 +            ExprKind::Call(func, call_args) => {
-         iter::zip(call_args, inputs)
-             .any(|(arg, ty)| arg.hir_id == cmt_hir_id && matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref()))
++                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, _: &rustc_typeck::expr_use_visitor::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 a426fa1b0ffcfc927eb90dc075162f04dae1c21c,0000000000000000000000000000000000000000..a05d633d980c3247b18f9e94140d379a80dd9c9c
mode 100644,000000..100644
--- /dev/null
@@@ -1,791 -1,0 +1,829 @@@
- /// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
 +//! Util methods for [`rustc_middle::ty`]
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use core::ops::ControlFlow;
 +use rustc_ast::ast::Mutability;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_hir as hir;
 +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::LateContext;
 +use rustc_middle::mir::interpret::{ConstValue, Scalar};
 +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
 +use rustc_middle::ty::{
 +    self, AdtDef, Binder, BoundRegion, DefIdTree, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, ProjectionTy,
 +    Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr,
 +};
 +use rustc_span::symbol::Ident;
 +use rustc_span::{sym, Span, Symbol, DUMMY_SP};
 +use rustc_target::abi::{Size, VariantIdx};
 +use rustc_trait_selection::infer::InferCtxtExt;
 +use rustc_trait_selection::traits::query::normalize::AtExt;
 +use std::iter;
 +
 +use crate::{match_def_path, path_res, paths};
 +
 +// Checks if the given type implements copy.
 +pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env)
 +}
 +
 +/// Checks whether a type can be partially moved.
 +pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    if has_drop(cx, ty) || is_copy(cx, ty) {
 +        return false;
 +    }
 +    match ty.kind() {
 +        ty::Param(_) => false,
 +        ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))),
 +        _ => true,
 +    }
 +}
 +
 +/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
 +pub fn contains_ty<'tcx>(ty: Ty<'tcx>, other_ty: Ty<'tcx>) -> bool {
 +    ty.walk().any(|inner| match inner.unpack() {
 +        GenericArgKind::Type(inner_ty) => other_ty == inner_ty,
 +        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
 +    })
 +}
 +
 +/// Walks into `ty` and returns `true` if any inner type is an instance of the given adt
 +/// constructor.
 +pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool {
 +    ty.walk().any(|inner| match inner.unpack() {
 +        GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt),
 +        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
 +    })
 +}
 +
 +/// Resolves `<T as Iterator>::Item` for `T`
 +/// Do not invoke without first verifying that the type implements `Iterator`
 +pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
 +    cx.tcx
 +        .get_diagnostic_item(sym::Iterator)
 +        .and_then(|iter_did| get_associated_type(cx, ty, iter_did, "Item"))
 +}
 +
 +/// Returns the associated type `name` for `ty` as an implementation of `trait_id`.
 +/// Do not invoke without first verifying that the type implements the trait.
 +pub fn get_associated_type<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: Ty<'tcx>,
 +    trait_id: DefId,
 +    name: &str,
 +) -> Option<Ty<'tcx>> {
 +    cx.tcx
 +        .associated_items(trait_id)
 +        .find_by_name_and_kind(cx.tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id)
 +        .and_then(|assoc| {
 +            let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[]));
 +            cx.tcx.try_normalize_erasing_regions(cx.param_env, proj).ok()
 +        })
 +}
 +
 +/// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type
 +/// implements a trait marked with a diagnostic item use [`implements_trait`].
 +///
 +/// For a further exploitation what diagnostic items are see [diagnostic items] in
 +/// rustc-dev-guide.
 +///
 +/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
 +pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symbol> {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()),
 +        _ => None,
 +    }
 +}
 +
 +/// Returns true if ty has `iter` or `iter_mut` methods
 +pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
 +    // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
 +    // exists and has the desired signature. Unfortunately FnCtxt is not exported
 +    // so we can't use its `lookup_method` method.
 +    let into_iter_collections: &[Symbol] = &[
 +        sym::Vec,
 +        sym::Option,
 +        sym::Result,
 +        sym::BTreeMap,
 +        sym::BTreeSet,
 +        sym::VecDeque,
 +        sym::LinkedList,
 +        sym::BinaryHeap,
 +        sym::HashSet,
 +        sym::HashMap,
 +        sym::PathBuf,
 +        sym::Path,
 +        sym::Receiver,
 +    ];
 +
 +    let ty_to_check = match probably_ref_ty.kind() {
 +        ty::Ref(_, ty_to_check, _) => *ty_to_check,
 +        _ => probably_ref_ty,
 +    };
 +
 +    let def_id = match ty_to_check.kind() {
 +        ty::Array(..) => return Some(sym::array),
 +        ty::Slice(..) => return Some(sym::slice),
 +        ty::Adt(adt, _) => adt.did(),
 +        _ => return None,
 +    };
 +
 +    for &name in into_iter_collections {
 +        if cx.tcx.is_diagnostic_item(name, def_id) {
 +            return Some(cx.tcx.item_name(def_id));
 +        }
 +    }
 +    None
 +}
 +
 +/// Checks whether a type implements a trait.
 +/// The function returns false in case the type contains an inference variable.
 +///
 +/// See:
 +/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`].
 +/// * [Common tools for writing lints] for an example how to use this function and other options.
 +///
-     Sig(Binder<'tcx, FnSig<'tcx>>),
++/// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
 +pub fn implements_trait<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: Ty<'tcx>,
 +    trait_id: DefId,
 +    ty_params: &[GenericArg<'tcx>],
 +) -> bool {
 +    implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params)
 +}
 +
 +/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
 +pub fn implements_trait_with_env<'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    param_env: ParamEnv<'tcx>,
 +    ty: Ty<'tcx>,
 +    trait_id: DefId,
 +    ty_params: &[GenericArg<'tcx>],
 +) -> bool {
 +    // Clippy shouldn't have infer types
 +    assert!(!ty.needs_infer());
 +
 +    let ty = tcx.erase_regions(ty);
 +    if ty.has_escaping_bound_vars() {
 +        return false;
 +    }
 +    let ty_params = tcx.mk_substs(ty_params.iter());
 +    tcx.infer_ctxt().enter(|infcx| {
 +        infcx
 +            .type_implements_trait(trait_id, ty, ty_params, param_env)
 +            .must_apply_modulo_regions()
 +    })
 +}
 +
 +/// Checks whether this type implements `Drop`.
 +pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    match ty.ty_adt_def() {
 +        Some(def) => def.has_dtor(cx.tcx),
 +        None => false,
 +    }
 +}
 +
 +// Returns whether the type has #[must_use] attribute
 +pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => cx.tcx.has_attr(adt.did(), sym::must_use),
 +        ty::Foreign(did) => cx.tcx.has_attr(*did, sym::must_use),
 +        ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => {
 +            // for the Array case we don't need to care for the len == 0 case
 +            // because we don't want to lint functions returning empty arrays
 +            is_must_use_ty(cx, *ty)
 +        },
 +        ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)),
 +        ty::Opaque(def_id, _) => {
 +            for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
 +                if let ty::PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() {
 +                    if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) {
 +                        return true;
 +                    }
 +                }
 +            }
 +            false
 +        },
 +        ty::Dynamic(binder, _) => {
 +            for predicate in binder.iter() {
 +                if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
 +                    if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) {
 +                        return true;
 +                    }
 +                }
 +            }
 +            false
 +        },
 +        _ => false,
 +    }
 +}
 +
 +// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
 +// this function can be removed once the `normalize` method does not panic when normalization does
 +// not succeed
 +/// Checks if `Ty` is normalizable. This function is useful
 +/// to avoid crashes on `layout_of`.
 +pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
 +    is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default())
 +}
 +
 +fn is_normalizable_helper<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    param_env: ty::ParamEnv<'tcx>,
 +    ty: Ty<'tcx>,
 +    cache: &mut FxHashMap<Ty<'tcx>, bool>,
 +) -> bool {
 +    if let Some(&cached_result) = cache.get(&ty) {
 +        return cached_result;
 +    }
 +    // prevent recursive loops, false-negative is better than endless loop leading to stack overflow
 +    cache.insert(ty, false);
 +    let result = cx.tcx.infer_ctxt().enter(|infcx| {
 +        let cause = rustc_middle::traits::ObligationCause::dummy();
 +        if infcx.at(&cause, param_env).normalize(ty).is_ok() {
 +            match ty.kind() {
 +                ty::Adt(def, substs) => def.variants().iter().all(|variant| {
 +                    variant
 +                        .fields
 +                        .iter()
 +                        .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
 +                }),
 +                _ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
 +                    GenericArgKind::Type(inner_ty) if inner_ty != ty => {
 +                        is_normalizable_helper(cx, param_env, inner_ty, cache)
 +                    },
 +                    _ => true, // if inner_ty == ty, we've already checked it
 +                }),
 +            }
 +        } else {
 +            false
 +        }
 +    });
 +    cache.insert(ty, result);
 +    result
 +}
 +
 +/// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any
 +/// integer or floating-point number type). For checking aggregation of primitive types (e.g.
 +/// tuples and slices of primitive type) see `is_recursively_primitive_type`
 +pub fn is_non_aggregate_primitive_type(ty: Ty<'_>) -> bool {
 +    matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_))
 +}
 +
 +/// Returns `true` if the given type is a primitive (a `bool` or `char`, any integer or
 +/// floating-point number type, a `str`, or an array, slice, or tuple of those types).
 +pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
 +    match *ty.kind() {
 +        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
 +        ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
 +        ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
 +        ty::Tuple(inner_types) => inner_types.iter().all(is_recursively_primitive_type),
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the type is a reference equals to a diagnostic item
 +pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
 +    match ty.kind() {
 +        ty::Ref(_, ref_ty, _) => match ref_ty.kind() {
 +            ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()),
 +            _ => false,
 +        },
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the type is equal to a diagnostic item. To check if a type implements a
 +/// trait marked with a diagnostic item use [`implements_trait`].
 +///
 +/// For a further exploitation what diagnostic items are see [diagnostic items] in
 +/// rustc-dev-guide.
 +///
 +/// ---
 +///
 +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
 +///
 +/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
 +pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()),
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the type is equal to a lang item.
 +///
 +/// Returns `false` if the `LangItem` is not defined.
 +pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => cx
 +            .tcx
 +            .lang_items()
 +            .require(lang_item)
 +            .map_or(false, |li| li == adt.did()),
 +        _ => false,
 +    }
 +}
 +
 +/// Return `true` if the passed `typ` is `isize` or `usize`.
 +pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
 +    matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
 +}
 +
 +/// Checks if type is struct, enum or union type with the given def path.
 +///
 +/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
 +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
 +pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => match_def_path(cx, adt.did(), path),
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the drop order for a type matters. Some std types implement drop solely to
 +/// deallocate memory. For these types, and composites containing them, changing the drop order
 +/// won't result in any observable side effects.
 +pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
 +        if !seen.insert(ty) {
 +            return false;
 +        }
 +        if !ty.has_significant_drop(cx.tcx, cx.param_env) {
 +            false
 +        }
 +        // Check for std types which implement drop, but only for memory allocation.
 +        else if is_type_lang_item(cx, ty, LangItem::OwnedBox)
 +            || matches!(
 +                get_type_diagnostic_name(cx, ty),
 +                Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type)
 +            )
 +            || match_type(cx, ty, &paths::WEAK_RC)
 +            || match_type(cx, ty, &paths::WEAK_ARC)
 +        {
 +            // Check all of the generic arguments.
 +            if let ty::Adt(_, subs) = ty.kind() {
 +                subs.types().any(|ty| needs_ordered_drop_inner(cx, ty, seen))
 +            } else {
 +                true
 +            }
 +        } else if !cx
 +            .tcx
 +            .lang_items()
 +            .drop_trait()
 +            .map_or(false, |id| implements_trait(cx, ty, id, &[]))
 +        {
 +            // This type doesn't implement drop, so no side effects here.
 +            // Check if any component type has any.
 +            match ty.kind() {
 +                ty::Tuple(fields) => fields.iter().any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
 +                ty::Array(ty, _) => needs_ordered_drop_inner(cx, *ty, seen),
 +                ty::Adt(adt, subs) => adt
 +                    .all_fields()
 +                    .map(|f| f.ty(cx.tcx, subs))
 +                    .any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
 +                _ => true,
 +            }
 +        } else {
 +            true
 +        }
 +    }
 +
 +    needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
 +}
 +
 +/// Peels off all references on the type. Returns the underlying type and the number of references
 +/// removed.
 +pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
 +    fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
 +        if let ty::Ref(_, ty, _) = ty.kind() {
 +            peel(*ty, count + 1)
 +        } else {
 +            (ty, count)
 +        }
 +    }
 +    peel(ty, 0)
 +}
 +
 +/// Peels off all references on the type.Returns the underlying type, the number of references
 +/// removed, and whether the pointer is ultimately mutable or not.
 +pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
 +    fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
 +        match ty.kind() {
 +            ty::Ref(_, ty, Mutability::Mut) => f(*ty, count + 1, mutability),
 +            ty::Ref(_, ty, Mutability::Not) => f(*ty, count + 1, Mutability::Not),
 +            _ => (ty, count, mutability),
 +        }
 +    }
 +    f(ty, 0, Mutability::Mut)
 +}
 +
 +/// Returns `true` if the given type is an `unsafe` function.
 +pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    match ty.kind() {
 +        ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
 +        _ => false,
 +    }
 +}
 +
 +/// Returns the base type for HIR references and pointers.
 +pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
 +    match ty.kind {
 +        TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty),
 +        _ => ty,
 +    }
 +}
 +
 +/// Returns the base type for references and raw pointers, and count reference
 +/// depth.
 +pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
 +    fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
 +        match ty.kind() {
 +            ty::Ref(_, ty, _) => inner(*ty, depth + 1),
 +            _ => (ty, depth),
 +        }
 +    }
 +    inner(ty, 0)
 +}
 +
 +/// Returns `true` if types `a` and `b` are same types having same `Const` generic args,
 +/// otherwise returns `false`
 +pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
 +    match (&a.kind(), &b.kind()) {
 +        (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => {
 +            if did_a != did_b {
 +                return false;
 +            }
 +
 +            substs_a
 +                .iter()
 +                .zip(substs_b.iter())
 +                .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) {
 +                    (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b,
 +                    (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => {
 +                        same_type_and_consts(type_a, type_b)
 +                    },
 +                    _ => true,
 +                })
 +        },
 +        _ => a == b,
 +    }
 +}
 +
 +/// Checks if a given type looks safe to be uninitialized.
 +pub fn is_uninit_value_valid_for_ty(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
 +    match *ty.kind() {
 +        ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component),
 +        ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)),
 +        ty::Adt(adt, _) => cx.tcx.lang_items().maybe_uninit() == Some(adt.did()),
 +        _ => false,
 +    }
 +}
 +
 +/// Gets an iterator over all predicates which apply to the given item.
 +pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(Predicate<'_>, Span)> {
 +    let mut next_id = Some(id);
 +    iter::from_fn(move || {
 +        next_id.take().map(|id| {
 +            let preds = tcx.predicates_of(id);
 +            next_id = preds.parent;
 +            preds.predicates.iter()
 +        })
 +    })
 +    .flatten()
 +}
 +
 +/// A signature for a function like type.
 +#[derive(Clone, Copy)]
 +pub enum ExprFnSig<'tcx> {
-             Self::Sig(sig) => {
++    Sig(Binder<'tcx, FnSig<'tcx>>, Option<DefId>),
 +    Closure(Option<&'tcx FnDecl<'tcx>>, Binder<'tcx, FnSig<'tcx>>),
 +    Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>),
 +}
 +impl<'tcx> ExprFnSig<'tcx> {
 +    /// Gets the argument type at the given offset. This will return `None` when the index is out of
 +    /// bounds only for variadic functions, otherwise this will panic.
 +    pub fn input(self, i: usize) -> Option<Binder<'tcx, Ty<'tcx>>> {
 +        match self {
-             Self::Sig(sig) => {
++            Self::Sig(sig, _) => {
 +                if sig.c_variadic() {
 +                    sig.inputs().map_bound(|inputs| inputs.get(i).copied()).transpose()
 +                } else {
 +                    Some(sig.input(i))
 +                }
 +            },
 +            Self::Closure(_, sig) => Some(sig.input(0).map_bound(|ty| ty.tuple_fields()[i])),
 +            Self::Trait(inputs, _) => Some(inputs.map_bound(|ty| ty.tuple_fields()[i])),
 +        }
 +    }
 +
 +    /// Gets the argument type at the given offset. For closures this will also get the type as
 +    /// written. This will return `None` when the index is out of bounds only for variadic
 +    /// functions, otherwise this will panic.
 +    pub fn input_with_hir(self, i: usize) -> Option<(Option<&'tcx hir::Ty<'tcx>>, Binder<'tcx, Ty<'tcx>>)> {
 +        match self {
-             Self::Sig(sig) | Self::Closure(_, sig) => Some(sig.output()),
++            Self::Sig(sig, _) => {
 +                if sig.c_variadic() {
 +                    sig.inputs()
 +                        .map_bound(|inputs| inputs.get(i).copied())
 +                        .transpose()
 +                        .map(|arg| (None, arg))
 +                } else {
 +                    Some((None, sig.input(i)))
 +                }
 +            },
 +            Self::Closure(decl, sig) => Some((
 +                decl.and_then(|decl| decl.inputs.get(i)),
 +                sig.input(0).map_bound(|ty| ty.tuple_fields()[i]),
 +            )),
 +            Self::Trait(inputs, _) => Some((None, inputs.map_bound(|ty| ty.tuple_fields()[i]))),
 +        }
 +    }
 +
 +    /// Gets the result type, if one could be found. Note that the result type of a trait may not be
 +    /// specified.
 +    pub fn output(self) -> Option<Binder<'tcx, Ty<'tcx>>> {
 +        match self {
-         Some(ExprFnSig::Sig(cx.tcx.fn_sig(id)))
++            Self::Sig(sig, _) | Self::Closure(_, sig) => Some(sig.output()),
 +            Self::Trait(_, output) => output,
 +        }
 +    }
++
++    pub fn predicates_id(&self) -> Option<DefId> {
++        if let ExprFnSig::Sig(_, id) = *self { id } else { None }
++    }
 +}
 +
 +/// If the expression is function like, get the signature for it.
 +pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
 +    if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
-         ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs))),
-         ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)),
++        Some(ExprFnSig::Sig(cx.tcx.fn_sig(id), Some(id)))
 +    } else {
 +        ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs())
 +    }
 +}
 +
 +fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
++    if ty.is_box() {
++        return ty_sig(cx, ty.boxed_ty());
++    }
 +    match *ty.kind() {
 +        ty::Closure(id, subs) => {
 +            let decl = id
 +                .as_local()
 +                .and_then(|id| cx.tcx.hir().fn_decl_by_hir_id(cx.tcx.hir().local_def_id_to_hir_id(id)));
 +            Some(ExprFnSig::Closure(decl, subs.as_closure().sig()))
 +        },
++        ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))),
++        ty::Opaque(id, _) => ty_sig(cx, cx.tcx.type_of(id)),
++        ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)),
 +        ty::Dynamic(bounds, _) => {
 +            let lang_items = cx.tcx.lang_items();
 +            match bounds.principal() {
 +                Some(bound)
 +                    if Some(bound.def_id()) == lang_items.fn_trait()
 +                        || Some(bound.def_id()) == lang_items.fn_once_trait()
 +                        || Some(bound.def_id()) == lang_items.fn_mut_trait() =>
 +                {
 +                    let output = bounds
 +                        .projection_bounds()
 +                        .find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
 +                        .map(|p| p.map_bound(|p| p.term.ty().unwrap()));
 +                    Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output))
 +                },
 +                _ => None,
 +            }
 +        },
 +        ty::Projection(proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) {
 +            Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty),
 +            _ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty)),
 +        },
 +        ty::Param(_) => sig_from_bounds(cx, ty),
 +        _ => None,
 +    }
 +}
 +
 +fn sig_from_bounds<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
 +    let mut inputs = None;
 +    let mut output = None;
 +    let lang_items = cx.tcx.lang_items();
 +
 +    for (pred, _) in all_predicates_of(cx.tcx, cx.typeck_results().hir_owner.to_def_id()) {
 +        match pred.kind().skip_binder() {
 +            PredicateKind::Trait(p)
 +                if (lang_items.fn_trait() == Some(p.def_id())
 +                    || lang_items.fn_mut_trait() == Some(p.def_id())
 +                    || lang_items.fn_once_trait() == Some(p.def_id()))
 +                    && p.self_ty() == ty =>
 +            {
 +                if inputs.is_some() {
 +                    // Multiple different fn trait impls. Is this even allowed?
 +                    return None;
 +                }
 +                inputs = Some(pred.kind().rebind(p.trait_ref.substs.type_at(1)));
 +            },
 +            PredicateKind::Projection(p)
 +                if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
 +                    && p.projection_ty.self_ty() == ty =>
 +            {
 +                if output.is_some() {
 +                    // Multiple different fn trait impls. Is this even allowed?
 +                    return None;
 +                }
 +                output = Some(pred.kind().rebind(p.term.ty().unwrap()));
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    inputs.map(|ty| ExprFnSig::Trait(ty, output))
 +}
 +
 +fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> Option<ExprFnSig<'tcx>> {
 +    let mut inputs = None;
 +    let mut output = None;
 +    let lang_items = cx.tcx.lang_items();
 +
 +    for pred in cx
 +        .tcx
 +        .bound_explicit_item_bounds(ty.item_def_id)
 +        .transpose_iter()
 +        .map(|x| x.map_bound(|(p, _)| p))
 +    {
 +        match pred.0.kind().skip_binder() {
 +            PredicateKind::Trait(p)
 +                if (lang_items.fn_trait() == Some(p.def_id())
 +                    || lang_items.fn_mut_trait() == Some(p.def_id())
 +                    || lang_items.fn_once_trait() == Some(p.def_id())) =>
 +            {
 +                if inputs.is_some() {
 +                    // Multiple different fn trait impls. Is this even allowed?
 +                    return None;
 +                }
 +                inputs = Some(
 +                    pred.map_bound(|pred| pred.kind().rebind(p.trait_ref.substs.type_at(1)))
 +                        .subst(cx.tcx, ty.substs),
 +                );
 +            },
 +            PredicateKind::Projection(p) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => {
 +                if output.is_some() {
 +                    // Multiple different fn trait impls. Is this even allowed?
 +                    return None;
 +                }
 +                output = Some(
 +                    pred.map_bound(|pred| pred.kind().rebind(p.term.ty().unwrap()))
 +                        .subst(cx.tcx, ty.substs),
 +                );
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    inputs.map(|ty| ExprFnSig::Trait(ty, output))
 +}
 +
 +#[derive(Clone, Copy)]
 +pub enum EnumValue {
 +    Unsigned(u128),
 +    Signed(i128),
 +}
 +impl core::ops::Add<u32> for EnumValue {
 +    type Output = Self;
 +    fn add(self, n: u32) -> Self::Output {
 +        match self {
 +            Self::Unsigned(x) => Self::Unsigned(x + u128::from(n)),
 +            Self::Signed(x) => Self::Signed(x + i128::from(n)),
 +        }
 +    }
 +}
 +
 +/// Attempts to read the given constant as though it were an an enum value.
 +#[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
 +pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option<EnumValue> {
 +    if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) {
 +        match tcx.type_of(id).kind() {
 +            ty::Int(_) => Some(EnumValue::Signed(match value.size().bytes() {
 +                1 => i128::from(value.assert_bits(Size::from_bytes(1)) as u8 as i8),
 +                2 => i128::from(value.assert_bits(Size::from_bytes(2)) as u16 as i16),
 +                4 => i128::from(value.assert_bits(Size::from_bytes(4)) as u32 as i32),
 +                8 => i128::from(value.assert_bits(Size::from_bytes(8)) as u64 as i64),
 +                16 => value.assert_bits(Size::from_bytes(16)) as i128,
 +                _ => return None,
 +            })),
 +            ty::Uint(_) => Some(EnumValue::Unsigned(match value.size().bytes() {
 +                1 => value.assert_bits(Size::from_bytes(1)),
 +                2 => value.assert_bits(Size::from_bytes(2)),
 +                4 => value.assert_bits(Size::from_bytes(4)),
 +                8 => value.assert_bits(Size::from_bytes(8)),
 +                16 => value.assert_bits(Size::from_bytes(16)),
 +                _ => return None,
 +            })),
 +            _ => None,
 +        }
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Gets the value of the given variant.
 +pub fn get_discriminant_value(tcx: TyCtxt<'_>, adt: AdtDef<'_>, i: VariantIdx) -> EnumValue {
 +    let variant = &adt.variant(i);
 +    match variant.discr {
 +        VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap(),
 +        VariantDiscr::Relative(x) => match adt.variant((i.as_usize() - x as usize).into()).discr {
 +            VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap() + x,
 +            VariantDiscr::Relative(_) => EnumValue::Unsigned(x.into()),
 +        },
 +    }
 +}
 +
 +/// Check if the given type is either `core::ffi::c_void`, `std::os::raw::c_void`, or one of the
 +/// platform specific `libc::<platform>::c_void` types in libc.
 +pub fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
 +    if let ty::Adt(adt, _) = ty.kind()
 +        && let &[krate, .., name] = &*cx.get_def_path(adt.did())
 +        && let sym::libc | sym::core | sym::std = krate
 +        && name.as_str() == "c_void"
 +    {
 +        true
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn for_each_top_level_late_bound_region<B>(
 +    ty: Ty<'_>,
 +    f: impl FnMut(BoundRegion) -> ControlFlow<B>,
 +) -> ControlFlow<B> {
 +    struct V<F> {
 +        index: u32,
 +        f: F,
 +    }
 +    impl<'tcx, B, F: FnMut(BoundRegion) -> ControlFlow<B>> TypeVisitor<'tcx> for V<F> {
 +        type BreakTy = B;
 +        fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
 +            if let RegionKind::ReLateBound(idx, bound) = r.kind() && idx.as_u32() == self.index {
 +                (self.f)(bound)
 +            } else {
 +                ControlFlow::Continue(())
 +            }
 +        }
 +        fn visit_binder<T: TypeVisitable<'tcx>>(&mut self, t: &Binder<'tcx, T>) -> ControlFlow<Self::BreakTy> {
 +            self.index += 1;
 +            let res = t.super_visit_with(self);
 +            self.index -= 1;
 +            res
 +        }
 +    }
 +    ty.visit_with(&mut V { index: 0, f })
 +}
 +
 +/// Gets the struct or enum variant from the given `Res`
 +pub fn variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<&'tcx VariantDef> {
 +    match res {
 +        Res::Def(DefKind::Struct, id) => Some(cx.tcx.adt_def(id).non_enum_variant()),
 +        Res::Def(DefKind::Variant, id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).variant_with_id(id)),
 +        Res::Def(DefKind::Ctor(CtorOf::Struct, _), id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).non_enum_variant()),
 +        Res::Def(DefKind::Ctor(CtorOf::Variant, _), id) => {
 +            let var_id = cx.tcx.parent(id);
 +            Some(cx.tcx.adt_def(cx.tcx.parent(var_id)).variant_with_id(var_id))
 +        },
 +        Res::SelfCtor(id) => Some(cx.tcx.type_of(id).ty_adt_def().unwrap().non_enum_variant()),
 +        _ => None,
 +    }
 +}
++
++/// Checks if the type is a type parameter implementing `FnOnce`, but not `FnMut`.
++pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tcx [Predicate<'_>]) -> bool {
++    let ty::Param(ty) = *ty.kind() else {
++        return false;
++    };
++    let lang = tcx.lang_items();
++    let (Some(fn_once_id), Some(fn_mut_id), Some(fn_id))
++        = (lang.fn_once_trait(), lang.fn_mut_trait(), lang.fn_trait())
++    else {
++        return false;
++    };
++    predicates
++        .iter()
++        .try_fold(false, |found, p| {
++            if let PredicateKind::Trait(p) = p.kind().skip_binder()
++            && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
++            && ty.index == self_ty.index
++        {
++            // This should use `super_traits_of`, but that's a private function.
++            if p.trait_ref.def_id == fn_once_id {
++                return Some(true);
++            } else if p.trait_ref.def_id == fn_mut_id || p.trait_ref.def_id == fn_id {
++                return None;
++            }
++        }
++            Some(found)
++        })
++        .unwrap_or(false)
++}
index 68cfa8c1aa8ec22e5a96a53e473759943cd018fa,0000000000000000000000000000000000000000..bae8ad9f5659067030d39315aa3276d68a4910a5
mode 100644,000000..100644
--- /dev/null
@@@ -1,619 -1,0 +1,733 @@@
-     Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, QPath, Stmt, UnOp,
 +use crate::ty::needs_ordered_drop;
 +use crate::{get_enclosing_block, path_to_local_id};
 +use core::ops::ControlFlow;
 +use rustc_hir as hir;
 +use rustc_hir::def::{CtorKind, DefKind, Res};
 +use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
 +use rustc_hir::{
++    Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath, Stmt, UnOp,
 +    UnsafeSource, Unsafety,
 +};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::ty::adjustment::Adjust;
 +use rustc_middle::ty::{self, Ty, TypeckResults};
++use rustc_span::Span;
++
++mod internal {
++    /// Trait for visitor functions to control whether or not to descend to child nodes. Implemented
++    /// for only two types. `()` always descends. `Descend` allows controlled descent.
++    pub trait Continue {
++        fn descend(&self) -> bool;
++    }
++}
++use internal::Continue;
++
++impl Continue for () {
++    fn descend(&self) -> bool {
++        true
++    }
++}
++
++/// Allows for controlled descent when using visitor functions. Use `()` instead when always
++/// descending into child nodes.
++#[derive(Clone, Copy)]
++pub enum Descend {
++    Yes,
++    No,
++}
++impl From<bool> for Descend {
++    fn from(from: bool) -> Self {
++        if from { Self::Yes } else { Self::No }
++    }
++}
++impl Continue for Descend {
++    fn descend(&self) -> bool {
++        matches!(self, Self::Yes)
++    }
++}
++
++/// Calls the given function once for each expression contained. This does not enter any bodies or
++/// nested items.
++pub fn for_each_expr<'tcx, B, C: Continue>(
++    node: impl Visitable<'tcx>,
++    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
++) -> Option<B> {
++    struct V<B, F> {
++        f: F,
++        res: Option<B>,
++    }
++    impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<B, F> {
++        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
++            if self.res.is_some() {
++                return;
++            }
++            match (self.f)(e) {
++                ControlFlow::Continue(c) if c.descend() => walk_expr(self, e),
++                ControlFlow::Break(b) => self.res = Some(b),
++                ControlFlow::Continue(_) => (),
++            }
++        }
++
++        // Avoid unnecessary `walk_*` calls.
++        fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {}
++        fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {}
++        fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {}
++        // Avoid monomorphising all `visit_*` functions.
++        fn visit_nested_item(&mut self, _: ItemId) {}
++    }
++    let mut v = V { f, res: None };
++    node.visit(&mut v);
++    v.res
++}
 +
 +/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
 +/// bodies (i.e. closures) are visited.
 +/// If the callback returns `true`, the expr just provided to the callback is walked.
 +#[must_use]
 +pub fn expr_visitor<'tcx>(cx: &LateContext<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
 +    struct V<'tcx, F> {
 +        hir: Map<'tcx>,
 +        f: F,
 +    }
 +    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<'tcx, F> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.hir
 +        }
 +
 +        fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 +            if (self.f)(expr) {
 +                walk_expr(self, expr);
 +            }
 +        }
 +    }
 +    V { hir: cx.tcx.hir(), f }
 +}
 +
 +/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
 +/// bodies (i.e. closures) are not visited.
 +/// If the callback returns `true`, the expr just provided to the callback is walked.
 +#[must_use]
 +pub fn expr_visitor_no_bodies<'tcx>(f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
 +    struct V<F>(F);
 +    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<F> {
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if (self.0)(e) {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +    V(f)
 +}
 +
 +/// returns `true` if expr contains match expr desugared from try
 +fn contains_try(expr: &hir::Expr<'_>) -> bool {
 +    let mut found = false;
 +    expr_visitor_no_bodies(|e| {
 +        if !found {
 +            found = matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar));
 +        }
 +        !found
 +    })
 +    .visit_expr(expr);
 +    found
 +}
 +
 +pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
 +where
 +    F: FnMut(&'hir hir::Expr<'hir>) -> bool,
 +{
 +    struct RetFinder<F> {
 +        in_stmt: bool,
 +        failed: bool,
 +        cb: F,
 +    }
 +
 +    struct WithStmtGuarg<'a, F> {
 +        val: &'a mut RetFinder<F>,
 +        prev_in_stmt: bool,
 +    }
 +
 +    impl<F> RetFinder<F> {
 +        fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
 +            let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
 +            WithStmtGuarg {
 +                val: self,
 +                prev_in_stmt,
 +            }
 +        }
 +    }
 +
 +    impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
 +        type Target = RetFinder<F>;
 +
 +        fn deref(&self) -> &Self::Target {
 +            self.val
 +        }
 +    }
 +
 +    impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
 +        fn deref_mut(&mut self) -> &mut Self::Target {
 +            self.val
 +        }
 +    }
 +
 +    impl<F> Drop for WithStmtGuarg<'_, F> {
 +        fn drop(&mut self) {
 +            self.val.in_stmt = self.prev_in_stmt;
 +        }
 +    }
 +
 +    impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder<F> {
 +        fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) {
 +            intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt);
 +        }
 +
 +        fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) {
 +            if self.failed {
 +                return;
 +            }
 +            if self.in_stmt {
 +                match expr.kind {
 +                    hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
 +                    _ => intravisit::walk_expr(self, expr),
 +                }
 +            } else {
 +                match expr.kind {
 +                    hir::ExprKind::If(cond, then, else_opt) => {
 +                        self.inside_stmt(true).visit_expr(cond);
 +                        self.visit_expr(then);
 +                        if let Some(el) = else_opt {
 +                            self.visit_expr(el);
 +                        }
 +                    },
 +                    hir::ExprKind::Match(cond, arms, _) => {
 +                        self.inside_stmt(true).visit_expr(cond);
 +                        for arm in arms {
 +                            self.visit_expr(arm.body);
 +                        }
 +                    },
 +                    hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr),
 +                    hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
 +                    _ => self.failed |= !(self.cb)(expr),
 +                }
 +            }
 +        }
 +    }
 +
 +    !contains_try(expr) && {
 +        let mut ret_finder = RetFinder {
 +            in_stmt: false,
 +            failed: false,
 +            cb: callback,
 +        };
 +        ret_finder.visit_expr(expr);
 +        !ret_finder.failed
 +    }
 +}
 +
 +/// A type which can be visited.
 +pub trait Visitable<'tcx> {
 +    /// Calls the corresponding `visit_*` function on the visitor.
 +    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V);
 +}
 +macro_rules! visitable_ref {
 +    ($t:ident, $f:ident) => {
 +        impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
 +            fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
 +                visitor.$f(self);
 +            }
 +        }
 +    };
 +}
 +visitable_ref!(Arm, visit_arm);
 +visitable_ref!(Block, visit_block);
 +visitable_ref!(Body, visit_body);
 +visitable_ref!(Expr, visit_expr);
 +visitable_ref!(Stmt, visit_stmt);
 +
 +// impl<'tcx, I: IntoIterator> Visitable<'tcx> for I
 +// where
 +//     I::Item: Visitable<'tcx>,
 +// {
 +//     fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
 +//         for x in self {
 +//             x.visit(visitor);
 +//         }
 +//     }
 +// }
 +
 +/// Checks if the given resolved path is used in the given body.
 +pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
 +    let mut found = false;
 +    expr_visitor(cx, |e| {
 +        if found {
 +            return false;
 +        }
 +
 +        if let ExprKind::Path(p) = &e.kind {
 +            if cx.qpath_res(p, e.hir_id) == res {
 +                found = true;
 +            }
 +        }
 +        !found
 +    })
 +    .visit_expr(&cx.tcx.hir().body(body).value);
 +    found
 +}
 +
 +/// Checks if the given local is used.
 +pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
 +    let mut is_used = false;
 +    let mut visitor = expr_visitor(cx, |expr| {
 +        if !is_used {
 +            is_used = path_to_local_id(expr, id);
 +        }
 +        !is_used
 +    });
 +    visitable.visit(&mut visitor);
 +    drop(visitor);
 +    is_used
 +}
 +
 +/// Checks if the given expression is a constant.
 +pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
 +    struct V<'a, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        is_const: bool,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if !self.is_const {
 +                return;
 +            }
 +            match e.kind {
 +                ExprKind::ConstBlock(_) => return,
 +                ExprKind::Call(
 +                    &Expr {
 +                        kind: ExprKind::Path(ref p),
 +                        hir_id,
 +                        ..
 +                    },
 +                    _,
 +                ) if self
 +                    .cx
 +                    .qpath_res(p, hir_id)
 +                    .opt_def_id()
 +                    .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
 +                ExprKind::MethodCall(..)
 +                    if self
 +                        .cx
 +                        .typeck_results()
 +                        .type_dependent_def_id(e.hir_id)
 +                        .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
 +                ExprKind::Binary(_, lhs, rhs)
 +                    if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
 +                        && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
 +                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (),
 +                ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (),
 +                ExprKind::Index(base, _)
 +                    if matches!(
 +                        self.cx.typeck_results().expr_ty(base).peel_refs().kind(),
 +                        ty::Slice(_) | ty::Array(..)
 +                    ) => {},
 +                ExprKind::Path(ref p)
 +                    if matches!(
 +                        self.cx.qpath_res(p, e.hir_id),
 +                        Res::Def(
 +                            DefKind::Const
 +                                | DefKind::AssocConst
 +                                | DefKind::AnonConst
 +                                | DefKind::ConstParam
 +                                | DefKind::Ctor(..)
 +                                | DefKind::Fn
 +                                | DefKind::AssocFn,
 +                            _
 +                        ) | Res::SelfCtor(_)
 +                    ) => {},
 +
 +                ExprKind::AddrOf(..)
 +                | ExprKind::Array(_)
 +                | ExprKind::Block(..)
 +                | ExprKind::Cast(..)
 +                | ExprKind::DropTemps(_)
 +                | ExprKind::Field(..)
 +                | ExprKind::If(..)
 +                | ExprKind::Let(..)
 +                | ExprKind::Lit(_)
 +                | ExprKind::Match(..)
 +                | ExprKind::Repeat(..)
 +                | ExprKind::Struct(..)
 +                | ExprKind::Tup(_)
 +                | ExprKind::Type(..) => (),
 +
 +                _ => {
 +                    self.is_const = false;
 +                    return;
 +                },
 +            }
 +            walk_expr(self, e);
 +        }
 +    }
 +
 +    let mut v = V { cx, is_const: true };
 +    v.visit_expr(e);
 +    v.is_const
 +}
 +
 +/// Checks if the given expression performs an unsafe operation outside of an unsafe block.
 +pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
 +    struct V<'a, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        is_unsafe: bool,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.is_unsafe {
 +                return;
 +            }
 +            match e.kind {
 +                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => {
 +                    self.is_unsafe = true;
 +                },
 +                ExprKind::MethodCall(..)
 +                    if self
 +                        .cx
 +                        .typeck_results()
 +                        .type_dependent_def_id(e.hir_id)
 +                        .map_or(false, |id| self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe) =>
 +                {
 +                    self.is_unsafe = true;
 +                },
 +                ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
 +                    ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
 +                    ty::FnPtr(sig) if sig.unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
 +                    _ => walk_expr(self, e),
 +                },
 +                ExprKind::Path(ref p)
 +                    if self
 +                        .cx
 +                        .qpath_res(p, e.hir_id)
 +                        .opt_def_id()
 +                        .map_or(false, |id| self.cx.tcx.is_mutable_static(id)) =>
 +                {
 +                    self.is_unsafe = true;
 +                },
 +                _ => walk_expr(self, e),
 +            }
 +        }
 +        fn visit_block(&mut self, b: &'tcx Block<'_>) {
 +            if !matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
 +                walk_block(self, b);
 +            }
 +        }
 +        fn visit_nested_item(&mut self, id: ItemId) {
 +            if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind {
 +                self.is_unsafe = i.unsafety == Unsafety::Unsafe;
 +            }
 +        }
 +    }
 +    let mut v = V { cx, is_unsafe: false };
 +    v.visit_expr(e);
 +    v.is_unsafe
 +}
 +
 +/// Checks if the given expression contains an unsafe block
 +pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
 +    struct V<'cx, 'tcx> {
 +        cx: &'cx LateContext<'tcx>,
 +        found_unsafe: bool,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_block(&mut self, b: &'tcx Block<'_>) {
 +            if self.found_unsafe {
 +                return;
 +            }
 +            if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
 +                self.found_unsafe = true;
 +                return;
 +            }
 +            walk_block(self, b);
 +        }
 +    }
 +    let mut v = V {
 +        cx,
 +        found_unsafe: false,
 +    };
 +    v.visit_expr(e);
 +    v.found_unsafe
 +}
 +
 +/// Runs the given function for each sub-expression producing the final value consumed by the parent
 +/// of the give expression.
 +///
 +/// e.g. for the following expression
 +/// ```rust,ignore
 +/// if foo {
 +///     f(0)
 +/// } else {
 +///     1 + 1
 +/// }
 +/// ```
 +/// this will pass both `f(0)` and `1+1` to the given function.
 +pub fn for_each_value_source<'tcx, B>(
 +    e: &'tcx Expr<'tcx>,
 +    f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
 +) -> ControlFlow<B> {
 +    match e.kind {
 +        ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
 +        ExprKind::Match(_, arms, _) => {
 +            for arm in arms {
 +                for_each_value_source(arm.body, f)?;
 +            }
 +            ControlFlow::Continue(())
 +        },
 +        ExprKind::If(_, if_expr, Some(else_expr)) => {
 +            for_each_value_source(if_expr, f)?;
 +            for_each_value_source(else_expr, f)
 +        },
 +        ExprKind::DropTemps(e) => for_each_value_source(e, f),
 +        _ => f(e),
 +    }
 +}
 +
 +/// Runs the given function for each path expression referencing the given local which occur after
 +/// the given expression.
 +pub fn for_each_local_use_after_expr<'tcx, B>(
 +    cx: &LateContext<'tcx>,
 +    local_id: HirId,
 +    expr_id: HirId,
 +    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
 +) -> ControlFlow<B> {
 +    struct V<'cx, 'tcx, F, B> {
 +        cx: &'cx LateContext<'tcx>,
 +        local_id: HirId,
 +        expr_id: HirId,
 +        found: bool,
 +        res: ControlFlow<B>,
 +        f: F,
 +    }
 +    impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
 +            if !self.found {
 +                if e.hir_id == self.expr_id {
 +                    self.found = true;
 +                } else {
 +                    walk_expr(self, e);
 +                }
 +                return;
 +            }
 +            if self.res.is_break() {
 +                return;
 +            }
 +            if path_to_local_id(e, self.local_id) {
 +                self.res = (self.f)(e);
 +            } else {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +
 +    if let Some(b) = get_enclosing_block(cx, local_id) {
 +        let mut v = V {
 +            cx,
 +            local_id,
 +            expr_id,
 +            found: false,
 +            res: ControlFlow::Continue(()),
 +            f,
 +        };
 +        v.visit_block(b);
 +        v.res
 +    } else {
 +        ControlFlow::Continue(())
 +    }
 +}
 +
 +// Calls the given function for every unconsumed temporary created by the expression. Note the
 +// function is only guaranteed to be called for types which need to be dropped, but it may be called
 +// for other types.
 +pub fn for_each_unconsumed_temporary<'tcx, B>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'tcx>,
 +    mut f: impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
 +) -> ControlFlow<B> {
 +    // Todo: Handle partially consumed values.
 +    fn helper<'tcx, B>(
 +        typeck: &'tcx TypeckResults<'tcx>,
 +        consume: bool,
 +        e: &'tcx Expr<'tcx>,
 +        f: &mut impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
 +    ) -> ControlFlow<B> {
 +        if !consume
 +            || matches!(
 +                typeck.expr_adjustments(e),
 +                [adjust, ..] if matches!(adjust.kind, Adjust::Borrow(_) | Adjust::Deref(_))
 +            )
 +        {
 +            match e.kind {
 +                ExprKind::Path(QPath::Resolved(None, p))
 +                    if matches!(p.res, Res::Def(DefKind::Ctor(_, CtorKind::Const), _)) =>
 +                {
 +                    f(typeck.expr_ty(e))?;
 +                },
 +                ExprKind::Path(_)
 +                | ExprKind::Unary(UnOp::Deref, _)
 +                | ExprKind::Index(..)
 +                | ExprKind::Field(..)
 +                | ExprKind::AddrOf(..) => (),
 +                _ => f(typeck.expr_ty(e))?,
 +            }
 +        }
 +        match e.kind {
 +            ExprKind::AddrOf(_, _, e)
 +            | ExprKind::Field(e, _)
 +            | ExprKind::Unary(UnOp::Deref, e)
 +            | ExprKind::Match(e, ..)
 +            | ExprKind::Let(&Let { init: e, .. }) => {
 +                helper(typeck, false, e, f)?;
 +            },
 +            ExprKind::Block(&Block { expr: Some(e), .. }, _)
 +            | ExprKind::Box(e)
 +            | ExprKind::Cast(e, _)
 +            | ExprKind::Unary(_, e) => {
 +                helper(typeck, true, e, f)?;
 +            },
 +            ExprKind::Call(callee, args) => {
 +                helper(typeck, true, callee, f)?;
 +                for arg in args {
 +                    helper(typeck, true, arg, f)?;
 +                }
 +            },
 +            ExprKind::MethodCall(_, args, _) | ExprKind::Tup(args) | ExprKind::Array(args) => {
 +                for arg in args {
 +                    helper(typeck, true, arg, f)?;
 +                }
 +            },
 +            ExprKind::Index(borrowed, consumed)
 +            | ExprKind::Assign(borrowed, consumed, _)
 +            | ExprKind::AssignOp(_, borrowed, consumed) => {
 +                helper(typeck, false, borrowed, f)?;
 +                helper(typeck, true, consumed, f)?;
 +            },
 +            ExprKind::Binary(_, lhs, rhs) => {
 +                helper(typeck, true, lhs, f)?;
 +                helper(typeck, true, rhs, f)?;
 +            },
 +            ExprKind::Struct(_, fields, default) => {
 +                for field in fields {
 +                    helper(typeck, true, field.expr, f)?;
 +                }
 +                if let Some(default) = default {
 +                    helper(typeck, false, default, f)?;
 +                }
 +            },
 +            ExprKind::If(cond, then, else_expr) => {
 +                helper(typeck, true, cond, f)?;
 +                helper(typeck, true, then, f)?;
 +                if let Some(else_expr) = else_expr {
 +                    helper(typeck, true, else_expr, f)?;
 +                }
 +            },
 +            ExprKind::Type(e, _) => {
 +                helper(typeck, consume, e, f)?;
 +            },
 +
 +            // Either drops temporaries, jumps out of the current expression, or has no sub expression.
 +            ExprKind::DropTemps(_)
 +            | ExprKind::Ret(_)
 +            | ExprKind::Break(..)
 +            | ExprKind::Yield(..)
 +            | ExprKind::Block(..)
 +            | ExprKind::Loop(..)
 +            | ExprKind::Repeat(..)
 +            | ExprKind::Lit(_)
 +            | ExprKind::ConstBlock(_)
 +            | ExprKind::Closure { .. }
 +            | ExprKind::Path(_)
 +            | ExprKind::Continue(_)
 +            | ExprKind::InlineAsm(_)
 +            | ExprKind::Err => (),
 +        }
 +        ControlFlow::Continue(())
 +    }
 +    helper(cx.typeck_results(), true, e, &mut f)
 +}
 +
 +pub fn any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
 +    for_each_unconsumed_temporary(cx, e, |ty| {
 +        if needs_ordered_drop(cx, ty) {
 +            ControlFlow::Break(())
 +        } else {
 +            ControlFlow::Continue(())
 +        }
 +    })
 +    .is_break()
 +}
++
++/// Runs the given function for each path expression referencing the given local which occur after
++/// the given expression.
++pub fn for_each_local_assignment<'tcx, B>(
++    cx: &LateContext<'tcx>,
++    local_id: HirId,
++    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
++) -> ControlFlow<B> {
++    struct V<'cx, 'tcx, F, B> {
++        cx: &'cx LateContext<'tcx>,
++        local_id: HirId,
++        res: ControlFlow<B>,
++        f: F,
++    }
++    impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> {
++        type NestedFilter = nested_filter::OnlyBodies;
++        fn nested_visit_map(&mut self) -> Self::Map {
++            self.cx.tcx.hir()
++        }
++
++        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
++            if let ExprKind::Assign(lhs, rhs, _) = e.kind
++                && self.res.is_continue()
++                && path_to_local_id(lhs, self.local_id)
++            {
++                self.res = (self.f)(rhs);
++                self.visit_expr(rhs);
++            } else {
++                walk_expr(self, e);
++            }
++        }
++    }
++
++    if let Some(b) = get_enclosing_block(cx, local_id) {
++        let mut v = V {
++            cx,
++            local_id,
++            res: ControlFlow::Continue(()),
++            f,
++        };
++        v.visit_block(b);
++        v.res
++    } else {
++        ControlFlow::Continue(())
++    }
++}
index 504d58b5197a105b4da50e5e3c43149dd29be060,0000000000000000000000000000000000000000..737c845c045153958cb8f542e24e3b9c4d21772d
mode 100644,000000..100644
--- /dev/null
@@@ -1,24 -1,0 +1,24 @@@
- description = "tool to monitor impact of changes in Clippys lints on a part of the ecosystem"
 +[package]
 +name = "lintcheck"
 +version = "0.0.1"
- clap = "3.1"
++description = "tool to monitor impact of changes in Clippy's lints on a part of the ecosystem"
 +readme = "README.md"
 +license = "MIT OR Apache-2.0"
 +repository = "https://github.com/rust-lang/rust-clippy"
 +categories = ["development-tools"]
 +edition = "2021"
 +publish = false
 +
 +[dependencies]
 +cargo_metadata = "0.14"
++clap = "3.2"
 +flate2 = "1.0"
 +rayon = "1.5.1"
 +serde = { version = "1.0", features = ["derive"] }
 +tar = "0.4"
 +toml = "0.5"
 +ureq = "2.2"
 +walkdir = "2.3"
 +
 +[features]
 +deny-warnings = []
index 8c169506e533d7b7892ff558924ca46c784529ca,0000000000000000000000000000000000000000..6f3d23382ce15d7036855a029f882035ecc3c6fe
mode 100644,000000..100644
--- /dev/null
@@@ -1,77 -1,0 +1,77 @@@
- print a warning if Clippys suggestions fail to apply (if the resulting code does not build).  
 +## `cargo lintcheck`
 +
 +Runs clippy on a fixed set of crates read from
 +`lintcheck/lintcheck_crates.toml` and saves logs of the lint warnings into the
 +repo.  We can then check the diff and spot new or disappearing warnings.
 +
 +From the repo root, run:
 +
 +```
 +cargo run --target-dir lintcheck/target --manifest-path lintcheck/Cargo.toml
 +```
 +
 +or
 +
 +```
 +cargo lintcheck
 +```
 +
 +By default the logs will be saved into
 +`lintcheck-logs/lintcheck_crates_logs.txt`.
 +
 +You can set a custom sources.toml by adding `--crates-toml custom.toml` or using
 +`LINTCHECK_TOML="custom.toml"` where `custom.toml` must be a relative path from
 +the repo root.
 +
 +The results will then be saved to `lintcheck-logs/custom_logs.toml`.
 +
 +### Configuring the Crate Sources
 +
 +The sources to check are saved in a `toml` file. There are three types of
 +sources.
 +
 +1. Crates-io Source
 +
 +   ```toml
 +   bitflags = {name = "bitflags", versions = ['1.2.1']}
 +   ```
 +   Requires a "name" and one or multiple "versions" to be checked.
 +
 +2. `git` Source
 +   ````toml
 +   puffin = {name = "puffin", git_url = "https://github.com/EmbarkStudios/puffin", git_hash = "02dd4a3"}
 +   ````
 +   Requires a name, the url to the repo and unique identifier of a commit,
 +   branch or tag which is checked out before linting.  There is no way to always
 +   check `HEAD` because that would lead to changing lint-results as the repo
 +   would get updated.  If `git_url` or `git_hash` is missing, an error will be
 +   thrown.
 +
 +3. Local Dependency
 +   ```toml
 +   clippy = {name = "clippy", path = "/home/user/clippy"}
 +   ```
 +   For when you want to add a repository that is not published yet.
 +
 +#### Command Line Options (optional)
 +
 +```toml
 +bitflags = {name = "bitflags", versions = ['1.2.1'], options = ['-Wclippy::pedantic', '-Wclippy::cargo']}
 +```
 +
 +It is possible to specify command line options for each crate. This makes it
 +possible to only check a crate for certain lint groups. If no options are
 +specified, the lint groups `clippy::all`, `clippy::pedantic`, and
 +`clippy::cargo` are checked. If an empty array is specified only `clippy::all`
 +is checked.
 +
 +**Note:** `-Wclippy::all` is always enabled by default, unless `-Aclippy::all`
 +is explicitly specified in the options.
 +
 +### Fix mode
 +You can run `./lintcheck/target/debug/lintcheck --fix` which will run Clippy with `--fix` and
++print a warning if Clippy's suggestions fail to apply (if the resulting code does not build).  
 +This lets us spot bad suggestions or false positives automatically in some cases.  
 +
 +Please note that the target dir should be cleaned afterwards since clippy will modify
 +the downloaded sources which can lead to unexpected results when running lintcheck again afterwards.
index dff9d27db0a6c45f54f7fcab3e090fc644e24c31,0000000000000000000000000000000000000000..9ee25280f046fdcbcf437a674109d4e38bf4614c
mode 100644,000000..100644
--- /dev/null
@@@ -1,814 -1,0 +1,814 @@@
-         eprintln!("lintcheck needs to be run from clippys repo root!\nUse `cargo lintcheck` alternatively.");
 +// 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;
 +
 +use config::LintcheckConfig;
 +
 +use std::collections::HashMap;
 +use std::env;
 +use std::fmt::Write as _;
 +use std::fs::write;
 +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::DiagnosticLevel;
 +use cargo_metadata::Message;
 +use rayon::prelude::*;
 +use serde::{Deserialize, Serialize};
 +use walkdir::{DirEntry, WalkDir};
 +
 +#[cfg(not(windows))]
 +const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver";
 +#[cfg(not(windows))]
 +const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy";
 +
 +#[cfg(windows)]
 +const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver.exe";
 +#[cfg(windows)]
 +const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy.exe";
 +
 +const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads";
 +const LINTCHECK_SOURCES: &str = "target/lintcheck/sources";
 +
 +/// List of sources to check, loaded from a .toml file
 +#[derive(Debug, Serialize, Deserialize)]
 +struct SourceList {
 +    crates: HashMap<String, TomlCrate>,
 +}
 +
 +/// A crate source stored inside the .toml
 +/// will be translated into on one of the `CrateSource` variants
 +#[derive(Debug, Serialize, Deserialize)]
 +struct TomlCrate {
 +    name: String,
 +    versions: Option<Vec<String>>,
 +    git_url: Option<String>,
 +    git_hash: Option<String>,
 +    path: Option<String>,
 +    options: Option<Vec<String>>,
 +}
 +
 +/// Represents an archive we download from crates.io, or a git repo, or a local repo/folder
 +/// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate`
 +#[derive(Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)]
 +enum CrateSource {
 +    CratesIo {
 +        name: String,
 +        version: String,
 +        options: Option<Vec<String>>,
 +    },
 +    Git {
 +        name: String,
 +        url: String,
 +        commit: String,
 +        options: Option<Vec<String>>,
 +    },
 +    Path {
 +        name: String,
 +        path: PathBuf,
 +        options: Option<Vec<String>>,
 +    },
 +}
 +
 +/// Represents the actual source code of a crate that we ran "cargo clippy" on
 +#[derive(Debug)]
 +struct Crate {
 +    version: String,
 +    name: String,
 +    // path to the extracted sources that clippy can check
 +    path: PathBuf,
 +    options: Option<Vec<String>>,
 +}
 +
 +/// A single warning that clippy issued while checking a `Crate`
 +#[derive(Debug)]
 +struct ClippyWarning {
 +    crate_name: String,
 +    file: String,
 +    line: usize,
 +    column: usize,
 +    lint_type: String,
 +    message: String,
 +    is_ice: bool,
 +}
 +
 +#[allow(unused)]
 +impl ClippyWarning {
 +    fn new(cargo_message: Message, krate: &Crate) -> Option<Self> {
 +        let diag = match cargo_message {
 +            Message::CompilerMessage(message) => message.message,
 +            _ => return None,
 +        };
 +
 +        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 = match Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) {
 +            Ok(stripped) => format!("$CARGO_HOME/{}", stripped.display()),
 +            Err(_) => format!(
 +                "target/lintcheck/sources/{}-{}/{}",
 +                krate.name, krate.version, span.file_name
 +            ),
 +        };
 +
 +        Some(Self {
 +            crate_name: krate.name.clone(),
 +            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 lint = format!("`{}`", self.lint_type);
 +
 +            let mut file = self.file.clone();
 +            if !file.starts_with('$') {
 +                file.insert_str(0, "../");
 +            }
 +
 +            let mut output = String::from("| ");
 +            let _ = write!(output, "[`{}`]({}#L{})", file_with_pos, file, self.line);
 +            let _ = write!(output, r#" | {:<50} | "{}" |"#, lint, self.message);
 +            output.push('\n');
 +            output
 +        } else {
 +            format!("{} {} \"{}\"\n", file_with_pos, self.lint_type, self.message)
 +        }
 +    }
 +}
 +
 +fn get(path: &str) -> Result<ureq::Response, ureq::Error> {
 +    const MAX_RETRIES: u8 = 4;
 +    let mut retries = 0;
 +    loop {
 +        match ureq::get(path).call() {
 +            Ok(res) => return Ok(res),
 +            Err(e) if retries >= MAX_RETRIES => return Err(e),
 +            Err(ureq::Error::Transport(e)) => eprintln!("Error: {}", e),
 +            Err(e) => return Err(e),
 +        }
 +        eprintln!("retrying in {} seconds...", retries);
 +        thread::sleep(Duration::from_secs(retries as u64));
 +        retries += 1;
 +    }
 +}
 +
 +impl CrateSource {
 +    /// Makes the sources available on the disk for clippy to check.
 +    /// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or
 +    /// copies a local folder
 +    fn download_and_extract(&self) -> Crate {
 +        match self {
 +            CrateSource::CratesIo { name, version, options } => {
 +                let extract_dir = PathBuf::from(LINTCHECK_SOURCES);
 +                let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS);
 +
 +                // url to download the crate from crates.io
 +                let url = format!("https://crates.io/api/v1/crates/{}/{}/download", name, version);
 +                println!("Downloading and extracting {} {} from {}", name, version, url);
 +                create_dirs(&krate_download_dir, &extract_dir);
 +
 +                let krate_file_path = krate_download_dir.join(format!("{}-{}.crate.tar.gz", name, version));
 +                // don't download/extract if we already have done so
 +                if !krate_file_path.is_file() {
 +                    // create a file path to download and write the crate data into
 +                    let mut krate_dest = std::fs::File::create(&krate_file_path).unwrap();
 +                    let mut krate_req = get(&url).unwrap().into_reader();
 +                    // copy the crate into the file
 +                    std::io::copy(&mut krate_req, &mut krate_dest).unwrap();
 +
 +                    // unzip the tarball
 +                    let ungz_tar = flate2::read::GzDecoder::new(std::fs::File::open(&krate_file_path).unwrap());
 +                    // extract the tar archive
 +                    let mut archive = tar::Archive::new(ungz_tar);
 +                    archive.unpack(&extract_dir).expect("Failed to extract!");
 +                }
 +                // crate is extracted, return a new Krate object which contains the path to the extracted
 +                // sources that clippy can check
 +                Crate {
 +                    version: version.clone(),
 +                    name: name.clone(),
 +                    path: extract_dir.join(format!("{}-{}/", name, version)),
 +                    options: options.clone(),
 +                }
 +            },
 +            CrateSource::Git {
 +                name,
 +                url,
 +                commit,
 +                options,
 +            } => {
 +                let repo_path = {
 +                    let mut repo_path = PathBuf::from(LINTCHECK_SOURCES);
 +                    // add a -git suffix in case we have the same crate from crates.io and a git repo
 +                    repo_path.push(format!("{}-git", name));
 +                    repo_path
 +                };
 +                // clone the repo if we have not done so
 +                if !repo_path.is_dir() {
 +                    println!("Cloning {} and checking out {}", url, commit);
 +                    if !Command::new("git")
 +                        .arg("clone")
 +                        .arg(url)
 +                        .arg(&repo_path)
 +                        .status()
 +                        .expect("Failed to clone git repo!")
 +                        .success()
 +                    {
 +                        eprintln!("Failed to clone {} into {}", url, repo_path.display())
 +                    }
 +                }
 +                // check out the commit/branch/whatever
 +                if !Command::new("git")
 +                    .arg("checkout")
 +                    .arg(commit)
 +                    .current_dir(&repo_path)
 +                    .status()
 +                    .expect("Failed to check out commit")
 +                    .success()
 +                {
 +                    eprintln!("Failed to checkout {} of repo at {}", commit, repo_path.display())
 +                }
 +
 +                Crate {
 +                    version: commit.clone(),
 +                    name: name.clone(),
 +                    path: repo_path,
 +                    options: options.clone(),
 +                }
 +            },
 +            CrateSource::Path { name, path, options } => {
 +                // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file.
 +                // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory
 +                // as a result of this filter.
 +                let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name);
 +                if dest_crate_root.exists() {
 +                    println!("Deleting existing directory at {:?}", dest_crate_root);
 +                    std::fs::remove_dir_all(&dest_crate_root).unwrap();
 +                }
 +
 +                println!("Copying {:?} to {:?}", path, dest_crate_root);
 +
 +                fn is_cache_dir(entry: &DirEntry) -> bool {
 +                    std::fs::read(entry.path().join("CACHEDIR.TAG"))
 +                        .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55"))
 +                        .unwrap_or(false)
 +                }
 +
 +                for entry in WalkDir::new(path).into_iter().filter_entry(|e| !is_cache_dir(e)) {
 +                    let entry = entry.unwrap();
 +                    let entry_path = entry.path();
 +                    let relative_entry_path = entry_path.strip_prefix(path).unwrap();
 +                    let dest_path = dest_crate_root.join(relative_entry_path);
 +                    let metadata = entry_path.symlink_metadata().unwrap();
 +
 +                    if metadata.is_dir() {
 +                        std::fs::create_dir(dest_path).unwrap();
 +                    } else if metadata.is_file() {
 +                        std::fs::copy(entry_path, dest_path).unwrap();
 +                    }
 +                }
 +
 +                Crate {
 +                    version: String::from("local"),
 +                    name: name.clone(),
 +                    path: dest_crate_root,
 +                    options: options.clone(),
 +                }
 +            },
 +        }
 +    }
 +}
 +
 +impl Crate {
 +    /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy
 +    /// issued
 +    fn run_clippy_lints(
 +        &self,
 +        cargo_clippy_path: &Path,
 +        target_dir_index: &AtomicUsize,
 +        total_crates_to_lint: usize,
 +        config: &LintcheckConfig,
 +        lint_filter: &Vec<String>,
 +    ) -> Vec<ClippyWarning> {
 +        // advance the atomic index by one
 +        let index = target_dir_index.fetch_add(1, Ordering::SeqCst);
 +        // "loop" the index within 0..thread_limit
 +        let thread_index = index % 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 args = if config.fix {
 +            vec!["--fix", "--"]
 +        } else {
 +            vec!["--", "--message-format=json", "--"]
 +        };
 +
 +        if let Some(options) = &self.options {
 +            for opt in options {
 +                args.push(opt);
 +            }
 +        } else {
 +            args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"])
 +        }
 +
 +        if lint_filter.is_empty() {
 +            args.push("--cap-lints=warn");
 +        } else {
 +            args.push("--cap-lints=allow");
 +            args.extend(lint_filter.iter().map(|filter| filter.as_str()))
 +        }
 +
 +        let all_output = std::process::Command::new(&cargo_clippy_path)
 +            // use the looping index to create individual target dirs
 +            .env(
 +                "CARGO_TARGET_DIR",
 +                shared_target_dir.join(format!("_{:?}", thread_index)),
 +            )
 +            // lint warnings will look like this:
 +            // src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter`
 +            .args(&args)
 +            .current_dir(&self.path)
 +            .output()
 +            .unwrap_or_else(|error| {
 +                panic!(
 +                    "Encountered error:\n{:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
 +                    error,
 +                    &cargo_clippy_path.display(),
 +                    &self.path.display()
 +                );
 +            });
 +        let stdout = String::from_utf8_lossy(&all_output.stdout);
 +        let stderr = String::from_utf8_lossy(&all_output.stderr);
 +        let status = &all_output.status;
 +
 +        if !status.success() {
 +            eprintln!(
 +                "\nWARNING: bad exit status after checking {} {} \n",
 +                self.name, self.version
 +            );
 +        }
 +
 +        if 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!(
 +                    "ERROR: failed to apply some suggetion to {} / to (sub)crate {}",
 +                    self.name, subcrate
 +                );
 +            }
 +            // fast path, we don't need the warnings anyway
 +            return Vec::new();
 +        }
 +
 +        // get all clippy warnings and ICEs
 +        let warnings: Vec<ClippyWarning> = Message::parse_stream(stdout.as_bytes())
 +            .filter_map(|msg| ClippyWarning::new(msg.unwrap(), &self))
 +            .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 `toml` file and return a list of `CrateSources` that we want to check with clippy
 +fn read_crates(toml_path: &Path) -> Vec<CrateSource> {
 +    let toml_content: String =
 +        std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display()));
 +    let crate_list: SourceList =
 +        toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e));
 +    // parse the hashmap of the toml file into a list of crates
 +    let tomlcrates: Vec<TomlCrate> = crate_list
 +        .crates
 +        .into_iter()
 +        .map(|(_cratename, tomlcrate)| tomlcrate)
 +        .collect();
 +
 +    // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate =>
 +    // multiple Cratesources)
 +    let mut crate_sources = Vec::new();
 +    tomlcrates.into_iter().for_each(|tk| {
 +        if let Some(ref path) = tk.path {
 +            crate_sources.push(CrateSource::Path {
 +                name: tk.name.clone(),
 +                path: PathBuf::from(path),
 +                options: tk.options.clone(),
 +            });
 +        } else if let Some(ref versions) = tk.versions {
 +            // if we have multiple versions, save each one
 +            versions.iter().for_each(|ver| {
 +                crate_sources.push(CrateSource::CratesIo {
 +                    name: tk.name.clone(),
 +                    version: ver.to_string(),
 +                    options: tk.options.clone(),
 +                });
 +            })
 +        } 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);
 +            if tk.git_hash.is_some() != tk.git_url.is_some() {
 +                panic!("Error: Encountered TomlCrate with only one of git_hash and git_url!");
 +            }
 +            if tk.path.is_some() && (tk.git_hash.is_some() || tk.versions.is_some()) {
 +                panic!("Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields");
 +            }
 +            unreachable!("Failed to translate TomlCrate into CrateSource!");
 +        }
 +    });
 +    // sort the crates
 +    crate_sources.sort();
 +
 +    crate_sources
 +}
 +
 +/// 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
 +    stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint));
 +
 +    let mut header = String::from("| lint                                               | count |\n");
 +    header.push_str("| -------------------------------------------------- | ----- |\n");
 +    let stats_string = stats
 +        .iter()
 +        .map(|(lint, count)| format!("| {:<50} |  {:>4} |\n", lint, count))
 +        .fold(header, |mut table, line| {
 +            table.push_str(&line);
 +            table
 +        });
 +
 +    (stats_string, counter)
 +}
 +
 +/// check if the latest modification of the logfile is older than the modification date of the
 +/// clippy binary, if this is true, we should clean the lintchec shared target directory and recheck
 +fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool {
 +    if !lintcheck_logs_path.exists() {
 +        return true;
 +    }
 +
 +    let clippy_modified: std::time::SystemTime = {
 +        let mut times = [CLIPPY_DRIVER_PATH, CARGO_CLIPPY_PATH].iter().map(|p| {
 +            std::fs::metadata(p)
 +                .expect("failed to get metadata of file")
 +                .modified()
 +                .expect("failed to get modification date")
 +        });
 +        // the oldest modification of either of the binaries
 +        std::cmp::max(times.next().unwrap(), times.next().unwrap())
 +    };
 +
 +    let logs_modified: std::time::SystemTime = std::fs::metadata(lintcheck_logs_path)
 +        .expect("failed to get metadata of file")
 +        .modified()
 +        .expect("failed to get modification date");
 +
 +    // time is represented in seconds since X
 +    // logs_modified 2 and clippy_modified 5 means clippy binary is older and we need to recheck
 +    logs_modified < clippy_modified
 +}
 +
 +fn main() {
 +    // assert that we launch lintcheck from the repo root (via cargo lintcheck)
 +    if std::fs::metadata("lintcheck/Cargo.toml").is_err() {
-     // download and extract the crates, then run clippy on them and collect clippys warnings
++        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");
 +
 +    // if the clippy bin is newer than our logs, throw away target dirs to force clippy to
 +    // refresh the logs
 +    if lintcheck_needs_rerun(&config.lintcheck_results_path) {
 +        let shared_target_dir = "target/lintcheck/shared_target_dir";
 +        // if we get an Err here, the shared target dir probably does simply not exist
 +        if let Ok(metadata) = std::fs::metadata(&shared_target_dir) {
 +            if metadata.is_dir() {
 +                println!("Clippy is newer than lint check logs, clearing lintcheck shared target dir...");
 +                std::fs::remove_dir_all(&shared_target_dir)
 +                    .expect("failed to remove target/lintcheck/shared_target_dir");
 +            }
 +        }
 +    }
 +
 +    let cargo_clippy_path: PathBuf = PathBuf::from(CARGO_CLIPPY_PATH)
 +        .canonicalize()
 +        .expect("failed to canonicalize path to clippy binary");
 +
 +    // assert that clippy is found
 +    assert!(
 +        cargo_clippy_path.is_file(),
 +        "target/debug/cargo-clippy binary not found! {}",
 +        cargo_clippy_path.display()
 +    );
 +
 +    let clippy_ver = std::process::Command::new(CARGO_CLIPPY_PATH)
 +        .arg("--version")
 +        .output()
 +        .map(|o| String::from_utf8_lossy(&o.stdout).into_owned())
 +        .expect("could not get clippy version!");
 +
++    // download and extract the crates, then run clippy on them and collect clippy's warnings
 +    // flatten into one big list of warnings
 +
 +    let crates = read_crates(&config.sources_toml_path);
 +    let old_stats = read_stats_from_file(&config.lintcheck_results_path);
 +
 +    let counter = AtomicUsize::new(1);
 +    let lint_filter: Vec<String> = config
 +        .lint_filter
 +        .iter()
 +        .map(|filter| {
 +            let mut filter = filter.clone();
 +            filter.insert_str(0, "--force-warn=");
 +            filter
 +        })
 +        .collect();
 +
 +    let 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 clippy_warnings: Vec<ClippyWarning> = crates
 +        .par_iter()
 +        .flat_map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &counter, crates.len(), &config, &lint_filter))
 +        .collect();
 +
 +    // if we are in --fix mode, don't change the log files, terminate here
 +    if config.fix {
 +        return;
 +    }
 +
 +    // generate some stats
 +    let (stats_formatted, new_stats) = gather_stats(&clippy_warnings);
 +
 +    // grab crashes/ICEs, save the crate name and the ice message
 +    let ices: Vec<(&String, &String)> = clippy_warnings
 +        .iter()
 +        .filter(|warning| warning.is_ice)
 +        .map(|w| (&w.crate_name, &w.message))
 +        .collect();
 +
 +    let mut all_msgs: Vec<String> = clippy_warnings
 +        .iter()
 +        .map(|warn| warn.to_output(config.markdown))
 +        .collect();
 +    all_msgs.sort();
 +    all_msgs.push("\n\n### Stats:\n\n".into());
 +    all_msgs.push(stats_formatted);
 +
 +    // save the text into lintcheck-logs/logs.txt
 +    let mut text = clippy_ver; // clippy version number on top
 +    text.push_str("\n### Reports\n\n");
 +    if config.markdown {
 +        text.push_str("| file | lint | message |\n");
 +        text.push_str("| --- | --- | --- |\n");
 +    }
 +    write!(text, "{}", all_msgs.join("")).unwrap();
 +    text.push_str("\n\n### ICEs:\n");
 +    for (cratename, msg) in ices.iter() {
 +        let _ = write!(text, "{}: '{}'", cratename, msg);
 +    }
 +
 +    println!("Writing logs to {}", config.lintcheck_results_path.display());
 +    std::fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap();
 +    write(&config.lintcheck_results_path, text).unwrap();
 +
 +    print_stats(old_stats, new_stats, &config.lint_filter);
 +}
 +
 +/// read the previous stats from the lintcheck-log file
 +fn read_stats_from_file(file_path: &Path) -> HashMap<String, usize> {
 +    let file_content: String = match std::fs::read_to_string(file_path).ok() {
 +        Some(content) => content,
 +        None => {
 +            return HashMap::new();
 +        },
 +    };
 +
 +    let lines: Vec<String> = file_content.lines().map(ToString::to_string).collect();
 +
 +    lines
 +        .iter()
 +        .skip_while(|line| line.as_str() != "### Stats:")
 +        // Skipping the table header and the `Stats:` label
 +        .skip(4)
 +        .take_while(|line| line.starts_with("| "))
 +        .filter_map(|line| {
 +            let mut spl = line.split('|');
 +            // Skip the first `|` symbol
 +            spl.next();
 +            if let (Some(lint), Some(count)) = (spl.next(), spl.next()) {
 +                Some((lint.trim().to_string(), count.trim().parse::<usize>().unwrap()))
 +            } else {
 +                None
 +            }
 +        })
 +        .collect::<HashMap<String, usize>>()
 +}
 +
 +/// print how lint counts changed between runs
 +fn print_stats(old_stats: HashMap<String, usize>, new_stats: HashMap<&String, usize>, lint_filter: &Vec<String>) {
 +    let same_in_both_hashmaps = old_stats
 +        .iter()
 +        .filter(|(old_key, old_val)| new_stats.get::<&String>(&old_key) == Some(old_val))
 +        .map(|(k, v)| (k.to_string(), *v))
 +        .collect::<Vec<(String, usize)>>();
 +
 +    let mut old_stats_deduped = old_stats;
 +    let mut new_stats_deduped = new_stats;
 +
 +    // remove duplicates from both hashmaps
 +    same_in_both_hashmaps.iter().for_each(|(k, v)| {
 +        assert!(old_stats_deduped.remove(k) == Some(*v));
 +        assert!(new_stats_deduped.remove(k) == Some(*v));
 +    });
 +
 +    println!("\nStats:");
 +
 +    // list all new counts  (key is in new stats but not in old stats)
 +    new_stats_deduped
 +        .iter()
 +        .filter(|(new_key, _)| old_stats_deduped.get::<str>(&new_key).is_none())
 +        .for_each(|(new_key, new_value)| {
 +            println!("{} 0 => {}", new_key, new_value);
 +        });
 +
 +    // list all changed counts (key is in both maps but value differs)
 +    new_stats_deduped
 +        .iter()
 +        .filter(|(new_key, _new_val)| old_stats_deduped.get::<str>(&new_key).is_some())
 +        .for_each(|(new_key, new_val)| {
 +            let old_val = old_stats_deduped.get::<str>(&new_key).unwrap();
 +            println!("{} {} => {}", new_key, old_val, new_val);
 +        });
 +
 +    // list all gone counts (key is in old status but not in new stats)
 +    old_stats_deduped
 +        .iter()
 +        .filter(|(old_key, _)| new_stats_deduped.get::<&String>(&old_key).is_none())
 +        .filter(|(old_key, _)| lint_filter.is_empty() || lint_filter.contains(old_key))
 +        .for_each(|(old_key, old_value)| {
 +            println!("{} {} => 0", old_key, old_value);
 +        });
 +}
 +
 +/// Create necessary directories to run the lintcheck tool.
 +///
 +/// # Panics
 +///
 +/// This function panics if creating one of the dirs fails.
 +fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) {
 +    std::fs::create_dir("target/lintcheck/").unwrap_or_else(|err| {
 +        if err.kind() != ErrorKind::AlreadyExists {
 +            panic!("cannot create lintcheck target dir");
 +        }
 +    });
 +    std::fs::create_dir(&krate_download_dir).unwrap_or_else(|err| {
 +        if err.kind() != ErrorKind::AlreadyExists {
 +            panic!("cannot create crate download dir");
 +        }
 +    });
 +    std::fs::create_dir(&extract_dir).unwrap_or_else(|err| {
 +        if err.kind() != ErrorKind::AlreadyExists {
 +            panic!("cannot create crate extraction dir");
 +        }
 +    });
 +}
 +
 +/// 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 6cc6d5036b379261f729c64128a5f4c68c604cf6,0000000000000000000000000000000000000000..e693e6837592fcaf0cf446016f11fdd3c6c173f2
mode 100644,000000..100644
--- /dev/null
@@@ -1,3 -1,0 +1,3 @@@
- channel = "nightly-2022-06-30"
 +[toolchain]
++channel = "nightly-2022-07-15"
 +components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
index bf7a39edf4c91e72d1c8e2bd04417d173b7ba9fb,0000000000000000000000000000000000000000..3615d07154dfb2d44d1f78c101475d0896675fd4
mode 100644,000000..100644
--- /dev/null
@@@ -1,469 -1,0 +1,470 @@@
 +#![feature(test)] // compiletest_rs requires this attribute
 +#![feature(once_cell)]
 +#![feature(is_sorted)]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use compiletest_rs as compiletest;
 +use compiletest_rs::common::Mode as TestMode;
 +
 +use std::collections::HashMap;
 +use std::env::{self, remove_var, set_var, var_os};
 +use std::ffi::{OsStr, OsString};
 +use std::fs;
 +use std::io;
 +use std::path::{Path, PathBuf};
 +use std::sync::LazyLock;
 +use test_utils::IS_RUSTC_TEST_SUITE;
 +
 +mod test_utils;
 +
 +// whether to run internal tests or not
 +const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal");
 +
 +/// All crates used in UI tests are listed here
 +static TEST_DEPENDENCIES: &[&str] = &[
 +    "clippy_lints",
 +    "clippy_utils",
 +    "derive_new",
 +    "futures",
 +    "if_chain",
 +    "itertools",
 +    "quote",
 +    "regex",
 +    "serde",
 +    "serde_derive",
 +    "syn",
 +    "tokio",
 +    "parking_lot",
 +    "rustc_semver",
 +];
 +
 +// Test dependencies may need an `extern crate` here to ensure that they show up
 +// in the depinfo file (otherwise cargo thinks they are unused)
 +#[allow(unused_extern_crates)]
 +extern crate clippy_lints;
 +#[allow(unused_extern_crates)]
 +extern crate clippy_utils;
 +#[allow(unused_extern_crates)]
 +extern crate derive_new;
 +#[allow(unused_extern_crates)]
 +extern crate futures;
 +#[allow(unused_extern_crates)]
 +extern crate if_chain;
 +#[allow(unused_extern_crates)]
 +extern crate itertools;
 +#[allow(unused_extern_crates)]
 +extern crate parking_lot;
 +#[allow(unused_extern_crates)]
 +extern crate quote;
 +#[allow(unused_extern_crates)]
 +extern crate rustc_semver;
 +#[allow(unused_extern_crates)]
 +extern crate syn;
 +#[allow(unused_extern_crates)]
 +extern crate tokio;
 +
 +/// Produces a string with an `--extern` flag for all UI test crate
 +/// dependencies.
 +///
 +/// The dependency files are located by parsing the depinfo file for this test
 +/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test
 +/// dependencies must be added to Cargo.toml at the project root. Test
 +/// dependencies that are not *directly* used by this test module require an
 +/// `extern crate` declaration.
 +static EXTERN_FLAGS: LazyLock<String> = LazyLock::new(|| {
 +    let current_exe_depinfo = {
 +        let mut path = env::current_exe().unwrap();
 +        path.set_extension("d");
 +        fs::read_to_string(path).unwrap()
 +    };
 +    let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len());
 +    for line in current_exe_depinfo.lines() {
 +        // each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:`
 +        let parse_name_path = || {
 +            if line.starts_with(char::is_whitespace) {
 +                return None;
 +            }
 +            let path_str = line.strip_suffix(':')?;
 +            let path = Path::new(path_str);
 +            if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") {
 +                return None;
 +            }
 +            let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?;
 +            // the "lib" prefix is not present for dll files
 +            let name = name.strip_prefix("lib").unwrap_or(name);
 +            Some((name, path_str))
 +        };
 +        if let Some((name, path)) = parse_name_path() {
 +            if TEST_DEPENDENCIES.contains(&name) {
 +                // A dependency may be listed twice if it is available in sysroot,
 +                // and the sysroot dependencies are listed first. As of the writing,
 +                // this only seems to apply to if_chain.
 +                crates.insert(name, path);
 +            }
 +        }
 +    }
 +    let not_found: Vec<&str> = TEST_DEPENDENCIES
 +        .iter()
 +        .copied()
 +        .filter(|n| !crates.contains_key(n))
 +        .collect();
 +    assert!(
 +        not_found.is_empty(),
 +        "dependencies not found in depinfo: {:?}\n\
 +        help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\
 +        help: Try adding to dev-dependencies in Cargo.toml\n\
 +        help: Be sure to also add `extern crate ...;` to tests/compile-test.rs",
 +        not_found,
 +    );
 +    crates
 +        .into_iter()
 +        .map(|(name, path)| format!(" --extern {}={}", name, path))
 +        .collect()
 +});
 +
 +fn base_config(test_dir: &str) -> compiletest::Config {
 +    let mut config = compiletest::Config {
 +        edition: Some("2021".into()),
 +        mode: TestMode::Ui,
 +        ..Default::default()
 +    };
 +
 +    if let Ok(filters) = env::var("TESTNAME") {
 +        config.filters = filters.split(',').map(ToString::to_string).collect();
 +    }
 +
 +    if let Some(path) = option_env!("RUSTC_LIB_PATH") {
 +        let path = PathBuf::from(path);
 +        config.run_lib_path = path.clone();
 +        config.compile_lib_path = path;
 +    }
 +    let current_exe_path = env::current_exe().unwrap();
 +    let deps_path = current_exe_path.parent().unwrap();
 +    let profile_path = deps_path.parent().unwrap();
 +
 +    // Using `-L dependency={}` enforces that external dependencies are added with `--extern`.
 +    // This is valuable because a) it allows us to monitor what external dependencies are used
 +    // and b) it ensures that conflicting rlibs are resolved properly.
 +    let host_libs = option_env!("HOST_LIBS")
 +        .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display()))
 +        .unwrap_or_default();
 +    config.target_rustcflags = Some(format!(
 +        "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}",
 +        deps_path.display(),
 +        host_libs,
 +        &*EXTERN_FLAGS,
 +    ));
 +
 +    config.src_base = Path::new("tests").join(test_dir);
 +    config.build_base = profile_path.join("test").join(test_dir);
 +    config.rustc_path = profile_path.join(if cfg!(windows) {
 +        "clippy-driver.exe"
 +    } else {
 +        "clippy-driver"
 +    });
 +    config
 +}
 +
 +fn run_ui() {
 +    let mut config = base_config("ui");
 +    config.rustfix_coverage = true;
 +    // use tests/clippy.toml
 +    let _g = VarGuard::set("CARGO_MANIFEST_DIR", fs::canonicalize("tests").unwrap());
 +    let _threads = VarGuard::set(
 +        "RUST_TEST_THREADS",
 +        // if RUST_TEST_THREADS is set, adhere to it, otherwise override it
 +        env::var("RUST_TEST_THREADS").unwrap_or_else(|_| {
 +            std::thread::available_parallelism()
 +                .map_or(1, std::num::NonZeroUsize::get)
 +                .to_string()
 +        }),
 +    );
 +    compiletest::run_tests(&config);
 +    check_rustfix_coverage();
 +}
 +
 +fn run_internal_tests() {
 +    // only run internal tests with the internal-tests feature
 +    if !RUN_INTERNAL_TESTS {
 +        return;
 +    }
 +    let config = base_config("ui-internal");
 +    compiletest::run_tests(&config);
 +}
 +
 +fn run_ui_toml() {
 +    fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
 +        let mut result = true;
 +        let opts = compiletest::test_opts(config);
 +        for dir in fs::read_dir(&config.src_base)? {
 +            let dir = dir?;
 +            if !dir.file_type()?.is_dir() {
 +                continue;
 +            }
 +            let dir_path = dir.path();
 +            let _g = VarGuard::set("CARGO_MANIFEST_DIR", &dir_path);
 +            for file in fs::read_dir(&dir_path)? {
 +                let file = file?;
 +                let file_path = file.path();
 +                if file.file_type()?.is_dir() {
 +                    continue;
 +                }
 +                if file_path.extension() != Some(OsStr::new("rs")) {
 +                    continue;
 +                }
 +                let paths = compiletest::common::TestPaths {
 +                    file: file_path,
 +                    base: config.src_base.clone(),
 +                    relative_dir: dir_path.file_name().unwrap().into(),
 +                };
 +                let test_name = compiletest::make_test_name(config, &paths);
 +                let index = tests
 +                    .iter()
 +                    .position(|test| test.desc.name == test_name)
 +                    .expect("The test should be in there");
 +                result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
 +            }
 +        }
 +        Ok(result)
 +    }
 +
 +    let mut config = base_config("ui-toml");
 +    config.src_base = config.src_base.canonicalize().unwrap();
 +
 +    let tests = compiletest::make_tests(&config);
 +
 +    let res = run_tests(&config, tests);
 +    match res {
 +        Ok(true) => {},
 +        Ok(false) => panic!("Some tests failed"),
 +        Err(e) => {
 +            panic!("I/O failure during tests: {:?}", e);
 +        },
 +    }
 +}
 +
 +fn run_ui_cargo() {
 +    fn run_tests(
 +        config: &compiletest::Config,
 +        filters: &[String],
 +        mut tests: Vec<tester::TestDescAndFn>,
 +    ) -> Result<bool, io::Error> {
 +        let mut result = true;
 +        let opts = compiletest::test_opts(config);
 +
 +        for dir in fs::read_dir(&config.src_base)? {
 +            let dir = dir?;
 +            if !dir.file_type()?.is_dir() {
 +                continue;
 +            }
 +
 +            // Use the filter if provided
 +            let dir_path = dir.path();
 +            for filter in filters {
 +                if !dir_path.ends_with(filter) {
 +                    continue;
 +                }
 +            }
 +
 +            for case in fs::read_dir(&dir_path)? {
 +                let case = case?;
 +                if !case.file_type()?.is_dir() {
 +                    continue;
 +                }
 +
 +                let src_path = case.path().join("src");
 +
 +                // When switching between branches, if the previous branch had a test
 +                // that the current branch does not have, the directory is not removed
 +                // because an ignored Cargo.lock file exists.
 +                if !src_path.exists() {
 +                    continue;
 +                }
 +
 +                env::set_current_dir(&src_path)?;
 +
 +                let cargo_toml_path = case.path().join("Cargo.toml");
 +                let cargo_content = fs::read(&cargo_toml_path)?;
 +                let cargo_parsed: toml::Value = toml::from_str(
 +                    std::str::from_utf8(&cargo_content).expect("`Cargo.toml` is not a valid utf-8 file!"),
 +                )
 +                .expect("Can't parse `Cargo.toml`");
 +
 +                let _g = VarGuard::set("CARGO_MANIFEST_DIR", case.path());
 +                let _h = VarGuard::set(
 +                    "CARGO_PKG_RUST_VERSION",
 +                    cargo_parsed
 +                        .get("package")
 +                        .and_then(|p| p.get("rust-version"))
 +                        .and_then(toml::Value::as_str)
 +                        .unwrap_or(""),
 +                );
 +
 +                for file in fs::read_dir(&src_path)? {
 +                    let file = file?;
 +                    if file.file_type()?.is_dir() {
 +                        continue;
 +                    }
 +
 +                    // Search for the main file to avoid running a test for each file in the project
 +                    let file_path = file.path();
 +                    match file_path.file_name().and_then(OsStr::to_str) {
 +                        Some("main.rs") => {},
 +                        _ => continue,
 +                    }
 +                    let _g = VarGuard::set("CLIPPY_CONF_DIR", case.path());
 +                    let paths = compiletest::common::TestPaths {
 +                        file: file_path,
 +                        base: config.src_base.clone(),
 +                        relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
 +                    };
 +                    let test_name = compiletest::make_test_name(config, &paths);
 +                    let index = tests
 +                        .iter()
 +                        .position(|test| test.desc.name == test_name)
 +                        .expect("The test should be in there");
 +                    result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
 +                }
 +            }
 +        }
 +        Ok(result)
 +    }
 +
 +    if IS_RUSTC_TEST_SUITE {
 +        return;
 +    }
 +
 +    let mut config = base_config("ui-cargo");
 +    config.src_base = config.src_base.canonicalize().unwrap();
 +
 +    let tests = compiletest::make_tests(&config);
 +
 +    let current_dir = env::current_dir().unwrap();
 +    let res = run_tests(&config, &config.filters, tests);
 +    env::set_current_dir(current_dir).unwrap();
 +
 +    match res {
 +        Ok(true) => {},
 +        Ok(false) => panic!("Some tests failed"),
 +        Err(e) => {
 +            panic!("I/O failure during tests: {:?}", e);
 +        },
 +    }
 +}
 +
 +#[test]
 +fn compile_test() {
 +    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
 +    run_ui();
 +    run_ui_toml();
 +    run_ui_cargo();
 +    run_internal_tests();
 +}
 +
 +const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[
 +    "assign_ops2.rs",
 +    "borrow_deref_ref_unfixable.rs",
 +    "cast_size_32bit.rs",
 +    "char_lit_as_u8.rs",
 +    "cmp_owned/without_suggestion.rs",
 +    "dbg_macro.rs",
 +    "deref_addrof_double_trigger.rs",
 +    "doc/unbalanced_ticks.rs",
 +    "eprint_with_newline.rs",
 +    "explicit_counter_loop.rs",
 +    "iter_skip_next_unfixable.rs",
 +    "let_and_return.rs",
 +    "literals.rs",
 +    "map_flatten.rs",
 +    "map_unwrap_or.rs",
 +    "match_bool.rs",
 +    "mem_replace_macro.rs",
 +    "needless_arbitrary_self_type_unfixable.rs",
 +    "needless_borrow_pat.rs",
 +    "needless_for_each_unfixable.rs",
 +    "nonminimal_bool.rs",
 +    "print_literal.rs",
 +    "print_with_newline.rs",
 +    "redundant_static_lifetimes_multiple.rs",
 +    "ref_binding_to_reference.rs",
 +    "repl_uninit.rs",
 +    "result_map_unit_fn_unfixable.rs",
 +    "search_is_some.rs",
 +    "single_component_path_imports_nested_first.rs",
 +    "string_add.rs",
 +    "toplevel_ref_arg_non_rustfix.rs",
++    "trait_duplication_in_bounds.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("target/debug/test/ui/rustfix_missing_coverage.txt");
 +
 +    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_path in missing_coverage_contents.lines() {
 +            if Path::new(rs_path).starts_with("tests/ui/crashes") {
 +                continue;
 +            }
 +            let filename = Path::new(rs_path).strip_prefix("tests/ui/").unwrap();
 +            assert!(
 +                RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS
 +                    .binary_search_by_key(&filename, Path::new)
 +                    .is_ok(),
 +                "`{}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \
 +                Please either add `// run-rustfix` at the top of the file or add the file to \
 +                `RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS` in `tests/compile-test.rs`.",
 +                rs_path,
 +            );
 +        }
 +    }
 +}
 +
 +#[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 exists",
 +            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()
 +        );
 +    }
 +}
 +
 +/// 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 fffc53603424ba831f52c94c14be81ed01af4b00,0000000000000000000000000000000000000000..5697e8680cd6f86779a303edfd5b63d940e8da14
mode 100644,000000..100644
--- /dev/null
@@@ -1,98 -1,0 +1,104 @@@
-         .arg("--all-features")
-         .arg("--")
-         .args(args)
-         .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
 +//! This test is a part of quality control and makes clippy eat what it produces. Awesome lints and
 +//! long error messages
 +//!
 +//! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context
 +
 +#![feature(once_cell)]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use std::path::PathBuf;
 +use std::process::Command;
 +use test_utils::IS_RUSTC_TEST_SUITE;
 +
 +mod test_utils;
 +
 +#[test]
 +fn dogfood_clippy() {
 +    if IS_RUSTC_TEST_SUITE {
 +        return;
 +    }
 +
 +    // "" is the root package
 +    for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] {
 +        run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]);
 +    }
 +}
 +
 +#[test]
 +#[ignore]
 +#[cfg(feature = "internal")]
 +fn run_metadata_collection_lint() {
 +    use std::fs::File;
 +    use std::time::SystemTime;
 +
 +    // Setup for validation
 +    let metadata_output_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("util/gh-pages/lints.json");
 +    let start_time = SystemTime::now();
 +
 +    // Run collection as is
 +    std::env::set_var("ENABLE_METADATA_COLLECTION", "1");
 +    run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]);
 +
 +    // Check if cargo caching got in the way
 +    if let Ok(file) = File::open(metadata_output_path) {
 +        if let Ok(metadata) = file.metadata() {
 +            if let Ok(last_modification) = metadata.modified() {
 +                if last_modification > start_time {
 +                    // The output file has been modified. Most likely by a hungry
 +                    // metadata collection monster. So We'll return.
 +                    return;
 +                }
 +            }
 +        }
 +    }
 +
 +    // Force cargo to invalidate the caches
 +    filetime::set_file_mtime(
 +        PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("clippy_lints/src/lib.rs"),
 +        filetime::FileTime::now(),
 +    )
 +    .unwrap();
 +
 +    // Running the collection again
 +    run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]);
 +}
 +
 +fn run_clippy_for_package(project: &str, args: &[&str]) {
 +    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
 +
 +    let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH);
 +
 +    command
 +        .current_dir(root_dir.join(project))
 +        .env("CARGO_INCREMENTAL", "0")
 +        .arg("clippy")
 +        .arg("--all-targets")
++        .arg("--all-features");
++
++    if let Ok(dogfood_args) = std::env::var("__CLIPPY_DOGFOOD_ARGS") {
++        for arg in dogfood_args.split_whitespace() {
++            command.arg(arg);
++        }
++    }
++
++    command.arg("--").args(args);
++    command.arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
 +
 +    if cfg!(feature = "internal") {
 +        // internal lints only exist if we build with the internal feature
 +        command.args(&["-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 dd1d441203600cf6f3d124177a4ef29f1ea0d67c,0000000000000000000000000000000000000000..c3aae1a9aa2d01992ee0c00b6c847bc869b033d0
mode 100644,000000..100644
--- /dev/null
@@@ -1,111 -1,0 +1,116 @@@
-         let content: String = std::fs::read_to_string(&path).unwrap();
++#![feature(once_cell)]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use std::ffi::OsStr;
 +use std::path::PathBuf;
++use std::sync::LazyLock;
 +
 +use regex::RegexSet;
 +
 +#[derive(Debug)]
 +struct Message {
 +    path: PathBuf,
 +    bad_lines: Vec<String>,
 +}
 +
 +impl Message {
 +    fn new(path: PathBuf) -> Self {
-         let regex_set: RegexSet = RegexSet::new(&[
-             r"error: [A-Z]",
-             r"help: [A-Z]",
-             r"warning: [A-Z]",
-             r"note: [A-Z]",
-             r"try this: [A-Z]",
-             r"error: .*[.!]$",
-             r"help: .*[.!]$",
-             r"warning: .*[.!]$",
-             r"note: .*[.!]$",
-             r"try this: .*[.!]$",
-         ])
-         .unwrap();
 +        // we don't want the first letter after "error: ", "help: " ... to be capitalized
 +        // also no punctuation (except for "?" ?) at the end of a line
-         let exceptions_set: RegexSet = RegexSet::new(&[
-             r".*C-like enum variant discriminant is not portable to 32-bit targets",
-             r".*did you mean `unix`?",
-             r".*the arguments may be inverted...",
-             r".*Intel x86 assembly syntax used",
-             r".*AT&T x86 assembly syntax used",
-             r".*remove .*the return type...",
-             r"note: Clippy version: .*",
-             r"the compiler unexpectedly panicked. this is a bug.",
-         ])
-         .unwrap();
++        static REGEX_SET: LazyLock<RegexSet> = LazyLock::new(|| {
++            RegexSet::new(&[
++                r"error: [A-Z]",
++                r"help: [A-Z]",
++                r"warning: [A-Z]",
++                r"note: [A-Z]",
++                r"try this: [A-Z]",
++                r"error: .*[.!]$",
++                r"help: .*[.!]$",
++                r"warning: .*[.!]$",
++                r"note: .*[.!]$",
++                r"try this: .*[.!]$",
++            ])
++            .unwrap()
++        });
 +
 +        // sometimes the first character is capitalized and it is legal (like in "C-like enum variants") or
 +        // we want to ask a question ending in "?"
-             .filter(|line| regex_set.matches(line).matched_any())
++        static EXCEPTIONS_SET: LazyLock<RegexSet> = LazyLock::new(|| {
++            RegexSet::new(&[
++                r"\.\.\.$",
++                r".*C-like enum variant discriminant is not portable to 32-bit targets",
++                r".*Intel x86 assembly syntax used",
++                r".*AT&T x86 assembly syntax used",
++                r"note: Clippy version: .*",
++                r"the compiler unexpectedly panicked. this is a bug.",
++            ])
++            .unwrap()
++        });
++
++        let content: String = std::fs::read_to_string(&path).unwrap();
 +
 +        let bad_lines = content
 +            .lines()
-             .filter(|line| !exceptions_set.matches(line).matched_any())
++            .filter(|line| REGEX_SET.matches(line).matched_any())
 +            // ignore exceptions
++            .filter(|line| !EXCEPTIONS_SET.matches(line).matched_any())
 +            .map(ToOwned::to_owned)
 +            .collect::<Vec<String>>();
 +
 +        Message { path, bad_lines }
 +    }
 +}
 +
 +#[test]
 +fn lint_message_convention() {
 +    // disable the test inside the rustc test suite
 +    if option_env!("RUSTC_TEST_SUITE").is_some() {
 +        return;
 +    }
 +
 +    // make sure that lint messages:
 +    // * are not capitalized
 +    // * don't have punctuation at the end of the last sentence
 +
 +    // these directories have interesting tests
 +    let test_dirs = ["ui", "ui-cargo", "ui-internal", "ui-toml"]
 +        .iter()
 +        .map(PathBuf::from)
 +        .map(|p| {
 +            let base = PathBuf::from("tests");
 +            base.join(p)
 +        });
 +
 +    // gather all .stderr files
 +    let tests = test_dirs
 +        .flat_map(|dir| {
 +            std::fs::read_dir(dir)
 +                .expect("failed to read dir")
 +                .map(|direntry| direntry.unwrap().path())
 +        })
 +        .filter(|file| matches!(file.extension().map(OsStr::to_str), Some(Some("stderr"))));
 +
 +    // get all files that have any "bad lines" in them
 +    let bad_tests: Vec<Message> = tests
 +        .map(Message::new)
 +        .filter(|message| !message.bad_lines.is_empty())
 +        .collect();
 +
 +    for message in &bad_tests {
 +        eprintln!(
 +            "error: the test '{}' contained the following nonconforming lines :",
 +            message.path.display()
 +        );
 +        message.bad_lines.iter().for_each(|line| eprintln!("{}", line));
 +        eprintln!("\n\n");
 +    }
 +
 +    eprintln!(
 +        "\n\n\nLint message should not start with a capital letter and should not have punctuation at the end of the message unless multiple sentences are needed."
 +    );
 +    eprintln!("Check out the rustc-dev-guide for more information:");
 +    eprintln!("https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-structure\n\n\n");
 +
 +    assert!(bad_tests.is_empty());
 +}
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391,0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc
mode 100644,000000..100644
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++
index 99ca538b6e4a58bcecd21c2ea8bc7dc04037a9d5,0000000000000000000000000000000000000000..6478e65ac81ad04b0ac79c4a553fc1a0486d8213
mode 100644,000000..100644
--- /dev/null
@@@ -1,29 -1,0 +1,28 @@@
- #[feature(lint_reasons)]
++#![feature(lint_reasons)]
 +
 +mod a;
 +
 +mod b;
 +#[path = "b.rs"]
 +mod b2;
 +
 +mod c;
 +#[path = "c.rs"]
 +mod c2;
 +#[path = "c.rs"]
 +mod c3;
 +
 +mod from_other_module;
 +mod other_module;
 +
 +mod d;
 +#[path = "d.rs"]
 +mod d2;
 +#[path = "d.rs"]
 +#[expect(clippy::duplicate_mod)]
 +mod d3;
 +#[path = "d.rs"]
 +#[allow(clippy::duplicate_mod)]
 +mod d4;
 +
 +fn main() {}
index 61df1ad5d501aad6d95ea089f282af0e639d7ce0,0000000000000000000000000000000000000000..b450a2b18f25713eefe7c86857fe963ccf174c7e
mode 100644,000000..100644
--- /dev/null
@@@ -1,53 -1,0 +1,53 @@@
- error: file is loaded as a module multiple times: `$DIR/b.rs`
-   --> $DIR/main.rs:18:1
-    |
- LL |   mod d;
-    |   ^^^^^^ first loaded here
- LL | / #[path = "d.rs"]
- LL | | mod d2;
-    | |_______^ loaded again here
-    |
-    = help: replace all but one `mod` item with `use` items
 +error: file is loaded as a module multiple times: `$DIR/b.rs`
 +  --> $DIR/main.rs:5:1
 +   |
 +LL |   mod b;
 +   |   ^^^^^^ first loaded here
 +LL | / #[path = "b.rs"]
 +LL | | mod b2;
 +   | |_______^ loaded again here
 +   |
 +   = note: `-D clippy::duplicate-mod` implied by `-D warnings`
 +   = help: replace all but one `mod` item with `use` items
 +
 +error: file is loaded as a module multiple times: `$DIR/c.rs`
 +  --> $DIR/main.rs:9:1
 +   |
 +LL |   mod c;
 +   |   ^^^^^^ first loaded here
 +LL | / #[path = "c.rs"]
 +LL | | mod c2;
 +   | |_______^ loaded again here
 +LL | / #[path = "c.rs"]
 +LL | | mod c3;
 +   | |_______^ loaded again here
 +   |
 +   = help: replace all but one `mod` item with `use` items
 +
++error: file is loaded as a module multiple times: `$DIR/d.rs`
++  --> $DIR/main.rs:18:1
++   |
++LL |   mod d;
++   |   ^^^^^^ first loaded here
++LL | / #[path = "d.rs"]
++LL | | mod d2;
++   | |_______^ loaded again here
++   |
++   = help: replace all but one `mod` item with `use` items
++
 +error: file is loaded as a module multiple times: `$DIR/from_other_module.rs`
 +  --> $DIR/main.rs:15:1
 +   |
 +LL |   mod from_other_module;
 +   |   ^^^^^^^^^^^^^^^^^^^^^^ first loaded here
 +   |
 +  ::: $DIR/other_module/mod.rs:1:1
 +   |
 +LL | / #[path = "../from_other_module.rs"]
 +LL | | mod m;
 +   | |______^ loaded again here
 +   |
 +   = help: replace all but one `mod` item with `use` items
 +
 +error: aborting due to 4 previous errors
 +
index 5bd2c2799f03683cab8c787f8a5c827616e12bfc,0000000000000000000000000000000000000000..83a0af6b87acad985fad701b01885bf3ba9f838c
mode 100644,000000..100644
--- /dev/null
@@@ -1,137 -1,0 +1,142 @@@
 +#![allow(dead_code)]
 +
 +//! Used to test that certain lints don't trigger in imported external macros
 +
 +#[macro_export]
 +macro_rules! foofoo {
 +    () => {
 +        loop {}
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! must_use_unit {
 +    () => {
 +        #[must_use]
 +        fn foo() {}
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! try_err {
 +    () => {
 +        pub fn try_err_fn() -> Result<i32, i32> {
 +            let err: i32 = 1;
 +            // To avoid warnings during rustfix
 +            if true { Err(err)? } else { Ok(2) }
 +        }
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! string_add {
 +    () => {
 +        let y = "".to_owned();
 +        let z = y + "...";
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! take_external {
 +    ($s:expr) => {
 +        std::mem::replace($s, Default::default())
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! option_env_unwrap_external {
 +    ($env: expr) => {
 +        option_env!($env).unwrap()
 +    };
 +    ($env: expr, $message: expr) => {
 +        option_env!($env).expect($message)
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! ref_arg_binding {
 +    () => {
 +        let ref _y = 42;
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! ref_arg_function {
 +    () => {
 +        fn fun_example(ref _x: usize) {}
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! as_conv_with_arg {
 +    (0u32 as u64) => {
 +        ()
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! as_conv {
 +    () => {
 +        0u32 as u64
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! large_enum_variant {
 +    () => {
 +        enum LargeEnumInMacro {
 +            A(i32),
 +            B([i32; 8000]),
 +        }
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! field_reassign_with_default {
 +    () => {
 +        #[derive(Default)]
 +        struct A {
 +            pub i: i32,
 +            pub j: i64,
 +        }
 +        fn lint() {
 +            let mut a: A = Default::default();
 +            a.i = 42;
 +            a;
 +        }
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! default_numeric_fallback {
 +    () => {
 +        let x = 22;
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! mut_mut {
 +    () => {
 +        let mut_mut_ty: &mut &mut u32 = &mut &mut 1u32;
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! ptr_as_ptr_cast {
 +    ($ptr: ident) => {
 +        $ptr as *const i32
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! manual_rem_euclid {
 +    () => {
 +        let value: i32 = 5;
 +        let _: i32 = ((value % 4) + 4) % 4;
 +    };
 +}
++
++#[macro_export]
++macro_rules! equatable_if_let {
++    ($a:ident) => {{ if let 2 = $a {} }};
++}
index e00f061f28a94b67a91f87d105b43a4131d68bc0,0000000000000000000000000000000000000000..1a74cdb3ff65926ab94e679bdcbea64d4e51db0f
mode 100644,000000..100644
--- /dev/null
@@@ -1,44 -1,0 +1,56 @@@
- use std::collections::HashMap;
 +#![warn(clippy::all)]
 +#![allow(
 +    clippy::boxed_local,
 +    clippy::needless_pass_by_value,
 +    clippy::blacklisted_name,
 +    unused
 +)]
 +
- fn test(foo: Box<Vec<bool>>) {}
++use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
 +
 +macro_rules! boxit {
 +    ($init:expr, $x:ty) => {
 +        let _: Box<$x> = Box::new($init);
 +    };
 +}
 +
 +fn test_macro() {
 +    boxit!(Vec::new(), Vec<u8>);
 +}
 +
++fn test1(foo: Box<Vec<bool>>) {}
 +
 +fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
 +    // pass if #31 is fixed
 +    foo(vec![1, 2, 3])
 +}
 +
 +fn test3(foo: Box<String>) {}
 +
 +fn test4(foo: Box<HashMap<String, String>>) {}
 +
++fn test5(foo: Box<HashSet<i64>>) {}
++
++fn test6(foo: Box<VecDeque<i32>>) {}
++
++fn test7(foo: Box<LinkedList<i16>>) {}
++
++fn test8(foo: Box<BTreeMap<i8, String>>) {}
++
++fn test9(foo: Box<BTreeSet<u64>>) {}
++
++fn test10(foo: Box<BinaryHeap<u32>>) {}
++
 +fn test_local_not_linted() {
 +    let _: Box<Vec<bool>>;
 +}
 +
 +// All of these test should be allowed because they are part of the
 +// public api and `avoid_breaking_exported_api` is `false` by default.
 +pub fn pub_test(foo: Box<Vec<bool>>) {}
 +
 +pub fn pub_test_ret() -> Box<Vec<bool>> {
 +    Box::new(Vec::new())
 +}
 +
 +fn main() {}
index 6de85d05a99f32b59eac0905a7f19967d8419f6e,0000000000000000000000000000000000000000..2b28598ded92f41bc2d000d4fbb03dd1f674b074
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,75 @@@
-   --> $DIR/box_collection.rs:21:14
 +error: you seem to be trying to use `Box<Vec<..>>`. Consider using just `Vec<..>`
- LL | fn test(foo: Box<Vec<bool>>) {}
-    |              ^^^^^^^^^^^^^^
++  --> $DIR/box_collection.rs:21:15
 +   |
- error: aborting due to 3 previous errors
++LL | fn test1(foo: Box<Vec<bool>>) {}
++   |               ^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::box-collection` implied by `-D warnings`
 +   = help: `Vec<..>` is already on the heap, `Box<Vec<..>>` makes an extra allocation
 +
 +error: you seem to be trying to use `Box<String>`. Consider using just `String`
 +  --> $DIR/box_collection.rs:28:15
 +   |
 +LL | fn test3(foo: Box<String>) {}
 +   |               ^^^^^^^^^^^
 +   |
 +   = help: `String` is already on the heap, `Box<String>` makes an extra allocation
 +
 +error: you seem to be trying to use `Box<HashMap<..>>`. Consider using just `HashMap<..>`
 +  --> $DIR/box_collection.rs:30:15
 +   |
 +LL | fn test4(foo: Box<HashMap<String, String>>) {}
 +   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: `HashMap<..>` is already on the heap, `Box<HashMap<..>>` makes an extra allocation
 +
++error: you seem to be trying to use `Box<HashSet<..>>`. Consider using just `HashSet<..>`
++  --> $DIR/box_collection.rs:32:15
++   |
++LL | fn test5(foo: Box<HashSet<i64>>) {}
++   |               ^^^^^^^^^^^^^^^^^
++   |
++   = help: `HashSet<..>` is already on the heap, `Box<HashSet<..>>` makes an extra allocation
++
++error: you seem to be trying to use `Box<VecDeque<..>>`. Consider using just `VecDeque<..>`
++  --> $DIR/box_collection.rs:34:15
++   |
++LL | fn test6(foo: Box<VecDeque<i32>>) {}
++   |               ^^^^^^^^^^^^^^^^^^
++   |
++   = help: `VecDeque<..>` is already on the heap, `Box<VecDeque<..>>` makes an extra allocation
++
++error: you seem to be trying to use `Box<LinkedList<..>>`. Consider using just `LinkedList<..>`
++  --> $DIR/box_collection.rs:36:15
++   |
++LL | fn test7(foo: Box<LinkedList<i16>>) {}
++   |               ^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: `LinkedList<..>` is already on the heap, `Box<LinkedList<..>>` makes an extra allocation
++
++error: you seem to be trying to use `Box<BTreeMap<..>>`. Consider using just `BTreeMap<..>`
++  --> $DIR/box_collection.rs:38:15
++   |
++LL | fn test8(foo: Box<BTreeMap<i8, String>>) {}
++   |               ^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: `BTreeMap<..>` is already on the heap, `Box<BTreeMap<..>>` makes an extra allocation
++
++error: you seem to be trying to use `Box<BTreeSet<..>>`. Consider using just `BTreeSet<..>`
++  --> $DIR/box_collection.rs:40:15
++   |
++LL | fn test9(foo: Box<BTreeSet<u64>>) {}
++   |               ^^^^^^^^^^^^^^^^^^
++   |
++   = help: `BTreeSet<..>` is already on the heap, `Box<BTreeSet<..>>` makes an extra allocation
++
++error: you seem to be trying to use `Box<BinaryHeap<..>>`. Consider using just `BinaryHeap<..>`
++  --> $DIR/box_collection.rs:42:16
++   |
++LL | fn test10(foo: Box<BinaryHeap<u32>>) {}
++   |                ^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: `BinaryHeap<..>` is already on the heap, `Box<BinaryHeap<..>>` makes an extra allocation
++
++error: aborting due to 9 previous errors
 +
index 064482009517a600b68be2592ac5de70e0161c8a,0000000000000000000000000000000000000000..5e3a1a29693f58ae5e392e222d6fb9b467216ee3
mode 100644,000000..100644
--- /dev/null
@@@ -1,41 -1,0 +1,95 @@@
 +#![allow(dead_code)]
 +#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
 +
++use std::sync::Mutex;
++
 +// ##################################
 +// # Issue clippy#7369
 +// ##################################
 +#[derive(Debug)]
 +pub struct FooBar {
 +    foo: Vec<u32>,
 +}
 +
 +impl FooBar {
 +    pub fn bar(&mut self) {
 +        if true {
 +            self.foo.pop();
 +        } else {
 +            self.baz();
 +
 +            self.foo.pop();
 +
 +            self.baz()
 +        }
 +    }
 +
 +    fn baz(&mut self) {}
 +}
 +
 +fn foo(x: u32, y: u32) -> u32 {
 +    x / y
 +}
 +
 +fn main() {
 +    let x = (1, 2);
 +    let _ = if true {
 +        let (x, y) = x;
 +        foo(x, y)
 +    } else {
 +        let (y, x) = x;
 +        foo(x, y)
 +    };
++
++    let m = Mutex::new(0u32);
++    let l = m.lock().unwrap();
++    let _ = if true {
++        drop(l);
++        println!("foo");
++        m.lock().unwrap();
++        0
++    } else if *l == 0 {
++        drop(l);
++        println!("foo");
++        println!("bar");
++        m.lock().unwrap();
++        1
++    } else {
++        drop(l);
++        println!("foo");
++        println!("baz");
++        m.lock().unwrap();
++        2
++    };
++
++    if true {
++        let _guard = m.lock();
++        println!("foo");
++    } else {
++        println!("foo");
++    }
++
++    if true {
++        let _guard = m.lock();
++        println!("foo");
++        println!("bar");
++    } else {
++        let _guard = m.lock();
++        println!("foo");
++        println!("baz");
++    }
++
++    let mut c = 0;
++    for _ in 0..5 {
++        if c == 0 {
++            c += 1;
++            println!("0");
++        } else if c == 1 {
++            c += 1;
++            println!("1");
++        } else {
++            c += 1;
++            println!("more");
++        }
++    }
 +}
index 7125f741c150e764ba59141368aba6e6b3d303e0,0000000000000000000000000000000000000000..8990c3ba739b0fea45b8b907b38798eaa7bec42b
mode 100644,000000..100644
--- /dev/null
@@@ -1,118 -1,0 +1,118 @@@
- error: aborting due to 20 previous errors
 +error: casting `isize` to `i8` may truncate the value
 +  --> $DIR/cast_size_32bit.rs:12:5
 +   |
 +LL |     1isize as i8;
 +   |     ^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
 +
 +error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
 +  --> $DIR/cast_size_32bit.rs:15:5
 +   |
 +LL |     x0 as f64;
 +   |     ^^^^^^^^^
 +   |
 +   = note: `-D clippy::cast-precision-loss` implied by `-D warnings`
 +
 +error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
 +  --> $DIR/cast_size_32bit.rs:16:5
 +   |
 +LL |     x1 as f64;
 +   |     ^^^^^^^^^
 +
 +error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
 +  --> $DIR/cast_size_32bit.rs:17:5
 +   |
 +LL |     x0 as f32;
 +   |     ^^^^^^^^^
 +
 +error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
 +  --> $DIR/cast_size_32bit.rs:18:5
 +   |
 +LL |     x1 as f32;
 +   |     ^^^^^^^^^
 +
 +error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers
 +  --> $DIR/cast_size_32bit.rs:19:5
 +   |
 +LL |     1isize as i32;
 +   |     ^^^^^^^^^^^^^
 +
 +error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers
 +  --> $DIR/cast_size_32bit.rs:20:5
 +   |
 +LL |     1isize as u32;
 +   |     ^^^^^^^^^^^^^
 +
 +error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers
 +  --> $DIR/cast_size_32bit.rs:21:5
 +   |
 +LL |     1usize as u32;
 +   |     ^^^^^^^^^^^^^
 +
 +error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers
 +  --> $DIR/cast_size_32bit.rs:22:5
 +   |
 +LL |     1usize as i32;
 +   |     ^^^^^^^^^^^^^
 +
 +error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers
 +  --> $DIR/cast_size_32bit.rs:22:5
 +   |
 +LL |     1usize as i32;
 +   |     ^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
 +
 +error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers
 +  --> $DIR/cast_size_32bit.rs:24:5
 +   |
 +LL |     1i64 as isize;
 +   |     ^^^^^^^^^^^^^
 +
 +error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers
 +  --> $DIR/cast_size_32bit.rs:25:5
 +   |
 +LL |     1i64 as usize;
 +   |     ^^^^^^^^^^^^^
 +
 +error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers
 +  --> $DIR/cast_size_32bit.rs:26:5
 +   |
 +LL |     1u64 as isize;
 +   |     ^^^^^^^^^^^^^
 +
 +error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers
 +  --> $DIR/cast_size_32bit.rs:26:5
 +   |
 +LL |     1u64 as isize;
 +   |     ^^^^^^^^^^^^^
 +
 +error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers
 +  --> $DIR/cast_size_32bit.rs:27:5
 +   |
 +LL |     1u64 as usize;
 +   |     ^^^^^^^^^^^^^
 +
 +error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers
 +  --> $DIR/cast_size_32bit.rs:28:5
 +   |
 +LL |     1u32 as isize;
 +   |     ^^^^^^^^^^^^^
 +
 +error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
 +  --> $DIR/cast_size_32bit.rs:33:5
 +   |
 +LL |     999_999_999 as f32;
 +   |     ^^^^^^^^^^^^^^^^^^
 +
 +error: casting integer literal to `f64` is unnecessary
 +  --> $DIR/cast_size_32bit.rs:34:5
 +   |
 +LL |     3_999_999_999usize as f64;
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3_999_999_999_f64`
 +   |
 +   = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
 +
++error: aborting due to 18 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..55cc9bc99a0ede4f1421de1b01f6b4018c224f8e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++pub struct Thing;
++
++pub fn has_thing(things: &[Thing]) -> bool {
++    let is_thing_ready = |_peer: &Thing| -> bool { todo!() };
++    things.iter().find(|p| is_thing_ready(p)).is_some()
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f5038f0a84847ece291d92fd7cec1dd85acdfac6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: called `is_some()` after searching an `Iterator` with `find`
++  --> $DIR/ice-9041.rs:5:19
++   |
++LL |     things.iter().find(|p| is_thing_ready(p)).is_some()
++   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|p| is_thing_ready(&p))`
++   |
++   = note: `-D clippy::search-is-some` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 47bf25e409bd26e9a0ebc7f66da008474a2552a9,0000000000000000000000000000000000000000..687efdada6e311f4efdf629ebfd0b46ac3bfa388
mode 100644,000000..100644
--- /dev/null
@@@ -1,78 -1,0 +1,84 @@@
 +// run-rustfix
++// aux-build:macro_rules.rs
 +
 +#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)]
 +#![warn(clippy::equatable_if_let)]
 +
++#[macro_use]
++extern crate macro_rules;
++
 +use std::cmp::Ordering;
 +
 +#[derive(PartialEq)]
 +enum Enum {
 +    TupleVariant(i32, u64),
 +    RecordVariant { a: i64, b: u32 },
 +    UnitVariant,
 +    Recursive(Struct),
 +}
 +
 +#[derive(PartialEq)]
 +struct Struct {
 +    a: i32,
 +    b: bool,
 +}
 +
 +enum NotPartialEq {
 +    A,
 +    B,
 +}
 +
 +enum NotStructuralEq {
 +    A,
 +    B,
 +}
 +
 +impl PartialEq for NotStructuralEq {
 +    fn eq(&self, _: &NotStructuralEq) -> bool {
 +        false
 +    }
 +}
 +
 +fn main() {
 +    let a = 2;
 +    let b = 3;
 +    let c = Some(2);
 +    let d = Struct { a: 2, b: false };
 +    let e = Enum::UnitVariant;
 +    let f = NotPartialEq::A;
 +    let g = NotStructuralEq::A;
 +
 +    // true
 +
 +    if a == 2 {}
 +    if a.cmp(&b) == Ordering::Greater {}
 +    if c == Some(2) {}
 +    if d == (Struct { a: 2, b: false }) {}
 +    if e == Enum::TupleVariant(32, 64) {}
 +    if e == (Enum::RecordVariant { a: 64, b: 32 }) {}
 +    if e == Enum::UnitVariant {}
 +    if (e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false }) {}
 +
 +    // false
 +
 +    if let 2 | 3 = a {}
 +    if let x @ 2 = a {}
 +    if let Some(3 | 4) = c {}
 +    if let Struct { a, b: false } = d {}
 +    if let Struct { a: 2, b: x } = d {}
 +    if let NotPartialEq::A = f {}
 +    if g == NotStructuralEq::A {}
 +    if let Some(NotPartialEq::A) = Some(f) {}
 +    if Some(g) == Some(NotStructuralEq::A) {}
 +
 +    macro_rules! m1 {
 +        (x) => {
 +            "abc"
 +        };
 +    }
 +    if "abc" == m1!(x) {
 +        println!("OK");
 +    }
++
++    equatable_if_let!(a);
 +}
index d498bca2455bd971afc750e34b5b90f0202301c7,0000000000000000000000000000000000000000..8c467d14d2a9fb8028406835235a34483ec02c47
mode 100644,000000..100644
--- /dev/null
@@@ -1,78 -1,0 +1,84 @@@
 +// run-rustfix
++// aux-build:macro_rules.rs
 +
 +#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)]
 +#![warn(clippy::equatable_if_let)]
 +
++#[macro_use]
++extern crate macro_rules;
++
 +use std::cmp::Ordering;
 +
 +#[derive(PartialEq)]
 +enum Enum {
 +    TupleVariant(i32, u64),
 +    RecordVariant { a: i64, b: u32 },
 +    UnitVariant,
 +    Recursive(Struct),
 +}
 +
 +#[derive(PartialEq)]
 +struct Struct {
 +    a: i32,
 +    b: bool,
 +}
 +
 +enum NotPartialEq {
 +    A,
 +    B,
 +}
 +
 +enum NotStructuralEq {
 +    A,
 +    B,
 +}
 +
 +impl PartialEq for NotStructuralEq {
 +    fn eq(&self, _: &NotStructuralEq) -> bool {
 +        false
 +    }
 +}
 +
 +fn main() {
 +    let a = 2;
 +    let b = 3;
 +    let c = Some(2);
 +    let d = Struct { a: 2, b: false };
 +    let e = Enum::UnitVariant;
 +    let f = NotPartialEq::A;
 +    let g = NotStructuralEq::A;
 +
 +    // true
 +
 +    if let 2 = a {}
 +    if let Ordering::Greater = a.cmp(&b) {}
 +    if let Some(2) = c {}
 +    if let Struct { a: 2, b: false } = d {}
 +    if let Enum::TupleVariant(32, 64) = e {}
 +    if let Enum::RecordVariant { a: 64, b: 32 } = e {}
 +    if let Enum::UnitVariant = e {}
 +    if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
 +
 +    // false
 +
 +    if let 2 | 3 = a {}
 +    if let x @ 2 = a {}
 +    if let Some(3 | 4) = c {}
 +    if let Struct { a, b: false } = d {}
 +    if let Struct { a: 2, b: x } = d {}
 +    if let NotPartialEq::A = f {}
 +    if let NotStructuralEq::A = g {}
 +    if let Some(NotPartialEq::A) = Some(f) {}
 +    if let Some(NotStructuralEq::A) = Some(g) {}
 +
 +    macro_rules! m1 {
 +        (x) => {
 +            "abc"
 +        };
 +    }
 +    if let m1!(x) = "abc" {
 +        println!("OK");
 +    }
++
++    equatable_if_let!(a);
 +}
index 760ff88f448f094561f008b0e29be43824306260,0000000000000000000000000000000000000000..9c4c3cc3682e631d7930d7c9b265b6ebab8afed0
mode 100644,000000..100644
--- /dev/null
@@@ -1,70 -1,0 +1,70 @@@
-   --> $DIR/equatable_if_let.rs:49:8
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:50:8
++  --> $DIR/equatable_if_let.rs:53:8
 +   |
 +LL |     if let 2 = a {}
 +   |        ^^^^^^^^^ help: try: `a == 2`
 +   |
 +   = note: `-D clippy::equatable-if-let` implied by `-D warnings`
 +
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:51:8
++  --> $DIR/equatable_if_let.rs:54:8
 +   |
 +LL |     if let Ordering::Greater = a.cmp(&b) {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater`
 +
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:52:8
++  --> $DIR/equatable_if_let.rs:55:8
 +   |
 +LL |     if let Some(2) = c {}
 +   |        ^^^^^^^^^^^^^^^ help: try: `c == Some(2)`
 +
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:53:8
++  --> $DIR/equatable_if_let.rs:56:8
 +   |
 +LL |     if let Struct { a: 2, b: false } = d {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })`
 +
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:54:8
++  --> $DIR/equatable_if_let.rs:57:8
 +   |
 +LL |     if let Enum::TupleVariant(32, 64) = e {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)`
 +
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:55:8
++  --> $DIR/equatable_if_let.rs:58:8
 +   |
 +LL |     if let Enum::RecordVariant { a: 64, b: 32 } = e {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })`
 +
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:56:8
++  --> $DIR/equatable_if_let.rs:59:8
 +   |
 +LL |     if let Enum::UnitVariant = e {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant`
 +
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:66:8
++  --> $DIR/equatable_if_let.rs:60:8
 +   |
 +LL |     if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })`
 +
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:68:8
++  --> $DIR/equatable_if_let.rs:70:8
 +   |
 +LL |     if let NotStructuralEq::A = g {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A`
 +
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:75:8
++  --> $DIR/equatable_if_let.rs:72:8
 +   |
 +LL |     if let Some(NotStructuralEq::A) = Some(g) {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)`
 +
 +error: this pattern matching can be expressed using equality
++  --> $DIR/equatable_if_let.rs:79:8
 +   |
 +LL |     if let m1!(x) = "abc" {
 +   |        ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)`
 +
 +error: aborting due to 11 previous errors
 +
index d4ff1b1566dc613bd50d9c46e1f3c5ccdb4d5b64,0000000000000000000000000000000000000000..a650fdc1f897256ab98026823f52aaeec250c19e
mode 100644,000000..100644
--- /dev/null
@@@ -1,214 -1,0 +1,218 @@@
 +// run-rustfix
 +
 +#![warn(clippy::explicit_auto_deref)]
 +#![allow(
 +    dead_code,
 +    unused_braces,
 +    clippy::borrowed_box,
 +    clippy::needless_borrow,
 +    clippy::needless_return,
 +    clippy::ptr_arg,
 +    clippy::redundant_field_names,
 +    clippy::too_many_arguments,
 +    clippy::borrow_deref_ref,
 +    clippy::let_unit_value
 +)]
 +
 +trait CallableStr {
 +    type T: Fn(&str);
 +    fn callable_str(&self) -> Self::T;
 +}
 +impl CallableStr for () {
 +    type T = fn(&str);
 +    fn callable_str(&self) -> Self::T {
 +        fn f(_: &str) {}
 +        f
 +    }
 +}
 +impl CallableStr for i32 {
 +    type T = <() as CallableStr>::T;
 +    fn callable_str(&self) -> Self::T {
 +        ().callable_str()
 +    }
 +}
 +
 +trait CallableT<U: ?Sized> {
 +    type T: Fn(&U);
 +    fn callable_t(&self) -> Self::T;
 +}
 +impl<U: ?Sized> CallableT<U> for () {
 +    type T = fn(&U);
 +    fn callable_t(&self) -> Self::T {
 +        fn f<U: ?Sized>(_: &U) {}
 +        f::<U>
 +    }
 +}
 +impl<U: ?Sized> CallableT<U> for i32 {
 +    type T = <() as CallableT<U>>::T;
 +    fn callable_t(&self) -> Self::T {
 +        ().callable_t()
 +    }
 +}
 +
 +fn f_str(_: &str) {}
 +fn f_string(_: &String) {}
 +fn f_t<T>(_: T) {}
 +fn f_ref_t<T: ?Sized>(_: &T) {}
 +
 +fn f_str_t<T>(_: &str, _: T) {}
 +
 +fn f_box_t<T>(_: &Box<T>) {}
 +
 +extern "C" {
 +    fn var(_: u32, ...);
 +}
 +
 +fn main() {
 +    let s = String::new();
 +
 +    let _: &str = &s;
 +    let _ = &*s; // Don't lint. Inferred type would change.
 +    let _: &_ = &*s; // Don't lint. Inferred type would change.
 +
 +    f_str(&s);
 +    f_t(&*s); // Don't lint. Inferred type would change.
 +    f_ref_t(&*s); // Don't lint. Inferred type would change.
 +
 +    f_str_t(&s, &*s); // Don't lint second param.
 +
 +    let b = Box::new(Box::new(Box::new(5)));
 +    let _: &Box<i32> = &b;
 +    let _: &Box<_> = &**b; // Don't lint. Inferred type would change.
 +
 +    f_box_t(&**b); // Don't lint. Inferred type would change.
 +
 +    let c = |_x: &str| ();
 +    c(&s);
 +
 +    let c = |_x| ();
 +    c(&*s); // Don't lint. Inferred type would change.
 +
 +    fn _f(x: &String) -> &str {
 +        x
 +    }
 +
 +    fn _f1(x: &String) -> &str {
 +        { x }
 +    }
 +
 +    fn _f2(x: &String) -> &str {
 +        { x }
 +    }
 +
 +    fn _f3(x: &Box<Box<Box<i32>>>) -> &Box<i32> {
 +        x
 +    }
 +
 +    fn _f4(
 +        x: String,
 +        f1: impl Fn(&str),
 +        f2: &dyn Fn(&str),
 +        f3: fn(&str),
 +        f4: impl CallableStr,
 +        f5: <() as CallableStr>::T,
 +        f6: <i32 as CallableStr>::T,
 +        f7: &dyn CallableStr<T = fn(&str)>,
 +        f8: impl CallableT<str>,
 +        f9: <() as CallableT<str>>::T,
 +        f10: <i32 as CallableT<str>>::T,
 +        f11: &dyn CallableT<str, T = fn(&str)>,
 +    ) {
 +        f1(&x);
 +        f2(&x);
 +        f3(&x);
 +        f4.callable_str()(&x);
 +        f5(&x);
 +        f6(&x);
 +        f7.callable_str()(&x);
 +        f8.callable_t()(&x);
 +        f9(&x);
 +        f10(&x);
 +        f11.callable_t()(&x);
 +    }
 +
 +    struct S1<'a>(&'a str);
 +    let _ = S1(&s);
 +
 +    struct S2<'a> {
 +        s: &'a str,
 +    }
 +    let _ = S2 { s: &s };
 +
 +    struct S3<'a, T: ?Sized>(&'a T);
 +    let _ = S3(&*s); // Don't lint. Inferred type would change.
 +
 +    struct S4<'a, T: ?Sized> {
 +        s: &'a T,
 +    }
 +    let _ = S4 { s: &*s }; // Don't lint. Inferred type would change.
 +
 +    enum E1<'a> {
 +        S1(&'a str),
 +        S2 { s: &'a str },
 +    }
 +    impl<'a> E1<'a> {
 +        fn m1(s: &'a String) {
 +            let _ = Self::S1(s);
 +            let _ = Self::S2 { s: s };
 +        }
 +    }
 +    let _ = E1::S1(&s);
 +    let _ = E1::S2 { s: &s };
 +
 +    enum E2<'a, T: ?Sized> {
 +        S1(&'a T),
 +        S2 { s: &'a T },
 +    }
 +    let _ = E2::S1(&*s); // Don't lint. Inferred type would change.
 +    let _ = E2::S2 { s: &*s }; // Don't lint. Inferred type would change.
 +
 +    let ref_s = &s;
 +    let _: &String = &*ref_s; // Don't lint reborrow.
 +    f_string(&*ref_s); // Don't lint reborrow.
 +
 +    struct S5 {
 +        foo: u32,
 +    }
 +    let b = Box::new(Box::new(S5 { foo: 5 }));
 +    let _ = b.foo;
 +    let _ = b.foo;
 +    let _ = b.foo;
 +
 +    struct S6 {
 +        foo: S5,
 +    }
 +    impl core::ops::Deref for S6 {
 +        type Target = S5;
 +        fn deref(&self) -> &Self::Target {
 +            &self.foo
 +        }
 +    }
 +    let s6 = S6 { foo: S5 { foo: 5 } };
 +    let _ = (*s6).foo; // Don't lint. `S6` also has a field named `foo`
 +
 +    let ref_str = &"foo";
 +    let _ = f_str(ref_str);
 +    let ref_ref_str = &ref_str;
 +    let _ = f_str(ref_ref_str);
 +
 +    fn _f5(x: &u32) -> u32 {
 +        if true {
 +            *x
 +        } else {
 +            return *x;
 +        }
 +    }
 +
 +    f_str(&&ref_str); // `needless_borrow` will suggest removing both references
 +    f_str(&ref_str); // `needless_borrow` will suggest removing only one reference
 +
 +    let x = &&40;
 +    unsafe {
 +        var(0, &**x);
 +    }
++
++    let s = &"str";
++    let _ = || return *s;
++    let _ = || -> &'static str { return s };
 +}
index 99294a7947bf16e8d24939434a085e1384fccbf6,0000000000000000000000000000000000000000..8f4f352576a734514f2648b826205efc4eb219e9
mode 100644,000000..100644
--- /dev/null
@@@ -1,214 -1,0 +1,218 @@@
 +// run-rustfix
 +
 +#![warn(clippy::explicit_auto_deref)]
 +#![allow(
 +    dead_code,
 +    unused_braces,
 +    clippy::borrowed_box,
 +    clippy::needless_borrow,
 +    clippy::needless_return,
 +    clippy::ptr_arg,
 +    clippy::redundant_field_names,
 +    clippy::too_many_arguments,
 +    clippy::borrow_deref_ref,
 +    clippy::let_unit_value
 +)]
 +
 +trait CallableStr {
 +    type T: Fn(&str);
 +    fn callable_str(&self) -> Self::T;
 +}
 +impl CallableStr for () {
 +    type T = fn(&str);
 +    fn callable_str(&self) -> Self::T {
 +        fn f(_: &str) {}
 +        f
 +    }
 +}
 +impl CallableStr for i32 {
 +    type T = <() as CallableStr>::T;
 +    fn callable_str(&self) -> Self::T {
 +        ().callable_str()
 +    }
 +}
 +
 +trait CallableT<U: ?Sized> {
 +    type T: Fn(&U);
 +    fn callable_t(&self) -> Self::T;
 +}
 +impl<U: ?Sized> CallableT<U> for () {
 +    type T = fn(&U);
 +    fn callable_t(&self) -> Self::T {
 +        fn f<U: ?Sized>(_: &U) {}
 +        f::<U>
 +    }
 +}
 +impl<U: ?Sized> CallableT<U> for i32 {
 +    type T = <() as CallableT<U>>::T;
 +    fn callable_t(&self) -> Self::T {
 +        ().callable_t()
 +    }
 +}
 +
 +fn f_str(_: &str) {}
 +fn f_string(_: &String) {}
 +fn f_t<T>(_: T) {}
 +fn f_ref_t<T: ?Sized>(_: &T) {}
 +
 +fn f_str_t<T>(_: &str, _: T) {}
 +
 +fn f_box_t<T>(_: &Box<T>) {}
 +
 +extern "C" {
 +    fn var(_: u32, ...);
 +}
 +
 +fn main() {
 +    let s = String::new();
 +
 +    let _: &str = &*s;
 +    let _ = &*s; // Don't lint. Inferred type would change.
 +    let _: &_ = &*s; // Don't lint. Inferred type would change.
 +
 +    f_str(&*s);
 +    f_t(&*s); // Don't lint. Inferred type would change.
 +    f_ref_t(&*s); // Don't lint. Inferred type would change.
 +
 +    f_str_t(&*s, &*s); // Don't lint second param.
 +
 +    let b = Box::new(Box::new(Box::new(5)));
 +    let _: &Box<i32> = &**b;
 +    let _: &Box<_> = &**b; // Don't lint. Inferred type would change.
 +
 +    f_box_t(&**b); // Don't lint. Inferred type would change.
 +
 +    let c = |_x: &str| ();
 +    c(&*s);
 +
 +    let c = |_x| ();
 +    c(&*s); // Don't lint. Inferred type would change.
 +
 +    fn _f(x: &String) -> &str {
 +        &**x
 +    }
 +
 +    fn _f1(x: &String) -> &str {
 +        { &**x }
 +    }
 +
 +    fn _f2(x: &String) -> &str {
 +        &**{ x }
 +    }
 +
 +    fn _f3(x: &Box<Box<Box<i32>>>) -> &Box<i32> {
 +        &***x
 +    }
 +
 +    fn _f4(
 +        x: String,
 +        f1: impl Fn(&str),
 +        f2: &dyn Fn(&str),
 +        f3: fn(&str),
 +        f4: impl CallableStr,
 +        f5: <() as CallableStr>::T,
 +        f6: <i32 as CallableStr>::T,
 +        f7: &dyn CallableStr<T = fn(&str)>,
 +        f8: impl CallableT<str>,
 +        f9: <() as CallableT<str>>::T,
 +        f10: <i32 as CallableT<str>>::T,
 +        f11: &dyn CallableT<str, T = fn(&str)>,
 +    ) {
 +        f1(&*x);
 +        f2(&*x);
 +        f3(&*x);
 +        f4.callable_str()(&*x);
 +        f5(&*x);
 +        f6(&*x);
 +        f7.callable_str()(&*x);
 +        f8.callable_t()(&*x);
 +        f9(&*x);
 +        f10(&*x);
 +        f11.callable_t()(&*x);
 +    }
 +
 +    struct S1<'a>(&'a str);
 +    let _ = S1(&*s);
 +
 +    struct S2<'a> {
 +        s: &'a str,
 +    }
 +    let _ = S2 { s: &*s };
 +
 +    struct S3<'a, T: ?Sized>(&'a T);
 +    let _ = S3(&*s); // Don't lint. Inferred type would change.
 +
 +    struct S4<'a, T: ?Sized> {
 +        s: &'a T,
 +    }
 +    let _ = S4 { s: &*s }; // Don't lint. Inferred type would change.
 +
 +    enum E1<'a> {
 +        S1(&'a str),
 +        S2 { s: &'a str },
 +    }
 +    impl<'a> E1<'a> {
 +        fn m1(s: &'a String) {
 +            let _ = Self::S1(&**s);
 +            let _ = Self::S2 { s: &**s };
 +        }
 +    }
 +    let _ = E1::S1(&*s);
 +    let _ = E1::S2 { s: &*s };
 +
 +    enum E2<'a, T: ?Sized> {
 +        S1(&'a T),
 +        S2 { s: &'a T },
 +    }
 +    let _ = E2::S1(&*s); // Don't lint. Inferred type would change.
 +    let _ = E2::S2 { s: &*s }; // Don't lint. Inferred type would change.
 +
 +    let ref_s = &s;
 +    let _: &String = &*ref_s; // Don't lint reborrow.
 +    f_string(&*ref_s); // Don't lint reborrow.
 +
 +    struct S5 {
 +        foo: u32,
 +    }
 +    let b = Box::new(Box::new(S5 { foo: 5 }));
 +    let _ = b.foo;
 +    let _ = (*b).foo;
 +    let _ = (**b).foo;
 +
 +    struct S6 {
 +        foo: S5,
 +    }
 +    impl core::ops::Deref for S6 {
 +        type Target = S5;
 +        fn deref(&self) -> &Self::Target {
 +            &self.foo
 +        }
 +    }
 +    let s6 = S6 { foo: S5 { foo: 5 } };
 +    let _ = (*s6).foo; // Don't lint. `S6` also has a field named `foo`
 +
 +    let ref_str = &"foo";
 +    let _ = f_str(*ref_str);
 +    let ref_ref_str = &ref_str;
 +    let _ = f_str(**ref_ref_str);
 +
 +    fn _f5(x: &u32) -> u32 {
 +        if true {
 +            *x
 +        } else {
 +            return *x;
 +        }
 +    }
 +
 +    f_str(&&*ref_str); // `needless_borrow` will suggest removing both references
 +    f_str(&&**ref_str); // `needless_borrow` will suggest removing only one reference
 +
 +    let x = &&40;
 +    unsafe {
 +        var(0, &**x);
 +    }
++
++    let s = &"str";
++    let _ = || return *s;
++    let _ = || -> &'static str { return *s };
 +}
index 55f956e37aed77fb51d97e222658614a9e2ccff6,0000000000000000000000000000000000000000..92765307ea73d55f30a30ecdbc984ba42a645066
mode 100644,000000..100644
--- /dev/null
@@@ -1,196 -1,0 +1,202 @@@
- error: aborting due to 32 previous errors
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:69:20
 +   |
 +LL |     let _: &str = &*s;
 +   |                    ^^ help: try this: `s`
 +   |
 +   = note: `-D clippy::explicit-auto-deref` implied by `-D warnings`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:73:12
 +   |
 +LL |     f_str(&*s);
 +   |            ^^ help: try this: `s`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:77:14
 +   |
 +LL |     f_str_t(&*s, &*s); // Don't lint second param.
 +   |              ^^ help: try this: `s`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:80:25
 +   |
 +LL |     let _: &Box<i32> = &**b;
 +   |                         ^^^ help: try this: `b`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:86:8
 +   |
 +LL |     c(&*s);
 +   |        ^^ help: try this: `s`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:92:9
 +   |
 +LL |         &**x
 +   |         ^^^^ help: try this: `x`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:96:11
 +   |
 +LL |         { &**x }
 +   |           ^^^^ help: try this: `x`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:100:9
 +   |
 +LL |         &**{ x }
 +   |         ^^^^^^^^ help: try this: `{ x }`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:104:9
 +   |
 +LL |         &***x
 +   |         ^^^^^ help: try this: `x`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:121:13
 +   |
 +LL |         f1(&*x);
 +   |             ^^ help: try this: `x`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:122:13
 +   |
 +LL |         f2(&*x);
 +   |             ^^ help: try this: `x`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:123:13
 +   |
 +LL |         f3(&*x);
 +   |             ^^ help: try this: `x`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:124:28
 +   |
 +LL |         f4.callable_str()(&*x);
 +   |                            ^^ help: try this: `x`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:125:13
 +   |
 +LL |         f5(&*x);
 +   |             ^^ help: try this: `x`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:126:13
 +   |
 +LL |         f6(&*x);
 +   |             ^^ help: try this: `x`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:127:28
 +   |
 +LL |         f7.callable_str()(&*x);
 +   |                            ^^ help: try this: `x`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:128:26
 +   |
 +LL |         f8.callable_t()(&*x);
 +   |                          ^^ help: try this: `x`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:129:13
 +   |
 +LL |         f9(&*x);
 +   |             ^^ help: try this: `x`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:130:14
 +   |
 +LL |         f10(&*x);
 +   |              ^^ help: try this: `x`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:131:27
 +   |
 +LL |         f11.callable_t()(&*x);
 +   |                           ^^ help: try this: `x`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:135:17
 +   |
 +LL |     let _ = S1(&*s);
 +   |                 ^^ help: try this: `s`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:140:22
 +   |
 +LL |     let _ = S2 { s: &*s };
 +   |                      ^^ help: try this: `s`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:156:30
 +   |
 +LL |             let _ = Self::S1(&**s);
 +   |                              ^^^^ help: try this: `s`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:157:35
 +   |
 +LL |             let _ = Self::S2 { s: &**s };
 +   |                                   ^^^^ help: try this: `s`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:160:21
 +   |
 +LL |     let _ = E1::S1(&*s);
 +   |                     ^^ help: try this: `s`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:161:26
 +   |
 +LL |     let _ = E1::S2 { s: &*s };
 +   |                          ^^ help: try this: `s`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:179:13
 +   |
 +LL |     let _ = (*b).foo;
 +   |             ^^^^ help: try this: `b`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:180:13
 +   |
 +LL |     let _ = (**b).foo;
 +   |             ^^^^^ help: try this: `b`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:195:19
 +   |
 +LL |     let _ = f_str(*ref_str);
 +   |                   ^^^^^^^^ help: try this: `ref_str`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:197:19
 +   |
 +LL |     let _ = f_str(**ref_ref_str);
 +   |                   ^^^^^^^^^^^^^ help: try this: `ref_ref_str`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:207:13
 +   |
 +LL |     f_str(&&*ref_str); // `needless_borrow` will suggest removing both references
 +   |             ^^^^^^^^ help: try this: `ref_str`
 +
 +error: deref which would be done by auto-deref
 +  --> $DIR/explicit_auto_deref.rs:208:12
 +   |
 +LL |     f_str(&&**ref_str); // `needless_borrow` will suggest removing only one reference
 +   |            ^^^^^^^^^^ help: try this: `ref_str`
 +
++error: deref which would be done by auto-deref
++  --> $DIR/explicit_auto_deref.rs:217:41
++   |
++LL |     let _ = || -> &'static str { return *s };
++   |                                         ^^ help: try this: `s`
++
++error: aborting due to 33 previous errors
 +
index 58feae422a3c121f3d89cc14278147037191a99a,0000000000000000000000000000000000000000..6cbfafbb38b9913e7ec71de26f6206fce73ae1cf
mode 100644,000000..100644
--- /dev/null
@@@ -1,42 -1,0 +1,42 @@@
- // When mutexs are different don't warn
 +#![warn(clippy::if_let_mutex)]
 +
 +use std::ops::Deref;
 +use std::sync::Mutex;
 +
 +fn do_stuff<T>(_: T) {}
 +
 +fn if_let() {
 +    let m = Mutex::new(1_u8);
 +    if let Err(locked) = m.lock() {
 +        do_stuff(locked);
 +    } else {
 +        let lock = m.lock().unwrap();
 +        do_stuff(lock);
 +    };
 +}
 +
 +// This is the most common case as the above case is pretty
 +// contrived.
 +fn if_let_option() {
 +    let m = Mutex::new(Some(0_u8));
 +    if let Some(locked) = m.lock().unwrap().deref() {
 +        do_stuff(locked);
 +    } else {
 +        let lock = m.lock().unwrap();
 +        do_stuff(lock);
 +    };
 +}
 +
++// When mutexes are different don't warn
 +fn if_let_different_mutex() {
 +    let m = Mutex::new(Some(0_u8));
 +    let other = Mutex::new(None::<u8>);
 +    if let Some(locked) = m.lock().unwrap().deref() {
 +        do_stuff(locked);
 +    } else {
 +        let lock = other.lock().unwrap();
 +        do_stuff(lock);
 +    };
 +}
 +
 +fn main() {}
index eb66d1afddce39dfce67d421a7229232b52b2b6f,0000000000000000000000000000000000000000..74ba2f1c5e70bd09c67e02c39d2c52a076bc8164
mode 100644,000000..100644
--- /dev/null
@@@ -1,73 -1,0 +1,73 @@@
-         // Shoule NOT lint because the order is the same as in the definition.
 +// run-rustfix
 +#![warn(clippy::inconsistent_struct_constructor)]
 +#![allow(clippy::redundant_field_names)]
 +#![allow(clippy::unnecessary_operation)]
 +#![allow(clippy::no_effect)]
 +#![allow(dead_code)]
 +
 +#[derive(Default)]
 +struct Foo {
 +    x: i32,
 +    y: i32,
 +    z: i32,
 +}
 +
 +macro_rules! new_foo {
 +    () => {
 +        let x = 1;
 +        let y = 1;
 +        let z = 1;
 +        Foo { y, x, z }
 +    };
 +}
 +
 +mod without_base {
 +    use super::Foo;
 +
 +    fn test() {
 +        let x = 1;
 +        let y = 1;
 +        let z = 1;
 +
 +        // Should lint.
 +        Foo { x, y, z };
 +
 +        // Should NOT lint.
 +        // issue #7069.
 +        new_foo!();
 +
++        // Should NOT lint because the order is the same as in the definition.
 +        Foo { x, y, z };
 +
 +        // Should NOT lint because z is not a shorthand init.
 +        Foo { y, x, z: z };
 +    }
 +}
 +
 +mod with_base {
 +    use super::Foo;
 +
 +    fn test() {
 +        let x = 1;
 +        let z = 1;
 +
 +        // Should lint.
 +        Foo { x, z, ..Default::default() };
 +
 +        // Should NOT lint because the order is consistent with the definition.
 +        Foo {
 +            x,
 +            z,
 +            ..Default::default()
 +        };
 +
 +        // Should NOT lint because z is not a shorthand init.
 +        Foo {
 +            z: z,
 +            x,
 +            ..Default::default()
 +        };
 +    }
 +}
 +
 +fn main() {}
index 5caadc7c62083fb2b214df04ded3b16e2bb665f7,0000000000000000000000000000000000000000..ba96e1e330f5fc62f3e408036a3897d2d6d28a24
mode 100644,000000..100644
--- /dev/null
@@@ -1,77 -1,0 +1,77 @@@
-         // Shoule NOT lint because the order is the same as in the definition.
 +// run-rustfix
 +#![warn(clippy::inconsistent_struct_constructor)]
 +#![allow(clippy::redundant_field_names)]
 +#![allow(clippy::unnecessary_operation)]
 +#![allow(clippy::no_effect)]
 +#![allow(dead_code)]
 +
 +#[derive(Default)]
 +struct Foo {
 +    x: i32,
 +    y: i32,
 +    z: i32,
 +}
 +
 +macro_rules! new_foo {
 +    () => {
 +        let x = 1;
 +        let y = 1;
 +        let z = 1;
 +        Foo { y, x, z }
 +    };
 +}
 +
 +mod without_base {
 +    use super::Foo;
 +
 +    fn test() {
 +        let x = 1;
 +        let y = 1;
 +        let z = 1;
 +
 +        // Should lint.
 +        Foo { y, x, z };
 +
 +        // Should NOT lint.
 +        // issue #7069.
 +        new_foo!();
 +
++        // Should NOT lint because the order is the same as in the definition.
 +        Foo { x, y, z };
 +
 +        // Should NOT lint because z is not a shorthand init.
 +        Foo { y, x, z: z };
 +    }
 +}
 +
 +mod with_base {
 +    use super::Foo;
 +
 +    fn test() {
 +        let x = 1;
 +        let z = 1;
 +
 +        // Should lint.
 +        Foo {
 +            z,
 +            x,
 +            ..Default::default()
 +        };
 +
 +        // Should NOT lint because the order is consistent with the definition.
 +        Foo {
 +            x,
 +            z,
 +            ..Default::default()
 +        };
 +
 +        // Should NOT lint because z is not a shorthand init.
 +        Foo {
 +            z: z,
 +            x,
 +            ..Default::default()
 +        };
 +    }
 +}
 +
 +fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3dc096d3197fbe5eba20a5ca73b5a65d72739c7b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++#![warn(clippy::invalid_utf8_in_unchecked)]
++
++fn main() {
++    // Valid
++    unsafe {
++        std::str::from_utf8_unchecked(&[99, 108, 105, 112, 112, 121]);
++        std::str::from_utf8_unchecked(&[b'c', b'l', b'i', b'p', b'p', b'y']);
++        std::str::from_utf8_unchecked(b"clippy");
++
++        let x = 0xA0;
++        std::str::from_utf8_unchecked(&[0xC0, x]);
++    }
++
++    // Invalid
++    unsafe {
++        std::str::from_utf8_unchecked(&[99, 108, 130, 105, 112, 112, 121]);
++        std::str::from_utf8_unchecked(&[b'c', b'l', b'\x82', b'i', b'p', b'p', b'y']);
++        std::str::from_utf8_unchecked(b"cl\x82ippy");
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c89cd2758ee9fe407fb359ed45f6232082fd400b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: non UTF-8 literal in `std::str::from_utf8_unchecked`
++  --> $DIR/invalid_utf8_in_unchecked.rs:16:9
++   |
++LL |         std::str::from_utf8_unchecked(&[99, 108, 130, 105, 112, 112, 121]);
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::invalid-utf8-in-unchecked` implied by `-D warnings`
++
++error: non UTF-8 literal in `std::str::from_utf8_unchecked`
++  --> $DIR/invalid_utf8_in_unchecked.rs:17:9
++   |
++LL |         std::str::from_utf8_unchecked(&[b'c', b'l', b'/x82', b'i', b'p', b'p', b'y']);
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: non UTF-8 literal in `std::str::from_utf8_unchecked`
++  --> $DIR/invalid_utf8_in_unchecked.rs:18:9
++   |
++LL |         std::str::from_utf8_unchecked(b"cl/x82ippy");
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
index e72b746232551b87861e2783cf5ce3d7d76dfd21,0000000000000000000000000000000000000000..6343cff0f7ff941e2c55b0e762b41186c6429048
mode 100644,000000..100644
--- /dev/null
@@@ -1,115 -1,0 +1,177 @@@
- #![allow(clippy::no_effect)]
- #![allow(unused_variables)]
 +// run-rustfix
 +
++#![feature(lint_reasons)]
 +#![warn(clippy::let_unit_value)]
-     fn f4<T>(mut x: Vec<T>) -> T {
-         x.pop().unwrap()
++#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)]
 +
 +macro_rules! let_and_return {
 +    ($n:expr) => {{
 +        let ret = $n;
 +    }};
 +}
 +
 +fn main() {
 +    println!("x");
 +    let _y = 1; // this is fine
 +    let _z = ((), 1); // this as well
 +    if true {
 +        ();
 +    }
 +
 +    consume_units_with_for_loop(); // should be fine as well
 +
 +    multiline_sugg();
 +
 +    let_and_return!(()) // should be fine
 +}
 +
 +// Related to issue #1964
 +fn consume_units_with_for_loop() {
 +    // `for_let_unit` lint should not be triggered by consuming them using for loop.
 +    let v = vec![(), (), ()];
 +    let mut count = 0;
 +    for _ in v {
 +        count += 1;
 +    }
 +    assert_eq!(count, 3);
 +
 +    // Same for consuming from some other Iterator<Item = ()>.
 +    let (tx, rx) = ::std::sync::mpsc::channel();
 +    tx.send(()).unwrap();
 +    drop(tx);
 +
 +    count = 0;
 +    for _ in rx.iter() {
 +        count += 1;
 +    }
 +    assert_eq!(count, 1);
 +}
 +
 +fn multiline_sugg() {
 +    let v: Vec<u8> = vec![2];
 +
 +    v
 +        .into_iter()
 +        .map(|i| i * 2)
 +        .filter(|i| i % 2 == 0)
 +        .map(|_| ())
 +        .next()
 +        .unwrap();
 +}
 +
 +#[derive(Copy, Clone)]
 +pub struct ContainsUnit(()); // should be fine
 +
 +fn _returns_generic() {
 +    fn f<T>() -> T {
 +        unimplemented!()
 +    }
 +    fn f2<T, U>(_: T) -> U {
 +        unimplemented!()
 +    }
 +    fn f3<T>(x: T) -> T {
 +        x
 +    }
-     f4(vec![()]); // Lint
-     f4(vec![()]); // Lint
++    fn f5<T: Default>(x: bool) -> Option<T> {
++        x.then(|| T::default())
 +    }
 +
 +    let _: () = f(); // Ok
 +    let _: () = f(); // Lint.
 +
 +    let _: () = f2(0i32); // Ok
 +    let _: () = f2(0i32); // Lint.
 +
 +    f3(()); // Lint
 +    f3(()); // Lint
 +
++    // Should lint:
++    // fn f4<T>(mut x: Vec<T>) -> T {
++    //    x.pop().unwrap()
++    // }
++    // let _: () = f4(vec![()]);
++    // let x: () = f4(vec![()]);
 +
 +    // Ok
 +    let _: () = {
 +        let x = 5;
 +        f2(x)
 +    };
 +
 +    let _: () = if true { f() } else { f2(0) }; // Ok
 +    let _: () = if true { f() } else { f2(0) }; // Lint
 +
 +    // Ok
 +    let _: () = match Some(0) {
 +        None => f2(1),
 +        Some(0) => f(),
 +        Some(1) => f2(3),
 +        Some(_) => f2('x'),
 +    };
 +
 +    // Lint
 +    match Some(0) {
 +        None => f2(1),
 +        Some(0) => f(),
 +        Some(1) => f2(3),
 +        Some(_) => (),
 +    };
++
++    let _: () = f5(true).unwrap();
++
++    #[allow(clippy::let_unit_value)]
++    {
++        let x = f();
++        let y;
++        let z;
++        match 0 {
++            0 => {
++                y = f();
++                z = f();
++            },
++            1 => {
++                println!("test");
++                y = f();
++                z = f3(());
++            },
++            _ => panic!(),
++        }
++
++        let x1;
++        let x2;
++        if true {
++            x1 = f();
++            x2 = x1;
++        } else {
++            x2 = f();
++            x1 = x2;
++        }
++
++        let opt;
++        match f5(true) {
++            Some(x) => opt = x,
++            None => panic!(),
++        };
++
++        #[warn(clippy::let_unit_value)]
++        {
++            let _: () = x;
++            let _: () = y;
++            z;
++            let _: () = x1;
++            let _: () = x2;
++            let _: () = opt;
++        }
++    }
++
++    let () = f();
++}
++
++fn attributes() {
++    fn f() {}
++
++    #[allow(clippy::let_unit_value)]
++    let _ = f();
++    #[expect(clippy::let_unit_value)]
++    let _ = f();
 +}
index 47ee0a76724792445b8da0bd6465355b43fafb73,0000000000000000000000000000000000000000..c9bb2849f5cf138f1f944182328d4d000a4957e8
mode 100644,000000..100644
--- /dev/null
@@@ -1,115 -1,0 +1,177 @@@
- #![allow(clippy::no_effect)]
- #![allow(unused_variables)]
 +// run-rustfix
 +
++#![feature(lint_reasons)]
 +#![warn(clippy::let_unit_value)]
-     fn f4<T>(mut x: Vec<T>) -> T {
-         x.pop().unwrap()
++#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)]
 +
 +macro_rules! let_and_return {
 +    ($n:expr) => {{
 +        let ret = $n;
 +    }};
 +}
 +
 +fn main() {
 +    let _x = println!("x");
 +    let _y = 1; // this is fine
 +    let _z = ((), 1); // this as well
 +    if true {
 +        let _a = ();
 +    }
 +
 +    consume_units_with_for_loop(); // should be fine as well
 +
 +    multiline_sugg();
 +
 +    let_and_return!(()) // should be fine
 +}
 +
 +// Related to issue #1964
 +fn consume_units_with_for_loop() {
 +    // `for_let_unit` lint should not be triggered by consuming them using for loop.
 +    let v = vec![(), (), ()];
 +    let mut count = 0;
 +    for _ in v {
 +        count += 1;
 +    }
 +    assert_eq!(count, 3);
 +
 +    // Same for consuming from some other Iterator<Item = ()>.
 +    let (tx, rx) = ::std::sync::mpsc::channel();
 +    tx.send(()).unwrap();
 +    drop(tx);
 +
 +    count = 0;
 +    for _ in rx.iter() {
 +        count += 1;
 +    }
 +    assert_eq!(count, 1);
 +}
 +
 +fn multiline_sugg() {
 +    let v: Vec<u8> = vec![2];
 +
 +    let _ = v
 +        .into_iter()
 +        .map(|i| i * 2)
 +        .filter(|i| i % 2 == 0)
 +        .map(|_| ())
 +        .next()
 +        .unwrap();
 +}
 +
 +#[derive(Copy, Clone)]
 +pub struct ContainsUnit(()); // should be fine
 +
 +fn _returns_generic() {
 +    fn f<T>() -> T {
 +        unimplemented!()
 +    }
 +    fn f2<T, U>(_: T) -> U {
 +        unimplemented!()
 +    }
 +    fn f3<T>(x: T) -> T {
 +        x
 +    }
-     let _: () = f4(vec![()]); // Lint
-     let x: () = f4(vec![()]); // Lint
++    fn f5<T: Default>(x: bool) -> Option<T> {
++        x.then(|| T::default())
 +    }
 +
 +    let _: () = f(); // Ok
 +    let x: () = f(); // Lint.
 +
 +    let _: () = f2(0i32); // Ok
 +    let x: () = f2(0i32); // Lint.
 +
 +    let _: () = f3(()); // Lint
 +    let x: () = f3(()); // Lint
 +
++    // Should lint:
++    // fn f4<T>(mut x: Vec<T>) -> T {
++    //    x.pop().unwrap()
++    // }
++    // let _: () = f4(vec![()]);
++    // let x: () = f4(vec![()]);
 +
 +    // Ok
 +    let _: () = {
 +        let x = 5;
 +        f2(x)
 +    };
 +
 +    let _: () = if true { f() } else { f2(0) }; // Ok
 +    let x: () = if true { f() } else { f2(0) }; // Lint
 +
 +    // Ok
 +    let _: () = match Some(0) {
 +        None => f2(1),
 +        Some(0) => f(),
 +        Some(1) => f2(3),
 +        Some(_) => f2('x'),
 +    };
 +
 +    // Lint
 +    let _: () = match Some(0) {
 +        None => f2(1),
 +        Some(0) => f(),
 +        Some(1) => f2(3),
 +        Some(_) => (),
 +    };
++
++    let _: () = f5(true).unwrap();
++
++    #[allow(clippy::let_unit_value)]
++    {
++        let x = f();
++        let y;
++        let z;
++        match 0 {
++            0 => {
++                y = f();
++                z = f();
++            },
++            1 => {
++                println!("test");
++                y = f();
++                z = f3(());
++            },
++            _ => panic!(),
++        }
++
++        let x1;
++        let x2;
++        if true {
++            x1 = f();
++            x2 = x1;
++        } else {
++            x2 = f();
++            x1 = x2;
++        }
++
++        let opt;
++        match f5(true) {
++            Some(x) => opt = x,
++            None => panic!(),
++        };
++
++        #[warn(clippy::let_unit_value)]
++        {
++            let _: () = x;
++            let _: () = y;
++            let _: () = z;
++            let _: () = x1;
++            let _: () = x2;
++            let _: () = opt;
++        }
++    }
++
++    let () = f();
++}
++
++fn attributes() {
++    fn f() {}
++
++    #[allow(clippy::let_unit_value)]
++    let _ = f();
++    #[expect(clippy::let_unit_value)]
++    let _ = f();
 +}
index 45bf67acdb7361b20bea1636c0c8d3477eb8d3c3,0000000000000000000000000000000000000000..49da74ca7e1c7b4d86e2ee0efce674cddae2025b
mode 100644,000000..100644
--- /dev/null
@@@ -1,108 -1,0 +1,102 @@@
-   --> $DIR/let_unit.rs:88:5
-    |
- LL |     let _: () = f4(vec![()]); // Lint
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);`
- error: this let-binding has unit value
-   --> $DIR/let_unit.rs:89:5
-    |
- LL |     let x: () = f4(vec![()]); // Lint
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);`
- error: this let-binding has unit value
-   --> $DIR/let_unit.rs:98:5
 +error: this let-binding has unit value
 +  --> $DIR/let_unit.rs:14:5
 +   |
 +LL |     let _x = println!("x");
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `println!("x");`
 +   |
 +   = note: `-D clippy::let-unit-value` implied by `-D warnings`
 +
 +error: this let-binding has unit value
 +  --> $DIR/let_unit.rs:18:9
 +   |
 +LL |         let _a = ();
 +   |         ^^^^^^^^^^^^ help: omit the `let` binding: `();`
 +
 +error: this let-binding has unit value
 +  --> $DIR/let_unit.rs:53:5
 +   |
 +LL | /     let _ = v
 +LL | |         .into_iter()
 +LL | |         .map(|i| i * 2)
 +LL | |         .filter(|i| i % 2 == 0)
 +LL | |         .map(|_| ())
 +LL | |         .next()
 +LL | |         .unwrap();
 +   | |__________________^
 +   |
 +help: omit the `let` binding
 +   |
 +LL ~     v
 +LL +         .into_iter()
 +LL +         .map(|i| i * 2)
 +LL +         .filter(|i| i % 2 == 0)
 +LL +         .map(|_| ())
 +LL +         .next()
 +LL +         .unwrap();
 +   |
 +
 +error: this let-binding has unit value
 +  --> $DIR/let_unit.rs:80:5
 +   |
 +LL |     let x: () = f(); // Lint.
 +   |     ^^^^-^^^^^^^^^^^
 +   |         |
 +   |         help: use a wild (`_`) binding: `_`
 +
 +error: this let-binding has unit value
 +  --> $DIR/let_unit.rs:83:5
 +   |
 +LL |     let x: () = f2(0i32); // Lint.
 +   |     ^^^^-^^^^^^^^^^^^^^^^
 +   |         |
 +   |         help: use a wild (`_`) binding: `_`
 +
 +error: this let-binding has unit value
 +  --> $DIR/let_unit.rs:85:5
 +   |
 +LL |     let _: () = f3(()); // Lint
 +   |     ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());`
 +
 +error: this let-binding has unit value
 +  --> $DIR/let_unit.rs:86:5
 +   |
 +LL |     let x: () = f3(()); // Lint
 +   |     ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());`
 +
 +error: this let-binding has unit value
-   --> $DIR/let_unit.rs:109:5
++  --> $DIR/let_unit.rs:102:5
 +   |
 +LL |     let x: () = if true { f() } else { f2(0) }; // Lint
 +   |     ^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |         |
 +   |         help: use a wild (`_`) binding: `_`
 +
 +error: this let-binding has unit value
- error: aborting due to 11 previous errors
++  --> $DIR/let_unit.rs:113:5
 +   |
 +LL | /     let _: () = match Some(0) {
 +LL | |         None => f2(1),
 +LL | |         Some(0) => f(),
 +LL | |         Some(1) => f2(3),
 +LL | |         Some(_) => (),
 +LL | |     };
 +   | |______^
 +   |
 +help: omit the `let` binding
 +   |
 +LL ~     match Some(0) {
 +LL +         None => f2(1),
 +LL +         Some(0) => f(),
 +LL +         Some(1) => f2(3),
 +LL +         Some(_) => (),
 +LL +     };
 +   |
 +
++error: this let-binding has unit value
++  --> $DIR/let_unit.rs:160:13
++   |
++LL |             let _: () = z;
++   |             ^^^^^^^^^^^^^^ help: omit the `let` binding: `z;`
++
++error: aborting due to 10 previous errors
 +
index de0d86148899d4c7be1220c597291729cec6de4a,0000000000000000000000000000000000000000..4936dc9b2e0178885f4a160739fa33ddab95ccdb
mode 100644,000000..100644
--- /dev/null
@@@ -1,87 -1,0 +1,121 @@@
 +// run-rustfix
 +#![allow(dead_code)]
 +#![warn(clippy::manual_filter_map)]
 +#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
 +
 +fn main() {
 +    // is_some(), unwrap()
 +    let _ = (0..).filter_map(|a| to_opt(a));
 +
 +    // ref pattern, expect()
 +    let _ = (0..).filter_map(|a| to_opt(a));
 +
 +    // is_ok(), unwrap_or()
 +    let _ = (0..).filter_map(|a| to_res(a).ok());
++
++    let _ = (1..5)
++        .filter_map(|y| *to_ref(to_opt(y)));
++    let _ = (1..5)
++        .filter_map(|y| *to_ref(to_opt(y)));
++
++    let _ = (1..5)
++        .filter_map(|y| to_ref(to_res(y)).ok());
++    let _ = (1..5)
++        .filter_map(|y| to_ref(to_res(y)).ok());
++}
++
++#[rustfmt::skip]
++fn simple_equal() {
++    iter::<Option<&u8>>().find_map(|x| x.cloned());
++    iter::<&Option<&u8>>().find_map(|x| x.cloned());
++    iter::<&Option<String>>().find_map(|x| x.as_deref());
++    iter::<Option<&String>>().find_map(|y| to_ref(y).cloned());
++
++    iter::<Result<u8, ()>>().find_map(|x| x.ok());
++    iter::<&Result<u8, ()>>().find_map(|x| x.ok());
++    iter::<&&Result<u8, ()>>().find_map(|x| x.ok());
++    iter::<Result<&u8, ()>>().find_map(|x| x.cloned().ok());
++    iter::<&Result<&u8, ()>>().find_map(|x| x.cloned().ok());
++    iter::<&Result<String, ()>>().find_map(|x| x.as_deref().ok());
++    iter::<Result<&String, ()>>().find_map(|y| to_ref(y).cloned().ok());
 +}
 +
 +fn no_lint() {
 +    // no shared code
 +    let _ = (0..).filter(|n| *n > 1).map(|n| n + 1);
 +
 +    // very close but different since filter() provides a reference
 +    let _ = (0..).filter(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap());
 +
 +    // similar but different
 +    let _ = (0..).filter(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap());
 +    let _ = (0..)
 +        .filter(|n| to_opt(n).map(|n| n + 1).is_some())
 +        .map(|a| to_opt(a).unwrap());
 +}
 +
++fn iter<T>() -> impl Iterator<Item = T> {
++    std::iter::empty()
++}
++
 +fn to_opt<T>(_: T) -> Option<T> {
 +    unimplemented!()
 +}
 +
 +fn to_res<T>(_: T) -> Result<T, ()> {
 +    unimplemented!()
 +}
 +
++fn to_ref<'a, T>(_: T) -> &'a T {
++    unimplemented!()
++}
++
 +struct Issue8920<'a> {
 +    option_field: Option<String>,
 +    result_field: Result<String, ()>,
 +    ref_field: Option<&'a usize>,
 +}
 +
 +fn issue_8920() {
 +    let mut vec = vec![Issue8920 {
 +        option_field: Some(String::from("str")),
 +        result_field: Ok(String::from("str")),
 +        ref_field: Some(&1),
 +    }];
 +
 +    let _ = vec
 +        .iter()
 +        .filter_map(|f| f.option_field.clone());
 +
 +    let _ = vec
 +        .iter()
 +        .filter_map(|f| f.ref_field.cloned());
 +
 +    let _ = vec
 +        .iter()
 +        .filter_map(|f| f.ref_field.copied());
 +
 +    let _ = vec
 +        .iter()
 +        .filter_map(|f| f.result_field.clone().ok());
 +
 +    let _ = vec
 +        .iter()
 +        .filter_map(|f| f.result_field.as_ref().ok());
 +
 +    let _ = vec
 +        .iter()
 +        .filter_map(|f| f.result_field.as_deref().ok());
 +
 +    let _ = vec
 +        .iter_mut()
 +        .filter_map(|f| f.result_field.as_mut().ok());
 +
 +    let _ = vec
 +        .iter_mut()
 +        .filter_map(|f| f.result_field.as_deref_mut().ok());
 +
 +    let _ = vec
 +        .iter()
 +        .filter_map(|f| f.result_field.to_owned().ok());
 +}
index bd6516f038b29f90e986931fea796df0c45d2ad1,0000000000000000000000000000000000000000..8c67e827b4c35fe6512b8806ddea1bdaff5880d4
mode 100644,000000..100644
--- /dev/null
@@@ -1,96 -1,0 +1,134 @@@
 +// run-rustfix
 +#![allow(dead_code)]
 +#![warn(clippy::manual_filter_map)]
 +#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
 +
 +fn main() {
 +    // is_some(), unwrap()
 +    let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
 +
 +    // ref pattern, expect()
 +    let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
 +
 +    // is_ok(), unwrap_or()
 +    let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
++
++    let _ = (1..5)
++        .filter(|&x| to_ref(to_opt(x)).is_some())
++        .map(|y| to_ref(to_opt(y)).unwrap());
++    let _ = (1..5)
++        .filter(|x| to_ref(to_opt(*x)).is_some())
++        .map(|y| to_ref(to_opt(y)).unwrap());
++
++    let _ = (1..5)
++        .filter(|&x| to_ref(to_res(x)).is_ok())
++        .map(|y| to_ref(to_res(y)).unwrap());
++    let _ = (1..5)
++        .filter(|x| to_ref(to_res(*x)).is_ok())
++        .map(|y| to_ref(to_res(y)).unwrap());
++}
++
++#[rustfmt::skip]
++fn simple_equal() {
++    iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
++    iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
++    iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
++    iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
++
++    iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
++    iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
++    iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
++    iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
++    iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
++    iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
++    iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
 +}
 +
 +fn no_lint() {
 +    // no shared code
 +    let _ = (0..).filter(|n| *n > 1).map(|n| n + 1);
 +
 +    // very close but different since filter() provides a reference
 +    let _ = (0..).filter(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap());
 +
 +    // similar but different
 +    let _ = (0..).filter(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap());
 +    let _ = (0..)
 +        .filter(|n| to_opt(n).map(|n| n + 1).is_some())
 +        .map(|a| to_opt(a).unwrap());
 +}
 +
++fn iter<T>() -> impl Iterator<Item = T> {
++    std::iter::empty()
++}
++
 +fn to_opt<T>(_: T) -> Option<T> {
 +    unimplemented!()
 +}
 +
 +fn to_res<T>(_: T) -> Result<T, ()> {
 +    unimplemented!()
 +}
 +
++fn to_ref<'a, T>(_: T) -> &'a T {
++    unimplemented!()
++}
++
 +struct Issue8920<'a> {
 +    option_field: Option<String>,
 +    result_field: Result<String, ()>,
 +    ref_field: Option<&'a usize>,
 +}
 +
 +fn issue_8920() {
 +    let mut vec = vec![Issue8920 {
 +        option_field: Some(String::from("str")),
 +        result_field: Ok(String::from("str")),
 +        ref_field: Some(&1),
 +    }];
 +
 +    let _ = vec
 +        .iter()
 +        .filter(|f| f.option_field.is_some())
 +        .map(|f| f.option_field.clone().unwrap());
 +
 +    let _ = vec
 +        .iter()
 +        .filter(|f| f.ref_field.is_some())
 +        .map(|f| f.ref_field.cloned().unwrap());
 +
 +    let _ = vec
 +        .iter()
 +        .filter(|f| f.ref_field.is_some())
 +        .map(|f| f.ref_field.copied().unwrap());
 +
 +    let _ = vec
 +        .iter()
 +        .filter(|f| f.result_field.is_ok())
 +        .map(|f| f.result_field.clone().unwrap());
 +
 +    let _ = vec
 +        .iter()
 +        .filter(|f| f.result_field.is_ok())
 +        .map(|f| f.result_field.as_ref().unwrap());
 +
 +    let _ = vec
 +        .iter()
 +        .filter(|f| f.result_field.is_ok())
 +        .map(|f| f.result_field.as_deref().unwrap());
 +
 +    let _ = vec
 +        .iter_mut()
 +        .filter(|f| f.result_field.is_ok())
 +        .map(|f| f.result_field.as_mut().unwrap());
 +
 +    let _ = vec
 +        .iter_mut()
 +        .filter(|f| f.result_field.is_ok())
 +        .map(|f| f.result_field.as_deref_mut().unwrap());
 +
 +    let _ = vec
 +        .iter()
 +        .filter(|f| f.result_field.is_ok())
 +        .map(|f| f.result_field.to_owned().unwrap());
 +}
index 465f1b1911017bed9e51509b6a46cac40c3ee744,0000000000000000000000000000000000000000..6e5bbe8f2aaf577c6afab54e692d686d05769b47
mode 100644,000000..100644
--- /dev/null
@@@ -1,94 -1,0 +1,194 @@@
-   --> $DIR/manual_filter_map.rs:54:10
 +error: `filter(..).map(..)` can be simplified as `filter_map(..)`
 +  --> $DIR/manual_filter_map.rs:8:19
 +   |
 +LL |     let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))`
 +   |
 +   = note: `-D clippy::manual-filter-map` implied by `-D warnings`
 +
 +error: `filter(..).map(..)` can be simplified as `filter_map(..)`
 +  --> $DIR/manual_filter_map.rs:11:19
 +   |
 +LL |     let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))`
 +
 +error: `filter(..).map(..)` can be simplified as `filter_map(..)`
 +  --> $DIR/manual_filter_map.rs:14:19
 +   |
 +LL |     let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_res(a).ok())`
 +
 +error: `filter(..).map(..)` can be simplified as `filter_map(..)`
-   --> $DIR/manual_filter_map.rs:59:10
++  --> $DIR/manual_filter_map.rs:17:10
++   |
++LL |           .filter(|&x| to_ref(to_opt(x)).is_some())
++   |  __________^
++LL | |         .map(|y| to_ref(to_opt(y)).unwrap());
++   | |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))`
++
++error: `filter(..).map(..)` can be simplified as `filter_map(..)`
++  --> $DIR/manual_filter_map.rs:20:10
++   |
++LL |           .filter(|x| to_ref(to_opt(*x)).is_some())
++   |  __________^
++LL | |         .map(|y| to_ref(to_opt(y)).unwrap());
++   | |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))`
++
++error: `filter(..).map(..)` can be simplified as `filter_map(..)`
++  --> $DIR/manual_filter_map.rs:24:10
++   |
++LL |           .filter(|&x| to_ref(to_res(x)).is_ok())
++   |  __________^
++LL | |         .map(|y| to_ref(to_res(y)).unwrap());
++   | |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())`
++
++error: `filter(..).map(..)` can be simplified as `filter_map(..)`
++  --> $DIR/manual_filter_map.rs:27:10
++   |
++LL |           .filter(|x| to_ref(to_res(*x)).is_ok())
++   |  __________^
++LL | |         .map(|y| to_ref(to_res(y)).unwrap());
++   | |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_filter_map.rs:33:27
++   |
++LL |     iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
++   |
++   = note: `-D clippy::manual-find-map` implied by `-D warnings`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_filter_map.rs:34:28
++   |
++LL |     iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
++   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_filter_map.rs:35:31
++   |
++LL |     iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_filter_map.rs:36:31
++   |
++LL |     iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_filter_map.rs:38:30
++   |
++LL |     iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_filter_map.rs:39:31
++   |
++LL |     iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_filter_map.rs:40:32
++   |
++LL |     iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
++   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_filter_map.rs:41:31
++   |
++LL |     iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_filter_map.rs:42:32
++   |
++LL |     iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
++   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_filter_map.rs:43:35
++   |
++LL |     iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
++   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref().ok())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_filter_map.rs:44:35
++   |
++LL |     iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
++   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())`
++
++error: `filter(..).map(..)` can be simplified as `filter_map(..)`
++  --> $DIR/manual_filter_map.rs:92:10
 +   |
 +LL |           .filter(|f| f.option_field.is_some())
 +   |  __________^
 +LL | |         .map(|f| f.option_field.clone().unwrap());
 +   | |_________________________________________________^ help: try: `filter_map(|f| f.option_field.clone())`
 +
 +error: `filter(..).map(..)` can be simplified as `filter_map(..)`
-   --> $DIR/manual_filter_map.rs:64:10
++  --> $DIR/manual_filter_map.rs:97:10
 +   |
 +LL |           .filter(|f| f.ref_field.is_some())
 +   |  __________^
 +LL | |         .map(|f| f.ref_field.cloned().unwrap());
 +   | |_______________________________________________^ help: try: `filter_map(|f| f.ref_field.cloned())`
 +
 +error: `filter(..).map(..)` can be simplified as `filter_map(..)`
-   --> $DIR/manual_filter_map.rs:69:10
++  --> $DIR/manual_filter_map.rs:102:10
 +   |
 +LL |           .filter(|f| f.ref_field.is_some())
 +   |  __________^
 +LL | |         .map(|f| f.ref_field.copied().unwrap());
 +   | |_______________________________________________^ help: try: `filter_map(|f| f.ref_field.copied())`
 +
 +error: `filter(..).map(..)` can be simplified as `filter_map(..)`
-   --> $DIR/manual_filter_map.rs:74:10
++  --> $DIR/manual_filter_map.rs:107:10
 +   |
 +LL |           .filter(|f| f.result_field.is_ok())
 +   |  __________^
 +LL | |         .map(|f| f.result_field.clone().unwrap());
 +   | |_________________________________________________^ help: try: `filter_map(|f| f.result_field.clone().ok())`
 +
 +error: `filter(..).map(..)` can be simplified as `filter_map(..)`
-   --> $DIR/manual_filter_map.rs:79:10
++  --> $DIR/manual_filter_map.rs:112:10
 +   |
 +LL |           .filter(|f| f.result_field.is_ok())
 +   |  __________^
 +LL | |         .map(|f| f.result_field.as_ref().unwrap());
 +   | |__________________________________________________^ help: try: `filter_map(|f| f.result_field.as_ref().ok())`
 +
 +error: `filter(..).map(..)` can be simplified as `filter_map(..)`
-   --> $DIR/manual_filter_map.rs:84:10
++  --> $DIR/manual_filter_map.rs:117:10
 +   |
 +LL |           .filter(|f| f.result_field.is_ok())
 +   |  __________^
 +LL | |         .map(|f| f.result_field.as_deref().unwrap());
 +   | |____________________________________________________^ help: try: `filter_map(|f| f.result_field.as_deref().ok())`
 +
 +error: `filter(..).map(..)` can be simplified as `filter_map(..)`
-   --> $DIR/manual_filter_map.rs:89:10
++  --> $DIR/manual_filter_map.rs:122:10
 +   |
 +LL |           .filter(|f| f.result_field.is_ok())
 +   |  __________^
 +LL | |         .map(|f| f.result_field.as_mut().unwrap());
 +   | |__________________________________________________^ help: try: `filter_map(|f| f.result_field.as_mut().ok())`
 +
 +error: `filter(..).map(..)` can be simplified as `filter_map(..)`
-   --> $DIR/manual_filter_map.rs:94:10
++  --> $DIR/manual_filter_map.rs:127:10
 +   |
 +LL |           .filter(|f| f.result_field.is_ok())
 +   |  __________^
 +LL | |         .map(|f| f.result_field.as_deref_mut().unwrap());
 +   | |________________________________________________________^ help: try: `filter_map(|f| f.result_field.as_deref_mut().ok())`
 +
 +error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- error: aborting due to 12 previous errors
++  --> $DIR/manual_filter_map.rs:132:10
 +   |
 +LL |           .filter(|f| f.result_field.is_ok())
 +   |  __________^
 +LL | |         .map(|f| f.result_field.to_owned().unwrap());
 +   | |____________________________________________________^ help: try: `filter_map(|f| f.result_field.to_owned().ok())`
 +
++error: aborting due to 27 previous errors
 +
index d69b6c1dcf3bb7b5ba84cac8d1e9fab1d0fef668,0000000000000000000000000000000000000000..54302beceff628171c4833ba1d3ead35bbfded5d
mode 100644,000000..100644
--- /dev/null
@@@ -1,87 -1,0 +1,124 @@@
 +// run-rustfix
 +#![allow(dead_code)]
 +#![warn(clippy::manual_find_map)]
 +#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
 +
 +fn main() {
 +    // is_some(), unwrap()
 +    let _ = (0..).find_map(|a| to_opt(a));
 +
 +    // ref pattern, expect()
 +    let _ = (0..).find_map(|a| to_opt(a));
 +
 +    // is_ok(), unwrap_or()
 +    let _ = (0..).find_map(|a| to_res(a).ok());
++
++    let _ = (1..5)
++        .find_map(|y| *to_ref(to_opt(y)));
++    let _ = (1..5)
++        .find_map(|y| *to_ref(to_opt(y)));
++
++    let _ = (1..5)
++        .find_map(|y| to_ref(to_res(y)).ok());
++    let _ = (1..5)
++        .find_map(|y| to_ref(to_res(y)).ok());
++}
++
++#[rustfmt::skip]
++fn simple_equal() {
++    iter::<Option<u8>>().find_map(|x| x);
++    iter::<&Option<u8>>().find_map(|x| *x);
++    iter::<&&Option<u8>>().find_map(|x| **x);
++    iter::<Option<&u8>>().find_map(|x| x.cloned());
++    iter::<&Option<&u8>>().find_map(|x| x.cloned());
++    iter::<&Option<String>>().find_map(|x| x.as_deref());
++    iter::<Option<&String>>().find_map(|y| to_ref(y).cloned());
++
++    iter::<Result<u8, ()>>().find_map(|x| x.ok());
++    iter::<&Result<u8, ()>>().find_map(|x| x.ok());
++    iter::<&&Result<u8, ()>>().find_map(|x| x.ok());
++    iter::<Result<&u8, ()>>().find_map(|x| x.cloned().ok());
++    iter::<&Result<&u8, ()>>().find_map(|x| x.cloned().ok());
++    iter::<&Result<String, ()>>().find_map(|x| x.as_deref().ok());
++    iter::<Result<&String, ()>>().find_map(|y| to_ref(y).cloned().ok());
 +}
 +
 +fn no_lint() {
 +    // no shared code
 +    let _ = (0..).filter(|n| *n > 1).map(|n| n + 1);
 +
 +    // very close but different since filter() provides a reference
 +    let _ = (0..).find(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap());
 +
 +    // similar but different
 +    let _ = (0..).find(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap());
 +    let _ = (0..)
 +        .find(|n| to_opt(n).map(|n| n + 1).is_some())
 +        .map(|a| to_opt(a).unwrap());
 +}
 +
++fn iter<T>() -> impl Iterator<Item = T> {
++    std::iter::empty()
++}
++
 +fn to_opt<T>(_: T) -> Option<T> {
 +    unimplemented!()
 +}
 +
 +fn to_res<T>(_: T) -> Result<T, ()> {
 +    unimplemented!()
 +}
 +
++fn to_ref<'a, T>(_: T) -> &'a T {
++    unimplemented!()
++}
++
 +struct Issue8920<'a> {
 +    option_field: Option<String>,
 +    result_field: Result<String, ()>,
 +    ref_field: Option<&'a usize>,
 +}
 +
 +fn issue_8920() {
 +    let mut vec = vec![Issue8920 {
 +        option_field: Some(String::from("str")),
 +        result_field: Ok(String::from("str")),
 +        ref_field: Some(&1),
 +    }];
 +
 +    let _ = vec
 +        .iter()
 +        .find_map(|f| f.option_field.clone());
 +
 +    let _ = vec
 +        .iter()
 +        .find_map(|f| f.ref_field.cloned());
 +
 +    let _ = vec
 +        .iter()
 +        .find_map(|f| f.ref_field.copied());
 +
 +    let _ = vec
 +        .iter()
 +        .find_map(|f| f.result_field.clone().ok());
 +
 +    let _ = vec
 +        .iter()
 +        .find_map(|f| f.result_field.as_ref().ok());
 +
 +    let _ = vec
 +        .iter()
 +        .find_map(|f| f.result_field.as_deref().ok());
 +
 +    let _ = vec
 +        .iter_mut()
 +        .find_map(|f| f.result_field.as_mut().ok());
 +
 +    let _ = vec
 +        .iter_mut()
 +        .find_map(|f| f.result_field.as_deref_mut().ok());
 +
 +    let _ = vec
 +        .iter()
 +        .find_map(|f| f.result_field.to_owned().ok());
 +}
index 1c4e18e31c8b1883fef79057675513ec717ff79a,0000000000000000000000000000000000000000..afcc1825a9ac1c9170faa6813c6864f2f7ae6d0e
mode 100644,000000..100644
--- /dev/null
@@@ -1,96 -1,0 +1,137 @@@
 +// run-rustfix
 +#![allow(dead_code)]
 +#![warn(clippy::manual_find_map)]
 +#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
 +
 +fn main() {
 +    // is_some(), unwrap()
 +    let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
 +
 +    // ref pattern, expect()
 +    let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
 +
 +    // is_ok(), unwrap_or()
 +    let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
++
++    let _ = (1..5)
++        .find(|&x| to_ref(to_opt(x)).is_some())
++        .map(|y| to_ref(to_opt(y)).unwrap());
++    let _ = (1..5)
++        .find(|x| to_ref(to_opt(*x)).is_some())
++        .map(|y| to_ref(to_opt(y)).unwrap());
++
++    let _ = (1..5)
++        .find(|&x| to_ref(to_res(x)).is_ok())
++        .map(|y| to_ref(to_res(y)).unwrap());
++    let _ = (1..5)
++        .find(|x| to_ref(to_res(*x)).is_ok())
++        .map(|y| to_ref(to_res(y)).unwrap());
++}
++
++#[rustfmt::skip]
++fn simple_equal() {
++    iter::<Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
++    iter::<&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
++    iter::<&&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
++    iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
++    iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
++    iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
++    iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
++
++    iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
++    iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
++    iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
++    iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
++    iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
++    iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
++    iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
 +}
 +
 +fn no_lint() {
 +    // no shared code
 +    let _ = (0..).filter(|n| *n > 1).map(|n| n + 1);
 +
 +    // very close but different since filter() provides a reference
 +    let _ = (0..).find(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap());
 +
 +    // similar but different
 +    let _ = (0..).find(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap());
 +    let _ = (0..)
 +        .find(|n| to_opt(n).map(|n| n + 1).is_some())
 +        .map(|a| to_opt(a).unwrap());
 +}
 +
++fn iter<T>() -> impl Iterator<Item = T> {
++    std::iter::empty()
++}
++
 +fn to_opt<T>(_: T) -> Option<T> {
 +    unimplemented!()
 +}
 +
 +fn to_res<T>(_: T) -> Result<T, ()> {
 +    unimplemented!()
 +}
 +
++fn to_ref<'a, T>(_: T) -> &'a T {
++    unimplemented!()
++}
++
 +struct Issue8920<'a> {
 +    option_field: Option<String>,
 +    result_field: Result<String, ()>,
 +    ref_field: Option<&'a usize>,
 +}
 +
 +fn issue_8920() {
 +    let mut vec = vec![Issue8920 {
 +        option_field: Some(String::from("str")),
 +        result_field: Ok(String::from("str")),
 +        ref_field: Some(&1),
 +    }];
 +
 +    let _ = vec
 +        .iter()
 +        .find(|f| f.option_field.is_some())
 +        .map(|f| f.option_field.clone().unwrap());
 +
 +    let _ = vec
 +        .iter()
 +        .find(|f| f.ref_field.is_some())
 +        .map(|f| f.ref_field.cloned().unwrap());
 +
 +    let _ = vec
 +        .iter()
 +        .find(|f| f.ref_field.is_some())
 +        .map(|f| f.ref_field.copied().unwrap());
 +
 +    let _ = vec
 +        .iter()
 +        .find(|f| f.result_field.is_ok())
 +        .map(|f| f.result_field.clone().unwrap());
 +
 +    let _ = vec
 +        .iter()
 +        .find(|f| f.result_field.is_ok())
 +        .map(|f| f.result_field.as_ref().unwrap());
 +
 +    let _ = vec
 +        .iter()
 +        .find(|f| f.result_field.is_ok())
 +        .map(|f| f.result_field.as_deref().unwrap());
 +
 +    let _ = vec
 +        .iter_mut()
 +        .find(|f| f.result_field.is_ok())
 +        .map(|f| f.result_field.as_mut().unwrap());
 +
 +    let _ = vec
 +        .iter_mut()
 +        .find(|f| f.result_field.is_ok())
 +        .map(|f| f.result_field.as_deref_mut().unwrap());
 +
 +    let _ = vec
 +        .iter()
 +        .find(|f| f.result_field.is_ok())
 +        .map(|f| f.result_field.to_owned().unwrap());
 +}
index 9dea42b76868b7d838338d55767aeef8da541a5d,0000000000000000000000000000000000000000..c1ac499f7c6010c9ce5f764aef45d01204e38155
mode 100644,000000..100644
--- /dev/null
@@@ -1,94 -1,0 +1,210 @@@
-   --> $DIR/manual_find_map.rs:54:10
 +error: `find(..).map(..)` can be simplified as `find_map(..)`
 +  --> $DIR/manual_find_map.rs:8:19
 +   |
 +LL |     let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))`
 +   |
 +   = note: `-D clippy::manual-find-map` implied by `-D warnings`
 +
 +error: `find(..).map(..)` can be simplified as `find_map(..)`
 +  --> $DIR/manual_find_map.rs:11:19
 +   |
 +LL |     let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))`
 +
 +error: `find(..).map(..)` can be simplified as `find_map(..)`
 +  --> $DIR/manual_find_map.rs:14:19
 +   |
 +LL |     let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_res(a).ok())`
 +
 +error: `find(..).map(..)` can be simplified as `find_map(..)`
-   --> $DIR/manual_find_map.rs:59:10
++  --> $DIR/manual_find_map.rs:17:10
++   |
++LL |           .find(|&x| to_ref(to_opt(x)).is_some())
++   |  __________^
++LL | |         .map(|y| to_ref(to_opt(y)).unwrap());
++   | |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:20:10
++   |
++LL |           .find(|x| to_ref(to_opt(*x)).is_some())
++   |  __________^
++LL | |         .map(|y| to_ref(to_opt(y)).unwrap());
++   | |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:24:10
++   |
++LL |           .find(|&x| to_ref(to_res(x)).is_ok())
++   |  __________^
++LL | |         .map(|y| to_ref(to_res(y)).unwrap());
++   | |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:27:10
++   |
++LL |           .find(|x| to_ref(to_res(*x)).is_ok())
++   |  __________^
++LL | |         .map(|y| to_ref(to_res(y)).unwrap());
++   | |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:33:26
++   |
++LL |     iter::<Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x)`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:34:27
++   |
++LL |     iter::<&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| *x)`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:35:28
++   |
++LL |     iter::<&&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
++   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| **x)`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:36:27
++   |
++LL |     iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:37:28
++   |
++LL |     iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
++   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:38:31
++   |
++LL |     iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:39:31
++   |
++LL |     iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:41:30
++   |
++LL |     iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:42:31
++   |
++LL |     iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:43:32
++   |
++LL |     iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
++   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:44:31
++   |
++LL |     iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:45:32
++   |
++LL |     iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
++   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:46:35
++   |
++LL |     iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
++   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref().ok())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:47:35
++   |
++LL |     iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
++   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())`
++
++error: `find(..).map(..)` can be simplified as `find_map(..)`
++  --> $DIR/manual_find_map.rs:95:10
 +   |
 +LL |           .find(|f| f.option_field.is_some())
 +   |  __________^
 +LL | |         .map(|f| f.option_field.clone().unwrap());
 +   | |_________________________________________________^ help: try: `find_map(|f| f.option_field.clone())`
 +
 +error: `find(..).map(..)` can be simplified as `find_map(..)`
-   --> $DIR/manual_find_map.rs:64:10
++  --> $DIR/manual_find_map.rs:100:10
 +   |
 +LL |           .find(|f| f.ref_field.is_some())
 +   |  __________^
 +LL | |         .map(|f| f.ref_field.cloned().unwrap());
 +   | |_______________________________________________^ help: try: `find_map(|f| f.ref_field.cloned())`
 +
 +error: `find(..).map(..)` can be simplified as `find_map(..)`
-   --> $DIR/manual_find_map.rs:69:10
++  --> $DIR/manual_find_map.rs:105:10
 +   |
 +LL |           .find(|f| f.ref_field.is_some())
 +   |  __________^
 +LL | |         .map(|f| f.ref_field.copied().unwrap());
 +   | |_______________________________________________^ help: try: `find_map(|f| f.ref_field.copied())`
 +
 +error: `find(..).map(..)` can be simplified as `find_map(..)`
-   --> $DIR/manual_find_map.rs:74:10
++  --> $DIR/manual_find_map.rs:110:10
 +   |
 +LL |           .find(|f| f.result_field.is_ok())
 +   |  __________^
 +LL | |         .map(|f| f.result_field.clone().unwrap());
 +   | |_________________________________________________^ help: try: `find_map(|f| f.result_field.clone().ok())`
 +
 +error: `find(..).map(..)` can be simplified as `find_map(..)`
-   --> $DIR/manual_find_map.rs:79:10
++  --> $DIR/manual_find_map.rs:115:10
 +   |
 +LL |           .find(|f| f.result_field.is_ok())
 +   |  __________^
 +LL | |         .map(|f| f.result_field.as_ref().unwrap());
 +   | |__________________________________________________^ help: try: `find_map(|f| f.result_field.as_ref().ok())`
 +
 +error: `find(..).map(..)` can be simplified as `find_map(..)`
-   --> $DIR/manual_find_map.rs:84:10
++  --> $DIR/manual_find_map.rs:120:10
 +   |
 +LL |           .find(|f| f.result_field.is_ok())
 +   |  __________^
 +LL | |         .map(|f| f.result_field.as_deref().unwrap());
 +   | |____________________________________________________^ help: try: `find_map(|f| f.result_field.as_deref().ok())`
 +
 +error: `find(..).map(..)` can be simplified as `find_map(..)`
-   --> $DIR/manual_find_map.rs:89:10
++  --> $DIR/manual_find_map.rs:125:10
 +   |
 +LL |           .find(|f| f.result_field.is_ok())
 +   |  __________^
 +LL | |         .map(|f| f.result_field.as_mut().unwrap());
 +   | |__________________________________________________^ help: try: `find_map(|f| f.result_field.as_mut().ok())`
 +
 +error: `find(..).map(..)` can be simplified as `find_map(..)`
-   --> $DIR/manual_find_map.rs:94:10
++  --> $DIR/manual_find_map.rs:130:10
 +   |
 +LL |           .find(|f| f.result_field.is_ok())
 +   |  __________^
 +LL | |         .map(|f| f.result_field.as_deref_mut().unwrap());
 +   | |________________________________________________________^ help: try: `find_map(|f| f.result_field.as_deref_mut().ok())`
 +
 +error: `find(..).map(..)` can be simplified as `find_map(..)`
- error: aborting due to 12 previous errors
++  --> $DIR/manual_find_map.rs:135:10
 +   |
 +LL |           .find(|f| f.result_field.is_ok())
 +   |  __________^
 +LL | |         .map(|f| f.result_field.to_owned().unwrap());
 +   | |____________________________________________________^ help: try: `find_map(|f| f.result_field.to_owned().ok())`
 +
++error: aborting due to 30 previous errors
 +
index 6c5232ec5f55b7d0e59dec9373f55b4f91cd0f16,0000000000000000000000000000000000000000..d922593bc6f93445ab9b3fc7805692a4da2a6c31
mode 100644,000000..100644
--- /dev/null
@@@ -1,109 -1,0 +1,125 @@@
 +#![warn(clippy::manual_flatten)]
 +#![allow(clippy::useless_vec)]
 +
 +fn main() {
 +    // Test for loop over implicitly adjusted `Iterator` with `if let` expression
 +    let x = vec![Some(1), Some(2), Some(3)];
 +    for n in x {
 +        if let Some(y) = n {
 +            println!("{}", y);
 +        }
 +    }
 +
 +    // Test for loop over implicitly implicitly adjusted `Iterator` with `if let` statement
 +    let y: Vec<Result<i32, i32>> = vec![];
 +    for n in y.clone() {
 +        if let Ok(n) = n {
 +            println!("{}", n);
 +        };
 +    }
 +
 +    // Test for loop over by reference
 +    for n in &y {
 +        if let Ok(n) = n {
 +            println!("{}", n);
 +        }
 +    }
 +
 +    // Test for loop over an implicit reference
 +    let z = &y;
 +    for n in z {
 +        if let Ok(n) = n {
 +            println!("{}", n);
 +        }
 +    }
 +
 +    // Test for loop over `Iterator` with `if let` expression
 +    let z = vec![Some(1), Some(2), Some(3)];
 +    let z = z.iter();
 +    for n in z {
 +        if let Some(m) = n {
 +            println!("{}", m);
 +        }
 +    }
 +
 +    // Using the `None` variant should not trigger the lint
 +    // Note: for an autofixable suggestion, the binding in the for loop has to take the
 +    // name of the binding in the `if let`
 +    let z = vec![Some(1), Some(2), Some(3)];
 +    for n in z {
 +        if n.is_none() {
 +            println!("Nada.");
 +        }
 +    }
 +
 +    // Using the `Err` variant should not trigger the lint
 +    for n in y.clone() {
 +        if let Err(e) = n {
 +            println!("Oops: {}!", e);
 +        }
 +    }
 +
 +    // Having an else clause should not trigger the lint
 +    for n in y.clone() {
 +        if let Ok(n) = n {
 +            println!("{}", n);
 +        } else {
 +            println!("Oops!");
 +        }
 +    }
 +
 +    let vec_of_ref = vec![&Some(1)];
 +    for n in &vec_of_ref {
 +        if let Some(n) = n {
 +            println!("{:?}", n);
 +        }
 +    }
 +
 +    let vec_of_ref = &vec_of_ref;
 +    for n in vec_of_ref {
 +        if let Some(n) = n {
 +            println!("{:?}", n);
 +        }
 +    }
 +
 +    let slice_of_ref = &[&Some(1)];
 +    for n in slice_of_ref {
 +        if let Some(n) = n {
 +            println!("{:?}", n);
 +        }
 +    }
 +
 +    struct Test {
 +        a: usize,
 +    }
 +
 +    let mut vec_of_struct = [Some(Test { a: 1 }), None];
 +
 +    // Usage of `if let` expression should not trigger lint
 +    for n in vec_of_struct.iter_mut() {
 +        if let Some(z) = n {
 +            *n = None;
 +        }
 +    }
 +
 +    // Using manual flatten should not trigger the lint
 +    for n in vec![Some(1), Some(2), Some(3)].iter().flatten() {
 +        println!("{}", n);
 +    }
++
++    run_unformatted_tests();
++}
++
++#[rustfmt::skip]
++fn run_unformatted_tests() {
++    // Skip rustfmt here on purpose so the suggestion does not fit in one line
++    for n in vec![
++        Some(1),
++        Some(2),
++        Some(3)
++    ].iter() {
++        if let Some(n) = n {
++            println!("{:?}", n);
++        }
++    }
 +}
index 392e1a39393754214ce44a4c04bdc222b85cb6b3,0000000000000000000000000000000000000000..da053c0566833618d5497711b314091575480b12
mode 100644,000000..100644
--- /dev/null
@@@ -1,171 -1,0 +1,199 @@@
- error: aborting due to 8 previous errors
 +error: unnecessary `if let` since only the `Some` variant of the iterator element is used
 +  --> $DIR/manual_flatten.rs:7:5
 +   |
 +LL |       for n in x {
 +   |       ^        - help: try: `x.into_iter().flatten()`
 +   |  _____|
 +   | |
 +LL | |         if let Some(y) = n {
 +LL | |             println!("{}", y);
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +   = note: `-D clippy::manual-flatten` implied by `-D warnings`
 +help: ...and remove the `if let` statement in the for loop
 +  --> $DIR/manual_flatten.rs:8:9
 +   |
 +LL | /         if let Some(y) = n {
 +LL | |             println!("{}", y);
 +LL | |         }
 +   | |_________^
 +
 +error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
 +  --> $DIR/manual_flatten.rs:15:5
 +   |
 +LL |       for n in y.clone() {
 +   |       ^        --------- help: try: `y.clone().into_iter().flatten()`
 +   |  _____|
 +   | |
 +LL | |         if let Ok(n) = n {
 +LL | |             println!("{}", n);
 +LL | |         };
 +LL | |     }
 +   | |_____^
 +   |
 +help: ...and remove the `if let` statement in the for loop
 +  --> $DIR/manual_flatten.rs:16:9
 +   |
 +LL | /         if let Ok(n) = n {
 +LL | |             println!("{}", n);
 +LL | |         };
 +   | |_________^
 +
 +error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
 +  --> $DIR/manual_flatten.rs:22:5
 +   |
 +LL |       for n in &y {
 +   |       ^        -- help: try: `y.iter().flatten()`
 +   |  _____|
 +   | |
 +LL | |         if let Ok(n) = n {
 +LL | |             println!("{}", n);
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +help: ...and remove the `if let` statement in the for loop
 +  --> $DIR/manual_flatten.rs:23:9
 +   |
 +LL | /         if let Ok(n) = n {
 +LL | |             println!("{}", n);
 +LL | |         }
 +   | |_________^
 +
 +error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
 +  --> $DIR/manual_flatten.rs:30:5
 +   |
 +LL |       for n in z {
 +   |       ^        - help: try: `z.iter().flatten()`
 +   |  _____|
 +   | |
 +LL | |         if let Ok(n) = n {
 +LL | |             println!("{}", n);
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +help: ...and remove the `if let` statement in the for loop
 +  --> $DIR/manual_flatten.rs:31:9
 +   |
 +LL | /         if let Ok(n) = n {
 +LL | |             println!("{}", n);
 +LL | |         }
 +   | |_________^
 +
 +error: unnecessary `if let` since only the `Some` variant of the iterator element is used
 +  --> $DIR/manual_flatten.rs:39:5
 +   |
 +LL |       for n in z {
 +   |       ^        - help: try: `z.flatten()`
 +   |  _____|
 +   | |
 +LL | |         if let Some(m) = n {
 +LL | |             println!("{}", m);
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +help: ...and remove the `if let` statement in the for loop
 +  --> $DIR/manual_flatten.rs:40:9
 +   |
 +LL | /         if let Some(m) = n {
 +LL | |             println!("{}", m);
 +LL | |         }
 +   | |_________^
 +
 +error: unnecessary `if let` since only the `Some` variant of the iterator element is used
 +  --> $DIR/manual_flatten.rs:72:5
 +   |
 +LL |       for n in &vec_of_ref {
 +   |       ^        ----------- help: try: `vec_of_ref.iter().copied().flatten()`
 +   |  _____|
 +   | |
 +LL | |         if let Some(n) = n {
 +LL | |             println!("{:?}", n);
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +help: ...and remove the `if let` statement in the for loop
 +  --> $DIR/manual_flatten.rs:73:9
 +   |
 +LL | /         if let Some(n) = n {
 +LL | |             println!("{:?}", n);
 +LL | |         }
 +   | |_________^
 +
 +error: unnecessary `if let` since only the `Some` variant of the iterator element is used
 +  --> $DIR/manual_flatten.rs:79:5
 +   |
 +LL |       for n in vec_of_ref {
 +   |       ^        ---------- help: try: `vec_of_ref.iter().copied().flatten()`
 +   |  _____|
 +   | |
 +LL | |         if let Some(n) = n {
 +LL | |             println!("{:?}", n);
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +help: ...and remove the `if let` statement in the for loop
 +  --> $DIR/manual_flatten.rs:80:9
 +   |
 +LL | /         if let Some(n) = n {
 +LL | |             println!("{:?}", n);
 +LL | |         }
 +   | |_________^
 +
 +error: unnecessary `if let` since only the `Some` variant of the iterator element is used
 +  --> $DIR/manual_flatten.rs:86:5
 +   |
 +LL |       for n in slice_of_ref {
 +   |       ^        ------------ help: try: `slice_of_ref.iter().copied().flatten()`
 +   |  _____|
 +   | |
 +LL | |         if let Some(n) = n {
 +LL | |             println!("{:?}", n);
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +help: ...and remove the `if let` statement in the for loop
 +  --> $DIR/manual_flatten.rs:87:9
 +   |
 +LL | /         if let Some(n) = n {
 +LL | |             println!("{:?}", n);
 +LL | |         }
 +   | |_________^
 +
++error: unnecessary `if let` since only the `Some` variant of the iterator element is used
++  --> $DIR/manual_flatten.rs:116:5
++   |
++LL | /     for n in vec![
++LL | |         Some(1),
++LL | |         Some(2),
++LL | |         Some(3)
++...  |
++LL | |         }
++LL | |     }
++   | |_____^
++   |
++help: remove the `if let` statement in the for loop and then...
++  --> $DIR/manual_flatten.rs:121:9
++   |
++LL | /         if let Some(n) = n {
++LL | |             println!("{:?}", n);
++LL | |         }
++   | |_________^
++help: try
++   |
++LL ~     for n in vec![
++LL +         Some(1),
++LL +         Some(2),
++LL +         Some(3)
++LL ~     ].iter().flatten() {
++   |
++
++error: aborting due to 9 previous errors
 +
index e9b41354c58fa50599e93f2b39a5dc015f61646d,0000000000000000000000000000000000000000..312819a0a2cf83d567307d788cb5e8c72394561b
mode 100644,000000..100644
--- /dev/null
@@@ -1,66 -1,0 +1,65 @@@
-     //     let _ = [0u8, 1, 2, 3]
-     //         .into_iter()
-     //         .map(|n| match n {
-     //             1 => [n
-     //                 .saturating_add(1)
-     //                 .saturating_add(1)
-     //                 .saturating_add(1)
-     //                 .saturating_add(1)
-     //                 .saturating_add(1)
-     //                 .saturating_add(1)
-     //                 .saturating_add(1)
-     //                 .saturating_add(1)],
-     //             n => [n],
-     //         })
-     //         .flatten();
 +// run-rustfix
 +
 +#![warn(clippy::all, clippy::pedantic)]
 +#![allow(clippy::let_underscore_drop)]
 +#![allow(clippy::missing_docs_in_private_items)]
 +#![allow(clippy::map_identity)]
 +#![allow(clippy::redundant_closure)]
 +#![allow(clippy::unnecessary_wraps)]
 +#![feature(result_flattening)]
 +
 +fn main() {
 +    // mapping to Option on Iterator
 +    fn option_id(x: i8) -> Option<i8> {
 +        Some(x)
 +    }
 +    let option_id_ref: fn(i8) -> Option<i8> = option_id;
 +    let option_id_closure = |x| Some(x);
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id).collect();
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_ref).collect();
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_closure).collect();
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect();
 +
 +    // mapping to Iterator on Iterator
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect();
 +
 +    // mapping to Option on Option
 +    let _: Option<_> = (Some(Some(1))).and_then(|x| x);
 +
 +    // mapping to Result on Result
 +    let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x);
 +
 +    issue8734();
 +    issue8878();
 +}
 +
 +fn issue8734() {
- // for a splitted output of the diagnostic
++    let _ = [0u8, 1, 2, 3]
++        .into_iter()
++        .flat_map(|n| match n {
++            1 => [n
++                .saturating_add(1)
++                .saturating_add(1)
++                .saturating_add(1)
++                .saturating_add(1)
++                .saturating_add(1)
++                .saturating_add(1)
++                .saturating_add(1)
++                .saturating_add(1)],
++            n => [n],
++        });
 +}
 +
 +#[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again
 +#[rustfmt::skip] // whitespace is important for this one
 +fn issue8878() {
 +    std::collections::HashMap::<u32, u32>::new()
 +        .get(&0)
 +        .and_then(|_| {
 +// we need some newlines
 +// so that the span is big enough
++// for a split output of the diagnostic
 +            Some("")
 + // whitespace beforehand is important as well
 +        });
 +}
index 4345c6eee746120afc03cb46fc3e6f79a2d94f40,0000000000000000000000000000000000000000..3fbf4f9a1b044b8c5621a2f73c8ffd7bd39deb84
mode 100644,000000..100644
--- /dev/null
@@@ -1,67 -1,0 +1,67 @@@
-     //     let _ = [0u8, 1, 2, 3]
-     //         .into_iter()
-     //         .map(|n| match n {
-     //             1 => [n
-     //                 .saturating_add(1)
-     //                 .saturating_add(1)
-     //                 .saturating_add(1)
-     //                 .saturating_add(1)
-     //                 .saturating_add(1)
-     //                 .saturating_add(1)
-     //                 .saturating_add(1)
-     //                 .saturating_add(1)],
-     //             n => [n],
-     //         })
-     //         .flatten();
 +// run-rustfix
 +
 +#![warn(clippy::all, clippy::pedantic)]
 +#![allow(clippy::let_underscore_drop)]
 +#![allow(clippy::missing_docs_in_private_items)]
 +#![allow(clippy::map_identity)]
 +#![allow(clippy::redundant_closure)]
 +#![allow(clippy::unnecessary_wraps)]
 +#![feature(result_flattening)]
 +
 +fn main() {
 +    // mapping to Option on Iterator
 +    fn option_id(x: i8) -> Option<i8> {
 +        Some(x)
 +    }
 +    let option_id_ref: fn(i8) -> Option<i8> = option_id;
 +    let option_id_closure = |x| Some(x);
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
 +
 +    // mapping to Iterator on Iterator
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
 +
 +    // mapping to Option on Option
 +    let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
 +
 +    // mapping to Result on Result
 +    let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
 +
 +    issue8734();
 +    issue8878();
 +}
 +
 +fn issue8734() {
- // for a splitted output of the diagnostic
++    let _ = [0u8, 1, 2, 3]
++        .into_iter()
++        .map(|n| match n {
++            1 => [n
++                .saturating_add(1)
++                .saturating_add(1)
++                .saturating_add(1)
++                .saturating_add(1)
++                .saturating_add(1)
++                .saturating_add(1)
++                .saturating_add(1)
++                .saturating_add(1)],
++            n => [n],
++        })
++        .flatten();
 +}
 +
 +#[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again
 +#[rustfmt::skip] // whitespace is important for this one
 +fn issue8878() {
 +    std::collections::HashMap::<u32, u32>::new()
 +        .get(&0)
 +        .map(|_| {
 +// we need some newlines
 +// so that the span is big enough
++// for a split output of the diagnostic
 +            Some("")
 + // whitespace beforehand is important as well
 +        })
 +        .flatten();
 +}
index f3b82ad08d0fc3c61c31cb53e46c035c06789440,0000000000000000000000000000000000000000..c91f0b9ae94fec2626997de308d60c006d6ffaa4
mode 100644,000000..100644
--- /dev/null
@@@ -1,70 -1,0 +1,99 @@@
- LL | | // for a splitted output of the diagnostic
 +error: called `map(..).flatten()` on `Iterator`
 +  --> $DIR/map_flatten_fixable.rs:18:47
 +   |
 +LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
 +   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id)`
 +   |
 +   = note: `-D clippy::map-flatten` implied by `-D warnings`
 +
 +error: called `map(..).flatten()` on `Iterator`
 +  --> $DIR/map_flatten_fixable.rs:19:47
 +   |
 +LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
 +   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_ref)`
 +
 +error: called `map(..).flatten()` on `Iterator`
 +  --> $DIR/map_flatten_fixable.rs:20:47
 +   |
 +LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
 +   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_closure)`
 +
 +error: called `map(..).flatten()` on `Iterator`
 +  --> $DIR/map_flatten_fixable.rs:21:47
 +   |
 +LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
 +   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(|x| x.checked_add(1))`
 +
 +error: called `map(..).flatten()` on `Iterator`
 +  --> $DIR/map_flatten_fixable.rs:24:47
 +   |
 +LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
 +   |                                               ^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `flat_map` and remove the `.flatten()`: `flat_map(|x| 0..x)`
 +
 +error: called `map(..).flatten()` on `Option`
 +  --> $DIR/map_flatten_fixable.rs:27:40
 +   |
 +LL |     let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
 +   |                                        ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)`
 +
 +error: called `map(..).flatten()` on `Result`
 +  --> $DIR/map_flatten_fixable.rs:30:42
 +   |
 +LL |     let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
 +   |                                          ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)`
 +
++error: called `map(..).flatten()` on `Iterator`
++  --> $DIR/map_flatten_fixable.rs:39:10
++   |
++LL |           .map(|n| match n {
++   |  __________^
++LL | |             1 => [n
++LL | |                 .saturating_add(1)
++LL | |                 .saturating_add(1)
++...  |
++LL | |         })
++LL | |         .flatten();
++   | |__________________^
++   |
++help: try replacing `map` with `flat_map` and remove the `.flatten()`
++   |
++LL ~         .flat_map(|n| match n {
++LL +             1 => [n
++LL +                 .saturating_add(1)
++LL +                 .saturating_add(1)
++LL +                 .saturating_add(1)
++LL +                 .saturating_add(1)
++LL +                 .saturating_add(1)
++LL +                 .saturating_add(1)
++LL +                 .saturating_add(1)
++LL +                 .saturating_add(1)],
++LL +             n => [n],
++LL ~         });
++   |
++
 +error: called `map(..).flatten()` on `Option`
 +  --> $DIR/map_flatten_fixable.rs:59:10
 +   |
 +LL |           .map(|_| {
 +   |  __________^
 +LL | | // we need some newlines
 +LL | | // so that the span is big enough
- LL + // for a splitted output of the diagnostic
++LL | | // for a split output of the diagnostic
 +...  |
 +LL | |         })
 +LL | |         .flatten();
 +   | |__________________^
 +   |
 +help: try replacing `map` with `and_then` and remove the `.flatten()`
 +   |
 +LL ~         .and_then(|_| {
 +LL + // we need some newlines
 +LL + // so that the span is big enough
- error: aborting due to 8 previous errors
++LL + // for a split output of the diagnostic
 +LL +             Some("")
 +LL +  // whitespace beforehand is important as well
 +LL ~         });
 +   |
 +
++error: aborting due to 9 previous errors
 +
index 36f233f33460720759355d78d1c0cf26623d7713,0000000000000000000000000000000000000000..1ccbfda64b73a5240ae68929f83c37d07cd66c05
mode 100644,000000..100644
--- /dev/null
@@@ -1,164 -1,0 +1,170 @@@
 +// run-rustfix
 +
 +#![warn(clippy::match_like_matches_macro)]
 +#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)]
 +
 +fn main() {
 +    let x = Some(5);
 +
 +    // Lint
 +    let _y = matches!(x, Some(0));
 +
 +    // Lint
 +    let _w = matches!(x, Some(_));
 +
 +    // Turn into is_none
 +    let _z = x.is_none();
 +
 +    // Lint
 +    let _zz = !matches!(x, Some(r) if r == 0);
 +
 +    // Lint
 +    let _zzz = matches!(x, Some(5));
 +
 +    // No lint
 +    let _a = match x {
 +        Some(_) => false,
 +        _ => false,
 +    };
 +
 +    // No lint
 +    let _ab = match x {
 +        Some(0) => false,
 +        _ => true,
 +        None => false,
 +    };
 +
 +    enum E {
 +        A(u32),
 +        B(i32),
 +        C,
 +        D,
 +    }
 +    let x = E::A(2);
 +    {
 +        // lint
 +        let _ans = matches!(x, E::A(_) | E::B(_));
 +    }
++    {
++        // lint
++        // skip rustfmt to prevent removing block for first pattern
++        #[rustfmt::skip]
++        let _ans = matches!(x, E::A(_) | E::B(_));
++    }
 +    {
 +        // lint
 +        let _ans = !matches!(x, E::B(_) | E::C);
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => false,
 +            E::C => true,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => true,
 +            E::B(_) => false,
 +            E::C => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) if a < 10 => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) => a == 10,
 +            E::B(_) => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        // should print "z" in suggestion (#6503)
 +        let z = &Some(3);
 +        let _z = matches!(z, Some(3));
 +    }
 +
 +    {
 +        // this could also print "z" in suggestion..?
 +        let z = Some(3);
 +        let _z = matches!(&z, Some(3));
 +    }
 +
 +    {
 +        enum AnEnum {
 +            X,
 +            Y,
 +        }
 +
 +        fn foo(_x: AnEnum) {}
 +
 +        fn main() {
 +            let z = AnEnum::X;
 +            // we can't remove the reference here!
 +            let _ = matches!(&z, AnEnum::X);
 +            foo(z);
 +        }
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        // we need the reference here because later val is consumed by fun()
 +        let _res = matches!(&val, &Some(ref _a));
 +        fun(val);
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        let _res = matches!(&val, &Some(ref _a));
 +        fun(val);
 +    }
 +
 +    {
 +        enum E {
 +            A,
 +            B,
 +            C,
 +        }
 +
 +        let _ = match E::A {
 +            E::B => true,
 +            #[cfg(feature = "foo")]
 +            E::A => true,
 +            _ => false,
 +        };
 +    }
 +}
index 750f69fa5088f6fa45898030bfca0d56ee486a81,0000000000000000000000000000000000000000..a49991f594174473c51cc41609d3f808b1d80768
mode 100644,000000..100644
--- /dev/null
@@@ -1,199 -1,0 +1,211 @@@
 +// run-rustfix
 +
 +#![warn(clippy::match_like_matches_macro)]
 +#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)]
 +
 +fn main() {
 +    let x = Some(5);
 +
 +    // Lint
 +    let _y = match x {
 +        Some(0) => true,
 +        _ => false,
 +    };
 +
 +    // Lint
 +    let _w = match x {
 +        Some(_) => true,
 +        _ => false,
 +    };
 +
 +    // Turn into is_none
 +    let _z = match x {
 +        Some(_) => false,
 +        None => true,
 +    };
 +
 +    // Lint
 +    let _zz = match x {
 +        Some(r) if r == 0 => false,
 +        _ => true,
 +    };
 +
 +    // Lint
 +    let _zzz = if let Some(5) = x { true } else { false };
 +
 +    // No lint
 +    let _a = match x {
 +        Some(_) => false,
 +        _ => false,
 +    };
 +
 +    // No lint
 +    let _ab = match x {
 +        Some(0) => false,
 +        _ => true,
 +        None => false,
 +    };
 +
 +    enum E {
 +        A(u32),
 +        B(i32),
 +        C,
 +        D,
 +    }
 +    let x = E::A(2);
 +    {
 +        // lint
 +        let _ans = match x {
 +            E::A(_) => true,
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
++    {
++        // lint
++        // skip rustfmt to prevent removing block for first pattern
++        #[rustfmt::skip]
++        let _ans = match x {
++            E::A(_) => {
++                true
++            }
++            E::B(_) => true,
++            _ => false,
++        };
++    }
 +    {
 +        // lint
 +        let _ans = match x {
 +            E::B(_) => false,
 +            E::C => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => false,
 +            E::C => true,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => true,
 +            E::B(_) => false,
 +            E::C => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) if a < 10 => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) => a == 10,
 +            E::B(_) => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        // should print "z" in suggestion (#6503)
 +        let z = &Some(3);
 +        let _z = match &z {
 +            Some(3) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        // this could also print "z" in suggestion..?
 +        let z = Some(3);
 +        let _z = match &z {
 +            Some(3) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        enum AnEnum {
 +            X,
 +            Y,
 +        }
 +
 +        fn foo(_x: AnEnum) {}
 +
 +        fn main() {
 +            let z = AnEnum::X;
 +            // we can't remove the reference here!
 +            let _ = match &z {
 +                AnEnum::X => true,
 +                _ => false,
 +            };
 +            foo(z);
 +        }
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        // we need the reference here because later val is consumed by fun()
 +        let _res = match &val {
 +            &Some(ref _a) => true,
 +            _ => false,
 +        };
 +        fun(val);
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        let _res = match &val {
 +            &Some(ref _a) => true,
 +            _ => false,
 +        };
 +        fun(val);
 +    }
 +
 +    {
 +        enum E {
 +            A,
 +            B,
 +            C,
 +        }
 +
 +        let _ = match E::A {
 +            E::B => true,
 +            #[cfg(feature = "foo")]
 +            E::A => true,
 +            _ => false,
 +        };
 +    }
 +}
index d7cedf9f9f1596dfabb659099e2922281b410f93,0000000000000000000000000000000000000000..e94555e27448b0d131c6e34d6d0cb5bdac330b4e
mode 100644,000000..100644
--- /dev/null
@@@ -1,124 -1,0 +1,137 @@@
-   --> $DIR/match_expr_like_matches_macro.rs:66:20
 +error: match expression looks like `matches!` macro
 +  --> $DIR/match_expr_like_matches_macro.rs:10:14
 +   |
 +LL |       let _y = match x {
 +   |  ______________^
 +LL | |         Some(0) => true,
 +LL | |         _ => false,
 +LL | |     };
 +   | |_____^ help: try this: `matches!(x, Some(0))`
 +   |
 +   = note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
 +
 +error: match expression looks like `matches!` macro
 +  --> $DIR/match_expr_like_matches_macro.rs:16:14
 +   |
 +LL |       let _w = match x {
 +   |  ______________^
 +LL | |         Some(_) => true,
 +LL | |         _ => false,
 +LL | |     };
 +   | |_____^ help: try this: `matches!(x, Some(_))`
 +
 +error: redundant pattern matching, consider using `is_none()`
 +  --> $DIR/match_expr_like_matches_macro.rs:22:14
 +   |
 +LL |       let _z = match x {
 +   |  ______________^
 +LL | |         Some(_) => false,
 +LL | |         None => true,
 +LL | |     };
 +   | |_____^ help: try this: `x.is_none()`
 +   |
 +   = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
 +
 +error: match expression looks like `matches!` macro
 +  --> $DIR/match_expr_like_matches_macro.rs:28:15
 +   |
 +LL |       let _zz = match x {
 +   |  _______________^
 +LL | |         Some(r) if r == 0 => false,
 +LL | |         _ => true,
 +LL | |     };
 +   | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)`
 +
 +error: if let .. else expression looks like `matches!` macro
 +  --> $DIR/match_expr_like_matches_macro.rs:34:16
 +   |
 +LL |     let _zzz = if let Some(5) = x { true } else { false };
 +   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))`
 +
 +error: match expression looks like `matches!` macro
 +  --> $DIR/match_expr_like_matches_macro.rs:58:20
 +   |
 +LL |           let _ans = match x {
 +   |  ____________________^
 +LL | |             E::A(_) => true,
 +LL | |             E::B(_) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
 +
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:126:18
++  --> $DIR/match_expr_like_matches_macro.rs:68: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:78: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:135:18
++  --> $DIR/match_expr_like_matches_macro.rs:138:18
 +   |
 +LL |           let _z = match &z {
 +   |  __________________^
 +LL | |             Some(3) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(z, Some(3))`
 +
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:152:21
++  --> $DIR/match_expr_like_matches_macro.rs:147: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:166:20
++  --> $DIR/match_expr_like_matches_macro.rs:164: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:178:20
++  --> $DIR/match_expr_like_matches_macro.rs:178:20
 +   |
 +LL |           let _res = match &val {
 +   |  ____________________^
 +LL | |             &Some(ref _a) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(&val, &Some(ref _a))`
 +
 +error: match expression looks like `matches!` macro
- error: aborting due to 12 previous errors
++  --> $DIR/match_expr_like_matches_macro.rs:190:20
 +   |
 +LL |           let _res = match &val {
 +   |  ____________________^
 +LL | |             &Some(ref _a) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(&val, &Some(ref _a))`
 +
++error: aborting due to 13 previous errors
 +
index cb005122436019f2a2be9ac1c8fb219d8257e534,0000000000000000000000000000000000000000..09afe2ddbbf6289e21d64cb36e1dea36053b51a0
mode 100644,000000..100644
--- /dev/null
@@@ -1,148 -1,0 +1,160 @@@
 +// run-rustfix
 +
 +#![feature(lint_reasons)]
 +
 +#[warn(clippy::all, clippy::needless_borrow)]
 +#[allow(unused_variables, clippy::unnecessary_mut_passed)]
 +fn main() {
 +    let a = 5;
 +    let ref_a = &a;
 +    let _ = x(&a); // no warning
 +    let _ = x(&a); // warn
 +
 +    let mut b = 5;
 +    mut_ref(&mut b); // no warning
 +    mut_ref(&mut b); // warn
 +
 +    let s = &String::from("hi");
 +    let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
 +    let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
 +    let vec = Vec::new();
 +    let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
 +    h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
 +    let garbl = match 42 {
 +        44 => &a,
 +        45 => {
 +            println!("foo");
 +            &a
 +        },
 +        46 => &a,
 +        47 => {
 +            println!("foo");
 +            loop {
 +                println!("{}", a);
 +                if a == 25 {
 +                    break ref_a;
 +                }
 +            }
 +        },
 +        _ => panic!(),
 +    };
 +
 +    let _ = x(&a);
 +    let _ = x(&a);
 +    let _ = x(&mut b);
 +    let _ = x(ref_a);
 +    {
 +        let b = &mut b;
 +        x(b);
 +    }
 +
 +    // Issue #8191
 +    let mut x = 5;
 +    let mut x = &mut x;
 +
 +    mut_ref(x);
 +    mut_ref(x);
 +    let y: &mut i32 = x;
 +    let y: &mut i32 = x;
 +
 +    let y = match 0 {
 +        // Don't lint. Removing the borrow would move 'x'
 +        0 => &mut x,
 +        _ => &mut *x,
 +    };
 +    let y: &mut i32 = match 0 {
 +        // Lint here. The type given above triggers auto-borrow.
 +        0 => x,
 +        _ => &mut *x,
 +    };
 +    fn ref_mut_i32(_: &mut i32) {}
 +    ref_mut_i32(match 0 {
 +        // Lint here. The type given above triggers auto-borrow.
 +        0 => x,
 +        _ => &mut *x,
 +    });
 +    // use 'x' after to make sure it's still usable in the fixed code.
 +    *x = 5;
 +
 +    let s = String::new();
 +    // let _ = (&s).len();
 +    // let _ = (&s).capacity();
 +    // let _ = (&&s).capacity();
 +
 +    let x = (1, 2);
 +    let _ = x.0;
 +    let x = &x as *const (i32, i32);
 +    let _ = unsafe { (*x).0 };
 +
 +    // Issue #8367
 +    trait Foo {
 +        fn foo(self);
 +    }
 +    impl Foo for &'_ () {
 +        fn foo(self) {}
 +    }
 +    (&()).foo(); // Don't lint. `()` doesn't implement `Foo`
 +    (&()).foo();
 +
 +    impl Foo for i32 {
 +        fn foo(self) {}
 +    }
 +    impl Foo for &'_ i32 {
 +        fn foo(self) {}
 +    }
 +    (&5).foo(); // Don't lint. `5` will call `<i32 as Foo>::foo`
 +    (&5).foo();
 +
 +    trait FooRef {
 +        fn foo_ref(&self);
 +    }
 +    impl FooRef for () {
 +        fn foo_ref(&self) {}
 +    }
 +    impl FooRef for &'_ () {
 +        fn foo_ref(&self) {}
 +    }
 +    (&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
++
++    struct S;
++    impl From<S> for u32 {
++        fn from(s: S) -> Self {
++            (&s).into()
++        }
++    }
++    impl From<&S> for u32 {
++        fn from(s: &S) -> Self {
++            0
++        }
++    }
 +}
 +
 +#[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);
 +}
index d636a40100378c0f5bebc5cbb3f85b9f902ab365,0000000000000000000000000000000000000000..3ae4722a1f8985e414ea3a9d49f85ccb397bf770
mode 100644,000000..100644
--- /dev/null
@@@ -1,148 -1,0 +1,160 @@@
 +// run-rustfix
 +
 +#![feature(lint_reasons)]
 +
 +#[warn(clippy::all, clippy::needless_borrow)]
 +#[allow(unused_variables, clippy::unnecessary_mut_passed)]
 +fn main() {
 +    let a = 5;
 +    let ref_a = &a;
 +    let _ = x(&a); // no warning
 +    let _ = x(&&a); // warn
 +
 +    let mut b = 5;
 +    mut_ref(&mut b); // no warning
 +    mut_ref(&mut &mut b); // warn
 +
 +    let s = &String::from("hi");
 +    let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
 +    let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
 +    let vec = Vec::new();
 +    let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
 +    h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
 +    let garbl = match 42 {
 +        44 => &a,
 +        45 => {
 +            println!("foo");
 +            &&a
 +        },
 +        46 => &&a,
 +        47 => {
 +            println!("foo");
 +            loop {
 +                println!("{}", a);
 +                if a == 25 {
 +                    break &ref_a;
 +                }
 +            }
 +        },
 +        _ => panic!(),
 +    };
 +
 +    let _ = x(&&&a);
 +    let _ = x(&mut &&a);
 +    let _ = x(&&&mut b);
 +    let _ = x(&&ref_a);
 +    {
 +        let b = &mut b;
 +        x(&b);
 +    }
 +
 +    // Issue #8191
 +    let mut x = 5;
 +    let mut x = &mut x;
 +
 +    mut_ref(&mut x);
 +    mut_ref(&mut &mut x);
 +    let y: &mut i32 = &mut x;
 +    let y: &mut i32 = &mut &mut x;
 +
 +    let y = match 0 {
 +        // Don't lint. Removing the borrow would move 'x'
 +        0 => &mut x,
 +        _ => &mut *x,
 +    };
 +    let y: &mut i32 = match 0 {
 +        // Lint here. The type given above triggers auto-borrow.
 +        0 => &mut x,
 +        _ => &mut *x,
 +    };
 +    fn ref_mut_i32(_: &mut i32) {}
 +    ref_mut_i32(match 0 {
 +        // Lint here. The type given above triggers auto-borrow.
 +        0 => &mut x,
 +        _ => &mut *x,
 +    });
 +    // use 'x' after to make sure it's still usable in the fixed code.
 +    *x = 5;
 +
 +    let s = String::new();
 +    // let _ = (&s).len();
 +    // let _ = (&s).capacity();
 +    // let _ = (&&s).capacity();
 +
 +    let x = (1, 2);
 +    let _ = (&x).0;
 +    let x = &x as *const (i32, i32);
 +    let _ = unsafe { (&*x).0 };
 +
 +    // Issue #8367
 +    trait Foo {
 +        fn foo(self);
 +    }
 +    impl Foo for &'_ () {
 +        fn foo(self) {}
 +    }
 +    (&()).foo(); // Don't lint. `()` doesn't implement `Foo`
 +    (&&()).foo();
 +
 +    impl Foo for i32 {
 +        fn foo(self) {}
 +    }
 +    impl Foo for &'_ i32 {
 +        fn foo(self) {}
 +    }
 +    (&5).foo(); // Don't lint. `5` will call `<i32 as Foo>::foo`
 +    (&&5).foo();
 +
 +    trait FooRef {
 +        fn foo_ref(&self);
 +    }
 +    impl FooRef for () {
 +        fn foo_ref(&self) {}
 +    }
 +    impl FooRef for &'_ () {
 +        fn foo_ref(&self) {}
 +    }
 +    (&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
++
++    struct S;
++    impl From<S> for u32 {
++        fn from(s: S) -> Self {
++            (&s).into()
++        }
++    }
++    impl From<&S> for u32 {
++        fn from(s: &S) -> Self {
++            0
++        }
++    }
 +}
 +
 +#[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);
 +}
index b997e5316cf3f451442295cc4a776f4bf41a8c3f,0000000000000000000000000000000000000000..0c9178fb85efe39a0a3e7938e658b162fcb2efc1
mode 100644,000000..100644
--- /dev/null
@@@ -1,209 -1,0 +1,210 @@@
 +// run-rustfix
 +#![warn(clippy::needless_match)]
 +#![allow(clippy::manual_map)]
 +#![allow(dead_code)]
 +
 +#[derive(Clone, Copy)]
 +enum Simple {
 +    A,
 +    B,
 +    C,
 +    D,
 +}
 +
 +fn useless_match() {
 +    let i = 10;
 +    let _: i32 = i;
 +    let s = "test";
 +    let _: &str = s;
 +}
 +
 +fn custom_type_match() {
 +    let se = Simple::A;
 +    let _: Simple = se;
 +    // Don't trigger
 +    let _: Simple = match se {
 +        Simple::A => Simple::A,
 +        Simple::B => Simple::B,
 +        _ => Simple::C,
 +    };
 +    // Mingled, don't trigger
 +    let _: Simple = match se {
 +        Simple::A => Simple::B,
 +        Simple::B => Simple::C,
 +        Simple::C => Simple::D,
 +        Simple::D => Simple::A,
 +    };
 +}
 +
 +fn option_match(x: Option<i32>) {
 +    let _: Option<i32> = x;
 +    // Don't trigger, this is the case for manual_map_option
 +    let _: Option<i32> = match x {
 +        Some(a) => Some(-a),
 +        None => None,
 +    };
 +}
 +
 +fn func_ret_err<T>(err: T) -> Result<i32, T> {
 +    Err(err)
 +}
 +
 +fn result_match() {
 +    let _: Result<i32, i32> = Ok(1);
 +    let _: Result<i32, i32> = func_ret_err(0_i32);
 +    // as ref, don't trigger
 +    let res = &func_ret_err(0_i32);
 +    let _: Result<&i32, &i32> = match *res {
 +        Ok(ref x) => Ok(x),
 +        Err(ref x) => Err(x),
 +    };
 +}
 +
 +fn if_let_option() {
 +    let _ = Some(1);
 +
 +    fn do_something() {}
 +
 +    // Don't trigger
 +    let _ = if let Some(a) = Some(1) {
 +        Some(a)
 +    } else {
 +        do_something();
 +        None
 +    };
 +
 +    // Don't trigger
 +    let _ = if let Some(a) = Some(1) {
 +        do_something();
 +        Some(a)
 +    } else {
 +        None
 +    };
 +
 +    // Don't trigger
 +    let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) };
 +}
 +
 +fn if_let_option_result() -> Result<(), ()> {
 +    fn f(x: i32) -> Result<Option<i32>, ()> {
 +        Ok(Some(x))
 +    }
 +    // Don't trigger
 +    let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? };
 +    Ok(())
 +}
 +
 +fn if_let_result() {
 +    let x: Result<i32, i32> = Ok(1);
 +    let _: Result<i32, i32> = x;
 +    let _: Result<i32, i32> = x;
 +    // Input type mismatch, don't trigger
++    #[allow(clippy::question_mark)]
 +    let _: Result<i32, i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
 +}
 +
 +fn if_let_custom_enum(x: Simple) {
 +    let _: Simple = x;
 +
 +    // Don't trigger
 +    let _: Simple = if let Simple::A = x {
 +        Simple::A
 +    } else if true {
 +        Simple::B
 +    } else {
 +        x
 +    };
 +}
 +
 +mod issue8542 {
 +    #[derive(Clone, Copy)]
 +    enum E {
 +        VariantA(u8, u8),
 +        VariantB(u8, bool),
 +    }
 +
 +    enum Complex {
 +        A(u8),
 +        B(u8, bool),
 +        C(u8, i32, f64),
 +        D(E, bool),
 +    }
 +
 +    fn match_test() {
 +        let ce = Complex::B(8, false);
 +        let aa = 0_u8;
 +        let bb = false;
 +
 +        let _: Complex = ce;
 +
 +        // Don't trigger
 +        let _: Complex = match ce {
 +            Complex::A(_) => Complex::A(aa),
 +            Complex::B(_, b) => Complex::B(aa, b),
 +            Complex::C(_, b, _) => Complex::C(aa, b, 64_f64),
 +            Complex::D(e, b) => Complex::D(e, b),
 +        };
 +
 +        // Don't trigger
 +        let _: Complex = match ce {
 +            Complex::A(a) => Complex::A(a),
 +            Complex::B(a, _) => Complex::B(a, bb),
 +            Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64),
 +            _ => ce,
 +        };
 +    }
 +}
 +
 +/// Lint triggered when type coercions happen.
 +/// Do NOT trigger on any of these.
 +mod issue8551 {
 +    trait Trait {}
 +    struct Struct;
 +    impl Trait for Struct {}
 +
 +    fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> {
 +        match s {
 +            Some(s) => Some(s),
 +            None => None,
 +        }
 +    }
 +
 +    fn lint_tests() {
 +        let option: Option<&Struct> = None;
 +        let _: Option<&dyn Trait> = match option {
 +            Some(s) => Some(s),
 +            None => None,
 +        };
 +
 +        let _: Option<&dyn Trait> = if true {
 +            match option {
 +                Some(s) => Some(s),
 +                None => None,
 +            }
 +        } else {
 +            None
 +        };
 +
 +        let result: Result<&Struct, i32> = Err(0);
 +        let _: Result<&dyn Trait, i32> = match result {
 +            Ok(s) => Ok(s),
 +            Err(e) => Err(e),
 +        };
 +
 +        let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None };
 +    }
 +}
 +
 +trait Tr {
 +    fn as_mut(&mut self) -> Result<&mut i32, &mut i32>;
 +}
 +impl Tr for Result<i32, i32> {
 +    fn as_mut(&mut self) -> Result<&mut i32, &mut i32> {
 +        match self {
 +            Ok(x) => Ok(x),
 +            Err(e) => Err(e),
 +        }
 +    }
 +}
 +
 +fn main() {}
index 90482775a1eebbf7d1fa1621b3c3a233b3739d5a,0000000000000000000000000000000000000000..f66f01d7ccaf4ef2f96be1e2e2247cebf83884f2
mode 100644,000000..100644
--- /dev/null
@@@ -1,246 -1,0 +1,247 @@@
 +// run-rustfix
 +#![warn(clippy::needless_match)]
 +#![allow(clippy::manual_map)]
 +#![allow(dead_code)]
 +
 +#[derive(Clone, Copy)]
 +enum Simple {
 +    A,
 +    B,
 +    C,
 +    D,
 +}
 +
 +fn useless_match() {
 +    let i = 10;
 +    let _: i32 = match i {
 +        0 => 0,
 +        1 => 1,
 +        2 => 2,
 +        _ => i,
 +    };
 +    let s = "test";
 +    let _: &str = match s {
 +        "a" => "a",
 +        "b" => "b",
 +        s => s,
 +    };
 +}
 +
 +fn custom_type_match() {
 +    let se = Simple::A;
 +    let _: Simple = match se {
 +        Simple::A => Simple::A,
 +        Simple::B => Simple::B,
 +        Simple::C => Simple::C,
 +        Simple::D => Simple::D,
 +    };
 +    // Don't trigger
 +    let _: Simple = match se {
 +        Simple::A => Simple::A,
 +        Simple::B => Simple::B,
 +        _ => Simple::C,
 +    };
 +    // Mingled, don't trigger
 +    let _: Simple = match se {
 +        Simple::A => Simple::B,
 +        Simple::B => Simple::C,
 +        Simple::C => Simple::D,
 +        Simple::D => Simple::A,
 +    };
 +}
 +
 +fn option_match(x: Option<i32>) {
 +    let _: Option<i32> = match x {
 +        Some(a) => Some(a),
 +        None => None,
 +    };
 +    // Don't trigger, this is the case for manual_map_option
 +    let _: Option<i32> = match x {
 +        Some(a) => Some(-a),
 +        None => None,
 +    };
 +}
 +
 +fn func_ret_err<T>(err: T) -> Result<i32, T> {
 +    Err(err)
 +}
 +
 +fn result_match() {
 +    let _: Result<i32, i32> = match Ok(1) {
 +        Ok(a) => Ok(a),
 +        Err(err) => Err(err),
 +    };
 +    let _: Result<i32, i32> = match func_ret_err(0_i32) {
 +        Err(err) => Err(err),
 +        Ok(a) => Ok(a),
 +    };
 +    // as ref, don't trigger
 +    let res = &func_ret_err(0_i32);
 +    let _: Result<&i32, &i32> = match *res {
 +        Ok(ref x) => Ok(x),
 +        Err(ref x) => Err(x),
 +    };
 +}
 +
 +fn if_let_option() {
 +    let _ = if let Some(a) = Some(1) { Some(a) } else { None };
 +
 +    fn do_something() {}
 +
 +    // Don't trigger
 +    let _ = if let Some(a) = Some(1) {
 +        Some(a)
 +    } else {
 +        do_something();
 +        None
 +    };
 +
 +    // Don't trigger
 +    let _ = if let Some(a) = Some(1) {
 +        do_something();
 +        Some(a)
 +    } else {
 +        None
 +    };
 +
 +    // Don't trigger
 +    let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) };
 +}
 +
 +fn if_let_option_result() -> Result<(), ()> {
 +    fn f(x: i32) -> Result<Option<i32>, ()> {
 +        Ok(Some(x))
 +    }
 +    // Don't trigger
 +    let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? };
 +    Ok(())
 +}
 +
 +fn if_let_result() {
 +    let x: Result<i32, i32> = Ok(1);
 +    let _: Result<i32, i32> = if let Err(e) = x { Err(e) } else { x };
 +    let _: Result<i32, i32> = if let Ok(val) = x { Ok(val) } else { x };
 +    // Input type mismatch, don't trigger
++    #[allow(clippy::question_mark)]
 +    let _: Result<i32, i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
 +}
 +
 +fn if_let_custom_enum(x: Simple) {
 +    let _: Simple = if let Simple::A = x {
 +        Simple::A
 +    } else if let Simple::B = x {
 +        Simple::B
 +    } else if let Simple::C = x {
 +        Simple::C
 +    } else {
 +        x
 +    };
 +
 +    // Don't trigger
 +    let _: Simple = if let Simple::A = x {
 +        Simple::A
 +    } else if true {
 +        Simple::B
 +    } else {
 +        x
 +    };
 +}
 +
 +mod issue8542 {
 +    #[derive(Clone, Copy)]
 +    enum E {
 +        VariantA(u8, u8),
 +        VariantB(u8, bool),
 +    }
 +
 +    enum Complex {
 +        A(u8),
 +        B(u8, bool),
 +        C(u8, i32, f64),
 +        D(E, bool),
 +    }
 +
 +    fn match_test() {
 +        let ce = Complex::B(8, false);
 +        let aa = 0_u8;
 +        let bb = false;
 +
 +        let _: Complex = match ce {
 +            Complex::A(a) => Complex::A(a),
 +            Complex::B(a, b) => Complex::B(a, b),
 +            Complex::C(a, b, c) => Complex::C(a, b, c),
 +            Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b),
 +            Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b),
 +        };
 +
 +        // Don't trigger
 +        let _: Complex = match ce {
 +            Complex::A(_) => Complex::A(aa),
 +            Complex::B(_, b) => Complex::B(aa, b),
 +            Complex::C(_, b, _) => Complex::C(aa, b, 64_f64),
 +            Complex::D(e, b) => Complex::D(e, b),
 +        };
 +
 +        // Don't trigger
 +        let _: Complex = match ce {
 +            Complex::A(a) => Complex::A(a),
 +            Complex::B(a, _) => Complex::B(a, bb),
 +            Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64),
 +            _ => ce,
 +        };
 +    }
 +}
 +
 +/// Lint triggered when type coercions happen.
 +/// Do NOT trigger on any of these.
 +mod issue8551 {
 +    trait Trait {}
 +    struct Struct;
 +    impl Trait for Struct {}
 +
 +    fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> {
 +        match s {
 +            Some(s) => Some(s),
 +            None => None,
 +        }
 +    }
 +
 +    fn lint_tests() {
 +        let option: Option<&Struct> = None;
 +        let _: Option<&dyn Trait> = match option {
 +            Some(s) => Some(s),
 +            None => None,
 +        };
 +
 +        let _: Option<&dyn Trait> = if true {
 +            match option {
 +                Some(s) => Some(s),
 +                None => None,
 +            }
 +        } else {
 +            None
 +        };
 +
 +        let result: Result<&Struct, i32> = Err(0);
 +        let _: Result<&dyn Trait, i32> = match result {
 +            Ok(s) => Ok(s),
 +            Err(e) => Err(e),
 +        };
 +
 +        let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None };
 +    }
 +}
 +
 +trait Tr {
 +    fn as_mut(&mut self) -> Result<&mut i32, &mut i32>;
 +}
 +impl Tr for Result<i32, i32> {
 +    fn as_mut(&mut self) -> Result<&mut i32, &mut i32> {
 +        match self {
 +            Ok(x) => Ok(x),
 +            Err(e) => Err(e),
 +        }
 +    }
 +}
 +
 +fn main() {}
index 2d679631c6f0d71d9da899d9966bb3bb8e595e3c,0000000000000000000000000000000000000000..5bc79800a1a748bcbbb696e6fa824c791af3e647
mode 100644,000000..100644
--- /dev/null
@@@ -1,113 -1,0 +1,113 @@@
-   --> $DIR/needless_match.rs:129:21
 +error: this match expression is unnecessary
 +  --> $DIR/needless_match.rs:16:18
 +   |
 +LL |       let _: i32 = match i {
 +   |  __________________^
 +LL | |         0 => 0,
 +LL | |         1 => 1,
 +LL | |         2 => 2,
 +LL | |         _ => i,
 +LL | |     };
 +   | |_____^ help: replace it with: `i`
 +   |
 +   = note: `-D clippy::needless-match` implied by `-D warnings`
 +
 +error: this match expression is unnecessary
 +  --> $DIR/needless_match.rs:23:19
 +   |
 +LL |       let _: &str = match s {
 +   |  ___________________^
 +LL | |         "a" => "a",
 +LL | |         "b" => "b",
 +LL | |         s => s,
 +LL | |     };
 +   | |_____^ help: replace it with: `s`
 +
 +error: this match expression is unnecessary
 +  --> $DIR/needless_match.rs:32:21
 +   |
 +LL |       let _: Simple = match se {
 +   |  _____________________^
 +LL | |         Simple::A => Simple::A,
 +LL | |         Simple::B => Simple::B,
 +LL | |         Simple::C => Simple::C,
 +LL | |         Simple::D => Simple::D,
 +LL | |     };
 +   | |_____^ help: replace it with: `se`
 +
 +error: this match expression is unnecessary
 +  --> $DIR/needless_match.rs:54:26
 +   |
 +LL |       let _: Option<i32> = match x {
 +   |  __________________________^
 +LL | |         Some(a) => Some(a),
 +LL | |         None => None,
 +LL | |     };
 +   | |_____^ help: replace it with: `x`
 +
 +error: this match expression is unnecessary
 +  --> $DIR/needless_match.rs:70:31
 +   |
 +LL |       let _: Result<i32, i32> = match Ok(1) {
 +   |  _______________________________^
 +LL | |         Ok(a) => Ok(a),
 +LL | |         Err(err) => Err(err),
 +LL | |     };
 +   | |_____^ help: replace it with: `Ok(1)`
 +
 +error: this match expression is unnecessary
 +  --> $DIR/needless_match.rs:74:31
 +   |
 +LL |       let _: Result<i32, i32> = match func_ret_err(0_i32) {
 +   |  _______________________________^
 +LL | |         Err(err) => Err(err),
 +LL | |         Ok(a) => Ok(a),
 +LL | |     };
 +   | |_____^ help: replace it with: `func_ret_err(0_i32)`
 +
 +error: this if-let expression is unnecessary
 +  --> $DIR/needless_match.rs:87:13
 +   |
 +LL |     let _ = if let Some(a) = Some(1) { Some(a) } else { None };
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)`
 +
 +error: this if-let expression is unnecessary
 +  --> $DIR/needless_match.rs:122:31
 +   |
 +LL |     let _: Result<i32, i32> = if let Err(e) = x { Err(e) } else { x };
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
 +
 +error: this if-let expression is unnecessary
 +  --> $DIR/needless_match.rs:123:31
 +   |
 +LL |     let _: Result<i32, i32> = if let Ok(val) = x { Ok(val) } else { x };
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
 +
 +error: this if-let expression is unnecessary
-   --> $DIR/needless_match.rs:168:26
++  --> $DIR/needless_match.rs:130:21
 +   |
 +LL |       let _: Simple = if let Simple::A = x {
 +   |  _____________________^
 +LL | |         Simple::A
 +LL | |     } else if let Simple::B = x {
 +LL | |         Simple::B
 +...  |
 +LL | |         x
 +LL | |     };
 +   | |_____^ help: replace it with: `x`
 +
 +error: this match expression is unnecessary
++  --> $DIR/needless_match.rs:169:26
 +   |
 +LL |           let _: Complex = match ce {
 +   |  __________________________^
 +LL | |             Complex::A(a) => Complex::A(a),
 +LL | |             Complex::B(a, b) => Complex::B(a, b),
 +LL | |             Complex::C(a, b, c) => Complex::C(a, b, c),
 +LL | |             Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b),
 +LL | |             Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b),
 +LL | |         };
 +   | |_________^ help: replace it with: `ce`
 +
 +error: aborting due to 11 previous errors
 +
index 538927b18055a289c771f5cb1cfdc1ba9bfad531,0000000000000000000000000000000000000000..65809023f8dff129d3d3d734c72d09def63b74c9
mode 100644,000000..100644
--- /dev/null
@@@ -1,214 -1,0 +1,228 @@@
 +#![allow(dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes)]
 +#![warn(clippy::new_without_default)]
 +
 +pub struct Foo;
 +
 +impl Foo {
 +    pub fn new() -> Foo {
 +        Foo
 +    }
 +}
 +
 +pub struct Bar;
 +
 +impl Bar {
 +    pub fn new() -> Self {
 +        Bar
 +    }
 +}
 +
 +pub struct Ok;
 +
 +impl Ok {
 +    pub fn new() -> Self {
 +        Ok
 +    }
 +}
 +
 +impl Default for Ok {
 +    fn default() -> Self {
 +        Ok
 +    }
 +}
 +
 +pub struct Params;
 +
 +impl Params {
 +    pub fn new(_: u32) -> Self {
 +        Params
 +    }
 +}
 +
 +pub struct GenericsOk<T> {
 +    bar: T,
 +}
 +
 +impl<U> Default for GenericsOk<U> {
 +    fn default() -> Self {
 +        unimplemented!();
 +    }
 +}
 +
 +impl<'c, V> GenericsOk<V> {
 +    pub fn new() -> GenericsOk<V> {
 +        unimplemented!()
 +    }
 +}
 +
 +pub struct LtOk<'a> {
 +    foo: &'a bool,
 +}
 +
 +impl<'b> Default for LtOk<'b> {
 +    fn default() -> Self {
 +        unimplemented!();
 +    }
 +}
 +
 +impl<'c> LtOk<'c> {
 +    pub fn new() -> LtOk<'c> {
 +        unimplemented!()
 +    }
 +}
 +
 +pub struct LtKo<'a> {
 +    foo: &'a bool,
 +}
 +
 +impl<'c> LtKo<'c> {
 +    pub fn new() -> LtKo<'c> {
 +        unimplemented!()
 +    }
 +    // FIXME: that suggestion is missing lifetimes
 +}
 +
 +struct Private;
 +
 +impl Private {
 +    fn new() -> Private {
 +        unimplemented!()
 +    } // We don't lint private items
 +}
 +
 +struct PrivateStruct;
 +
 +impl PrivateStruct {
 +    pub fn new() -> PrivateStruct {
 +        unimplemented!()
 +    } // We don't lint public items on private structs
 +}
 +
 +pub struct PrivateItem;
 +
 +impl PrivateItem {
 +    fn new() -> PrivateItem {
 +        unimplemented!()
 +    } // We don't lint private items on public structs
 +}
 +
 +struct Const;
 +
 +impl Const {
 +    pub const fn new() -> Const {
 +        Const
 +    } // const fns can't be implemented via Default
 +}
 +
 +pub struct IgnoreGenericNew;
 +
 +impl IgnoreGenericNew {
 +    pub fn new<T>() -> Self {
 +        IgnoreGenericNew
 +    } // the derived Default does not make sense here as the result depends on T
 +}
 +
 +pub trait TraitWithNew: Sized {
 +    fn new() -> Self {
 +        panic!()
 +    }
 +}
 +
 +pub struct IgnoreUnsafeNew;
 +
 +impl IgnoreUnsafeNew {
 +    pub unsafe fn new() -> Self {
 +        IgnoreUnsafeNew
 +    }
 +}
 +
 +#[derive(Default)]
 +pub struct OptionRefWrapper<'a, T>(Option<&'a T>);
 +
 +impl<'a, T> OptionRefWrapper<'a, T> {
 +    pub fn new() -> Self {
 +        OptionRefWrapper(None)
 +    }
 +}
 +
 +pub struct Allow(Foo);
 +
 +impl Allow {
 +    #[allow(clippy::new_without_default)]
 +    pub fn new() -> Self {
 +        unimplemented!()
 +    }
 +}
 +
 +pub struct AllowDerive;
 +
 +impl AllowDerive {
 +    #[allow(clippy::new_without_default)]
 +    pub fn new() -> Self {
 +        unimplemented!()
 +    }
 +}
 +
 +pub struct NewNotEqualToDerive {
 +    foo: i32,
 +}
 +
 +impl NewNotEqualToDerive {
 +    // This `new` implementation is not equal to a derived `Default`, so do not suggest deriving.
 +    pub fn new() -> Self {
 +        NewNotEqualToDerive { foo: 1 }
 +    }
 +}
 +
 +// see #6933
 +pub struct FooGenerics<T>(std::marker::PhantomData<T>);
 +impl<T> FooGenerics<T> {
 +    pub fn new() -> Self {
 +        Self(Default::default())
 +    }
 +}
 +
 +pub struct BarGenerics<T>(std::marker::PhantomData<T>);
 +impl<T: Copy> BarGenerics<T> {
 +    pub fn new() -> Self {
 +        Self(Default::default())
 +    }
 +}
 +
 +pub mod issue7220 {
 +    pub struct Foo<T> {
 +        _bar: *mut T,
 +    }
 +
 +    impl<T> Foo<T> {
 +        pub fn new() -> Self {
 +            todo!()
 +        }
 +    }
 +}
 +
 +// see issue #8152
 +// This should not create any lints
 +pub struct DocHidden;
 +impl DocHidden {
 +    #[doc(hidden)]
 +    pub fn new() -> Self {
 +        DocHidden
 +    }
 +}
 +
 +fn main() {}
++
++pub struct IgnoreConstGenericNew(usize);
++impl IgnoreConstGenericNew {
++    pub fn new<const N: usize>() -> Self {
++        Self(N)
++    }
++}
++
++pub struct IgnoreLifetimeNew;
++impl IgnoreLifetimeNew {
++    pub fn new<'a>() -> Self {
++        Self
++    }
++}
index f0c59b4080be3ca78c60631c67ec214afaa9acce,0000000000000000000000000000000000000000..fdefb11ae17a543f4f4c1cabdd4a1d88eddb36ad
mode 100644,000000..100644
--- /dev/null
@@@ -1,144 -1,0 +1,143 @@@
 +#![feature(box_syntax, fn_traits, unboxed_closures)]
 +#![warn(clippy::no_effect_underscore_binding)]
 +#![allow(dead_code)]
 +#![allow(path_statements)]
 +#![allow(clippy::deref_addrof)]
 +#![allow(clippy::redundant_field_names)]
 +
 +struct Unit;
 +struct Tuple(i32);
 +struct Struct {
 +    field: i32,
 +}
 +enum Enum {
 +    Tuple(i32),
 +    Struct { field: i32 },
 +}
 +struct DropUnit;
 +impl Drop for DropUnit {
 +    fn drop(&mut self) {}
 +}
 +struct DropStruct {
 +    field: i32,
 +}
 +impl Drop for DropStruct {
 +    fn drop(&mut self) {}
 +}
 +struct DropTuple(i32);
 +impl Drop for DropTuple {
 +    fn drop(&mut self) {}
 +}
 +enum DropEnum {
 +    Tuple(i32),
 +    Struct { field: i32 },
 +}
 +impl Drop for DropEnum {
 +    fn drop(&mut self) {}
 +}
 +struct FooString {
 +    s: String,
 +}
 +union Union {
 +    a: u8,
 +    b: f64,
 +}
 +
 +fn get_number() -> i32 {
 +    0
 +}
 +fn get_struct() -> Struct {
 +    Struct { field: 0 }
 +}
 +fn get_drop_struct() -> DropStruct {
 +    DropStruct { field: 0 }
 +}
 +
 +unsafe fn unsafe_fn() -> i32 {
 +    0
 +}
 +
 +struct GreetStruct1;
 +
 +impl FnOnce<(&str,)> for GreetStruct1 {
 +    type Output = ();
 +
 +    extern "rust-call" fn call_once(self, (who,): (&str,)) -> Self::Output {
 +        println!("hello {}", who);
 +    }
 +}
 +
 +struct GreetStruct2();
 +
 +impl FnOnce<(&str,)> for GreetStruct2 {
 +    type Output = ();
 +
 +    extern "rust-call" fn call_once(self, (who,): (&str,)) -> Self::Output {
 +        println!("hello {}", who);
 +    }
 +}
 +
 +struct GreetStruct3;
 +
 +impl FnOnce<(&str,)> for GreetStruct3 {
 +    type Output = ();
 +
 +    extern "rust-call" fn call_once(self, (who,): (&str,)) -> Self::Output {
 +        println!("hello {}", who);
 +    }
 +}
 +
 +fn main() {
 +    let s = get_struct();
 +    let s2 = get_struct();
 +
 +    0;
 +    s2;
 +    Unit;
 +    Tuple(0);
 +    Struct { field: 0 };
 +    Struct { ..s };
 +    Union { a: 0 };
 +    Enum::Tuple(0);
 +    Enum::Struct { field: 0 };
 +    5 + 6;
 +    *&42;
 +    &6;
 +    (5, 6, 7);
 +    box 42;
 +    ..;
 +    5..;
 +    ..5;
 +    5..6;
 +    5..=6;
 +    [42, 55];
 +    [42, 55][1];
 +    (42, 55).1;
 +    [42; 55];
 +    [42; 55][13];
 +    let mut x = 0;
 +    || x += 5;
 +    let s: String = "foo".into();
 +    FooString { s: s };
 +    let _unused = 1;
 +    let _penguin = || println!("Some helpful closure");
 +    let _duck = Struct { field: 0 };
 +    let _cat = [2, 4, 6, 8][2];
 +
 +    #[allow(clippy::no_effect)]
 +    0;
 +
 +    // Do not warn
 +    get_number();
 +    unsafe { unsafe_fn() };
 +    let _used = get_struct();
 +    let _x = vec![1];
 +    DropUnit;
 +    DropStruct { field: 0 };
 +    DropTuple(0);
 +    DropEnum::Tuple(0);
 +    DropEnum::Struct { field: 0 };
 +    GreetStruct1("world");
 +    GreetStruct2()("world");
 +    GreetStruct3 {}("world");
 +}
index 06b88bb5bee7ab5c3ef908a1a7efc66950c3bcf3,0000000000000000000000000000000000000000..328d2555ceb8e98304485abf59f08d3ceda9cfaa
mode 100644,000000..100644
--- /dev/null
@@@ -1,186 -1,0 +1,186 @@@
-   --> $DIR/no_effect.rs:95:5
 +error: statement with no effect
-   --> $DIR/no_effect.rs:96:5
++  --> $DIR/no_effect.rs:94:5
 +   |
 +LL |     0;
 +   |     ^^
 +   |
 +   = note: `-D clippy::no-effect` implied by `-D warnings`
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:97:5
++  --> $DIR/no_effect.rs:95:5
 +   |
 +LL |     s2;
 +   |     ^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:98:5
++  --> $DIR/no_effect.rs:96:5
 +   |
 +LL |     Unit;
 +   |     ^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:99:5
++  --> $DIR/no_effect.rs:97:5
 +   |
 +LL |     Tuple(0);
 +   |     ^^^^^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:100:5
++  --> $DIR/no_effect.rs:98:5
 +   |
 +LL |     Struct { field: 0 };
 +   |     ^^^^^^^^^^^^^^^^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:101:5
++  --> $DIR/no_effect.rs:99:5
 +   |
 +LL |     Struct { ..s };
 +   |     ^^^^^^^^^^^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:102:5
++  --> $DIR/no_effect.rs:100:5
 +   |
 +LL |     Union { a: 0 };
 +   |     ^^^^^^^^^^^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:103:5
++  --> $DIR/no_effect.rs:101:5
 +   |
 +LL |     Enum::Tuple(0);
 +   |     ^^^^^^^^^^^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:104:5
++  --> $DIR/no_effect.rs:102:5
 +   |
 +LL |     Enum::Struct { field: 0 };
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:105:5
++  --> $DIR/no_effect.rs:103:5
 +   |
 +LL |     5 + 6;
 +   |     ^^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:106:5
++  --> $DIR/no_effect.rs:104:5
 +   |
 +LL |     *&42;
 +   |     ^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:107:5
++  --> $DIR/no_effect.rs:105:5
 +   |
 +LL |     &6;
 +   |     ^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:108:5
++  --> $DIR/no_effect.rs:106:5
 +   |
 +LL |     (5, 6, 7);
 +   |     ^^^^^^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:109:5
++  --> $DIR/no_effect.rs:107:5
 +   |
 +LL |     box 42;
 +   |     ^^^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:110:5
++  --> $DIR/no_effect.rs:108:5
 +   |
 +LL |     ..;
 +   |     ^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:111:5
++  --> $DIR/no_effect.rs:109:5
 +   |
 +LL |     5..;
 +   |     ^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:112:5
++  --> $DIR/no_effect.rs:110:5
 +   |
 +LL |     ..5;
 +   |     ^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:113:5
++  --> $DIR/no_effect.rs:111:5
 +   |
 +LL |     5..6;
 +   |     ^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:114:5
++  --> $DIR/no_effect.rs:112:5
 +   |
 +LL |     5..=6;
 +   |     ^^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:115:5
++  --> $DIR/no_effect.rs:113:5
 +   |
 +LL |     [42, 55];
 +   |     ^^^^^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:116:5
++  --> $DIR/no_effect.rs:114:5
 +   |
 +LL |     [42, 55][1];
 +   |     ^^^^^^^^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:117:5
++  --> $DIR/no_effect.rs:115:5
 +   |
 +LL |     (42, 55).1;
 +   |     ^^^^^^^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:118:5
++  --> $DIR/no_effect.rs:116:5
 +   |
 +LL |     [42; 55];
 +   |     ^^^^^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:120:5
++  --> $DIR/no_effect.rs:117:5
 +   |
 +LL |     [42; 55][13];
 +   |     ^^^^^^^^^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:122:5
++  --> $DIR/no_effect.rs:119:5
 +   |
 +LL |     || x += 5;
 +   |     ^^^^^^^^^^
 +
 +error: statement with no effect
-   --> $DIR/no_effect.rs:123:5
++  --> $DIR/no_effect.rs:121:5
 +   |
 +LL |     FooString { s: s };
 +   |     ^^^^^^^^^^^^^^^^^^^
 +
 +error: binding to `_` prefixed variable with no side-effect
-   --> $DIR/no_effect.rs:124:5
++  --> $DIR/no_effect.rs:122:5
 +   |
 +LL |     let _unused = 1;
 +   |     ^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings`
 +
 +error: binding to `_` prefixed variable with no side-effect
-   --> $DIR/no_effect.rs:125:5
++  --> $DIR/no_effect.rs:123:5
 +   |
 +LL |     let _penguin = || println!("Some helpful closure");
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: binding to `_` prefixed variable with no side-effect
-   --> $DIR/no_effect.rs:126:5
++  --> $DIR/no_effect.rs:124:5
 +   |
 +LL |     let _duck = Struct { field: 0 };
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: binding to `_` prefixed variable with no side-effect
++  --> $DIR/no_effect.rs:125:5
 +   |
 +LL |     let _cat = [2, 4, 6, 8][2];
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: aborting due to 30 previous errors
 +
index 123aed40251e2a639a63a6b630fc6a084944e02c,0000000000000000000000000000000000000000..fdb08d953ff1dbc95263e6d41e88861310d9a6b5
mode 100644,000000..100644
--- /dev/null
@@@ -1,225 -1,0 +1,229 @@@
-             }).unwrap_or_default();
 +// run-rustfix
 +
 +#![warn(clippy::or_fun_call)]
 +#![allow(dead_code)]
 +#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)]
 +
 +use std::collections::BTreeMap;
 +use std::collections::HashMap;
 +use std::time::Duration;
 +
 +/// Checks implementation of the `OR_FUN_CALL` lint.
 +fn or_fun_call() {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo
 +        }
 +    }
 +
 +    struct FakeDefault;
 +    impl FakeDefault {
 +        fn default() -> Self {
 +            FakeDefault
 +        }
 +    }
 +
 +    impl Default for FakeDefault {
 +        fn default() -> Self {
 +            FakeDefault
 +        }
 +    }
 +
 +    enum Enum {
 +        A(i32),
 +    }
 +
 +    fn make<T>() -> T {
 +        unimplemented!();
 +    }
 +
 +    let with_enum = Some(Enum::A(1));
 +    with_enum.unwrap_or(Enum::A(5));
 +
 +    let with_const_fn = Some(Duration::from_secs(1));
 +    with_const_fn.unwrap_or(Duration::from_secs(5));
 +
 +    let with_constructor = Some(vec![1]);
 +    with_constructor.unwrap_or_else(make);
 +
 +    let with_new = Some(vec![1]);
 +    with_new.unwrap_or_default();
 +
 +    let with_const_args = Some(vec![1]);
 +    with_const_args.unwrap_or_else(|| Vec::with_capacity(12));
 +
 +    let with_err: Result<_, ()> = Ok(vec![1]);
 +    with_err.unwrap_or_else(|_| make());
 +
 +    let with_err_args: Result<_, ()> = Ok(vec![1]);
 +    with_err_args.unwrap_or_else(|_| Vec::with_capacity(12));
 +
 +    let with_default_trait = Some(1);
 +    with_default_trait.unwrap_or_default();
 +
 +    let with_default_type = Some(1);
 +    with_default_type.unwrap_or_default();
 +
 +    let self_default = None::<FakeDefault>;
 +    self_default.unwrap_or_else(<FakeDefault>::default);
 +
 +    let real_default = None::<FakeDefault>;
 +    real_default.unwrap_or_default();
 +
 +    let with_vec = Some(vec![1]);
 +    with_vec.unwrap_or_default();
 +
 +    let without_default = Some(Foo);
 +    without_default.unwrap_or_else(Foo::new);
 +
 +    let mut map = HashMap::<u64, String>::new();
 +    map.entry(42).or_insert(String::new());
 +
 +    let mut map_vec = HashMap::<u64, Vec<i32>>::new();
 +    map_vec.entry(42).or_insert(vec![]);
 +
 +    let mut btree = BTreeMap::<u64, String>::new();
 +    btree.entry(42).or_insert(String::new());
 +
 +    let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
 +    btree_vec.entry(42).or_insert(vec![]);
 +
 +    let stringy = Some(String::from(""));
 +    let _ = stringy.unwrap_or_else(|| "".to_owned());
 +
 +    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();
++            })
++            .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();
++            })
++            .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();
++            })
++            .unwrap_or_default();
 +    }
 +
 +    fn less_than_max_suggestion_highest_lines() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        let map = iter.map(|f: &String| f.to_lowercase());
 +        map.reduce(|mut acc, f| {
 +            acc.push_str(&f);
 +            acc
++        })
++        .unwrap_or_default();
 +    }
 +}
 +
 +fn main() {}
index dfe15654bc32c05db6992bfde425b6ba40915883,0000000000000000000000000000000000000000..4c5938ab88b90926c25c189063ad1b4ab1e268f5
mode 100644,000000..100644
--- /dev/null
@@@ -1,198 -1,0 +1,136 @@@
-   --> $DIR/or_fun_call.rs:52:5
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:49:22
 +   |
 +LL |     with_constructor.unwrap_or(make());
 +   |                      ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)`
 +   |
 +   = note: `-D clippy::or-fun-call` implied by `-D warnings`
 +
 +error: use of `unwrap_or` followed by a call to `new`
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_new.unwrap_or_default()`
++  --> $DIR/or_fun_call.rs:52:14
 +   |
 +LL |     with_new.unwrap_or(Vec::new());
-   --> $DIR/or_fun_call.rs:64:5
++   |              ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:55:21
 +   |
 +LL |     with_const_args.unwrap_or(Vec::with_capacity(12));
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Vec::with_capacity(12))`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:58:14
 +   |
 +LL |     with_err.unwrap_or(make());
 +   |              ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| make())`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:61:19
 +   |
 +LL |     with_err_args.unwrap_or(Vec::with_capacity(12));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))`
 +
 +error: use of `unwrap_or` followed by a call to `default`
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_trait.unwrap_or_default()`
++  --> $DIR/or_fun_call.rs:64:24
 +   |
 +LL |     with_default_trait.unwrap_or(Default::default());
-   --> $DIR/or_fun_call.rs:67:5
++   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `default`
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_type.unwrap_or_default()`
++  --> $DIR/or_fun_call.rs:67:23
 +   |
 +LL |     with_default_type.unwrap_or(u64::default());
-   --> $DIR/or_fun_call.rs:73:5
++   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:70:18
 +   |
 +LL |     self_default.unwrap_or(<FakeDefault>::default());
 +   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(<FakeDefault>::default)`
 +
 +error: use of `unwrap_or` followed by a call to `default`
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `real_default.unwrap_or_default()`
++  --> $DIR/or_fun_call.rs:73:18
 +   |
 +LL |     real_default.unwrap_or(<FakeDefault as Default>::default());
-   --> $DIR/or_fun_call.rs:76:5
++   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `new`
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_vec.unwrap_or_default()`
++  --> $DIR/or_fun_call.rs:76:14
 +   |
 +LL |     with_vec.unwrap_or(vec![]);
-   --> $DIR/or_fun_call.rs:182:9
-    |
- LL | /         frames
- LL | |             .iter()
- LL | |             .map(|f: &String| f.to_lowercase())
- LL | |             .reduce(|mut acc, f| {
- ...  |
- LL | |             })
- LL | |             .unwrap_or(String::new());
-    | |_____________________________________^
-    |
- help: try this
-    |
- LL ~         frames
- LL +             .iter()
- LL +             .map(|f: &String| f.to_lowercase())
- LL +             .reduce(|mut acc, f| {
- LL +                 acc.push_str(&f);
- LL +                 acc
- LL ~             }).unwrap_or_default();
++   |              ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:79:21
 +   |
 +LL |     without_default.unwrap_or(Foo::new());
 +   |                     ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:94:21
 +   |
 +LL |     let _ = stringy.unwrap_or("".to_owned());
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:102:21
 +   |
 +LL |     let _ = Some(1).unwrap_or(map[&1]);
 +   |                     ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:104:21
 +   |
 +LL |     let _ = Some(1).unwrap_or(map[&1]);
 +   |                     ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])`
 +
 +error: use of `or` followed by a function call
 +  --> $DIR/or_fun_call.rs:128:35
 +   |
 +LL |     let _ = Some("a".to_string()).or(Some("b".to_string()));
 +   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:167:14
 +   |
 +LL |         None.unwrap_or(ptr_to_ref(s));
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| ptr_to_ref(s))`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:173:14
 +   |
 +LL |         None.unwrap_or(unsafe { ptr_to_ref(s) });
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:175:14
 +   |
 +LL |         None.unwrap_or( unsafe { ptr_to_ref(s) }    );
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
 +
 +error: use of `unwrap_or` followed by a call to `new`
-   --> $DIR/or_fun_call.rs:195:9
-    |
- LL | /         iter.map(|f: &String| f.to_lowercase())
- LL | |             .reduce(|mut acc, f| {
- LL | |                 let _ = "";
- LL | |                 let _ = "";
- ...  |
- LL | |             })
- LL | |             .unwrap_or(String::new());
-    | |_____________________________________^
-    |
- help: try this
-    |
- LL ~         iter.map(|f: &String| f.to_lowercase())
- LL +             .reduce(|mut acc, f| {
- LL +                 let _ = "";
- LL +                 let _ = "";
- LL +                 acc.push_str(&f);
- LL +                 acc
- LL ~             }).unwrap_or_default();
++  --> $DIR/or_fun_call.rs:189:14
 +   |
++LL |             .unwrap_or(String::new());
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `new`
-   --> $DIR/or_fun_call.rs:208:9
-    |
- LL | /         iter.map(|f: &String| f.to_lowercase())
- LL | |             .reduce(|mut acc, f| {
- LL | |                 let _ = "";
- LL | |                 acc.push_str(&f);
- LL | |                 acc
- LL | |             })
- LL | |             .unwrap_or(String::new());
-    | |_____________________________________^
-    |
- help: try this
-    |
- LL ~         iter.map(|f: &String| f.to_lowercase())
- LL +             .reduce(|mut acc, f| {
- LL +                 let _ = "";
- LL +                 acc.push_str(&f);
- LL +                 acc
- LL ~             }).unwrap_or_default();
++  --> $DIR/or_fun_call.rs:202:14
 +   |
++LL |             .unwrap_or(String::new());
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `new`
-   --> $DIR/or_fun_call.rs:221:9
-    |
- LL | /         map.reduce(|mut acc, f| {
- LL | |             acc.push_str(&f);
- LL | |             acc
- LL | |         })
- LL | |         .unwrap_or(String::new());
-    | |_________________________________^
-    |
- help: try this
-    |
- LL ~         map.reduce(|mut acc, f| {
- LL +             acc.push_str(&f);
- LL +             acc
- LL ~         }).unwrap_or_default();
++  --> $DIR/or_fun_call.rs:214:14
 +   |
++LL |             .unwrap_or(String::new());
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `new`
++  --> $DIR/or_fun_call.rs:225:10
 +   |
++LL |         .unwrap_or(String::new());
++   |          ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: aborting due to 22 previous errors
 +
index 13ce0f32d4bb14b7e6f2777be15ee32fc6e7e52b,0000000000000000000000000000000000000000..c4c9c82143336647e2dafb04292544c81797c6d5
mode 100644,000000..100644
--- /dev/null
@@@ -1,179 -1,0 +1,210 @@@
- fn main() {
-     some_func(Some(42));
-     some_func(None);
-     some_other_func(Some(42));
 +// run-rustfix
 +#![allow(unreachable_code)]
++#![allow(dead_code)]
 +#![allow(clippy::unnecessary_wraps)]
 +
 +fn some_func(a: Option<u32>) -> Option<u32> {
 +    a?;
 +
 +    a
 +}
 +
 +fn some_other_func(a: Option<u32>) -> Option<u32> {
 +    if a.is_none() {
 +        return None;
 +    } else {
 +        return Some(0);
 +    }
 +    unreachable!()
 +}
 +
 +pub enum SeemsOption<T> {
 +    Some(T),
 +    None,
 +}
 +
 +impl<T> SeemsOption<T> {
 +    pub fn is_none(&self) -> bool {
 +        match *self {
 +            SeemsOption::None => true,
 +            SeemsOption::Some(_) => false,
 +        }
 +    }
 +}
 +
 +fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> {
 +    if a.is_none() {
 +        return SeemsOption::None;
 +    }
 +
 +    a
 +}
 +
 +pub struct CopyStruct {
 +    pub opt: Option<u32>,
 +}
 +
 +impl CopyStruct {
 +    #[rustfmt::skip]
 +    pub fn func(&self) -> Option<u32> {
 +        (self.opt)?;
 +
 +        self.opt?;
 +
 +        let _ = Some(self.opt?);
 +
 +        let _ = self.opt?;
 +
 +        self.opt
 +    }
 +}
 +
 +#[derive(Clone)]
 +pub struct MoveStruct {
 +    pub opt: Option<Vec<u32>>,
 +}
 +
 +impl MoveStruct {
 +    pub fn ref_func(&self) -> Option<Vec<u32>> {
 +        self.opt.as_ref()?;
 +
 +        self.opt.clone()
 +    }
 +
 +    pub fn mov_func_reuse(self) -> Option<Vec<u32>> {
 +        self.opt.as_ref()?;
 +
 +        self.opt
 +    }
 +
 +    pub fn mov_func_no_use(self) -> Option<Vec<u32>> {
 +        self.opt.as_ref()?;
 +        Some(Vec::new())
 +    }
 +
 +    pub fn if_let_ref_func(self) -> Option<Vec<u32>> {
 +        let v: &Vec<_> = self.opt.as_ref()?;
 +
 +        Some(v.clone())
 +    }
 +
 +    pub fn if_let_mov_func(self) -> Option<Vec<u32>> {
 +        let v = self.opt?;
 +
 +        Some(v)
 +    }
 +}
 +
 +fn func() -> Option<i32> {
 +    fn f() -> Option<String> {
 +        Some(String::new())
 +    }
 +
 +    f()?;
 +
 +    Some(0)
 +}
 +
 +fn func_returning_result() -> Result<i32, i32> {
 +    Ok(1)
 +}
 +
 +fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
 +    let _ = x?;
 +
 +    x?;
 +
 +    // No warning
 +    let y = if let Ok(x) = x {
 +        x
 +    } else {
 +        return Err(0);
 +    };
 +
 +    // issue #7859
 +    // no warning
 +    let _ = if let Ok(x) = func_returning_result() {
 +        x
 +    } else {
 +        return Err(0);
 +    };
 +
 +    // no warning
 +    if func_returning_result().is_err() {
 +        return func_returning_result();
 +    }
 +
 +    Ok(y)
 +}
 +
 +// see issue #8019
 +pub enum NotOption {
 +    None,
 +    First,
 +    AfterFirst,
 +}
 +
 +fn obj(_: i32) -> Result<(), NotOption> {
 +    Err(NotOption::First)
 +}
 +
 +fn f() -> NotOption {
 +    if obj(2).is_err() {
 +        return NotOption::None;
 +    }
 +    NotOption::First
 +}
 +
-     let copy_struct = CopyStruct { opt: Some(54) };
-     copy_struct.func();
++fn do_something() {}
 +
-     let move_struct = MoveStruct {
-         opt: Some(vec![42, 1337]),
-     };
-     move_struct.ref_func();
-     move_struct.clone().mov_func_reuse();
-     move_struct.mov_func_no_use();
++fn err_immediate_return() -> Result<i32, i32> {
++    func_returning_result()?;
++    Ok(1)
++}
 +
-     let so = SeemsOption::Some(45);
-     returns_something_similar_to_option(so);
++fn err_immediate_return_and_do_something() -> Result<i32, i32> {
++    func_returning_result()?;
++    do_something();
++    Ok(1)
++}
 +
-     func();
++// No warning
++fn no_immediate_return() -> Result<i32, i32> {
++    if let Err(err) = func_returning_result() {
++        do_something();
++        return Err(err);
++    }
++    Ok(1)
++}
 +
-     let _ = result_func(Ok(42));
-     let _ = f();
++// No warning
++fn mixed_result_and_option() -> Option<i32> {
++    if let Err(err) = func_returning_result() {
++        return Some(err);
++    }
++    None
++}
++
++// No warning
++fn else_if_check() -> Result<i32, i32> {
++    if true {
++        Ok(1)
++    } else if let Err(e) = func_returning_result() {
++        Err(e)
++    } else {
++        Err(-1)
++    }
++}
 +
++// No warning
++#[allow(clippy::manual_map)]
++#[rustfmt::skip]
++fn option_map() -> Option<bool> {
++    if let Some(a) = Some(false) {
++        Some(!a)
++    } else {
++        None
++    }
 +}
++
++fn main() {}
index 60590fd931188042efee4d99058ea3dac6ecd219,0000000000000000000000000000000000000000..cdbc7b1606f80782c7d107e213311e4b79cb6769
mode 100644,000000..100644
--- /dev/null
@@@ -1,211 -1,0 +1,246 @@@
- fn main() {
-     some_func(Some(42));
-     some_func(None);
-     some_other_func(Some(42));
 +// run-rustfix
 +#![allow(unreachable_code)]
++#![allow(dead_code)]
 +#![allow(clippy::unnecessary_wraps)]
 +
 +fn some_func(a: Option<u32>) -> Option<u32> {
 +    if a.is_none() {
 +        return None;
 +    }
 +
 +    a
 +}
 +
 +fn some_other_func(a: Option<u32>) -> Option<u32> {
 +    if a.is_none() {
 +        return None;
 +    } else {
 +        return Some(0);
 +    }
 +    unreachable!()
 +}
 +
 +pub enum SeemsOption<T> {
 +    Some(T),
 +    None,
 +}
 +
 +impl<T> SeemsOption<T> {
 +    pub fn is_none(&self) -> bool {
 +        match *self {
 +            SeemsOption::None => true,
 +            SeemsOption::Some(_) => false,
 +        }
 +    }
 +}
 +
 +fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> {
 +    if a.is_none() {
 +        return SeemsOption::None;
 +    }
 +
 +    a
 +}
 +
 +pub struct CopyStruct {
 +    pub opt: Option<u32>,
 +}
 +
 +impl CopyStruct {
 +    #[rustfmt::skip]
 +    pub fn func(&self) -> Option<u32> {
 +        if (self.opt).is_none() {
 +            return None;
 +        }
 +
 +        if self.opt.is_none() {
 +            return None
 +        }
 +
 +        let _ = if self.opt.is_none() {
 +            return None;
 +        } else {
 +            self.opt
 +        };
 +
 +        let _ = if let Some(x) = self.opt {
 +            x
 +        } else {
 +            return None;
 +        };
 +
 +        self.opt
 +    }
 +}
 +
 +#[derive(Clone)]
 +pub struct MoveStruct {
 +    pub opt: Option<Vec<u32>>,
 +}
 +
 +impl MoveStruct {
 +    pub fn ref_func(&self) -> Option<Vec<u32>> {
 +        if self.opt.is_none() {
 +            return None;
 +        }
 +
 +        self.opt.clone()
 +    }
 +
 +    pub fn mov_func_reuse(self) -> Option<Vec<u32>> {
 +        if self.opt.is_none() {
 +            return None;
 +        }
 +
 +        self.opt
 +    }
 +
 +    pub fn mov_func_no_use(self) -> Option<Vec<u32>> {
 +        if self.opt.is_none() {
 +            return None;
 +        }
 +        Some(Vec::new())
 +    }
 +
 +    pub fn if_let_ref_func(self) -> Option<Vec<u32>> {
 +        let v: &Vec<_> = if let Some(ref v) = self.opt {
 +            v
 +        } else {
 +            return None;
 +        };
 +
 +        Some(v.clone())
 +    }
 +
 +    pub fn if_let_mov_func(self) -> Option<Vec<u32>> {
 +        let v = if let Some(v) = self.opt {
 +            v
 +        } else {
 +            return None;
 +        };
 +
 +        Some(v)
 +    }
 +}
 +
 +fn func() -> Option<i32> {
 +    fn f() -> Option<String> {
 +        Some(String::new())
 +    }
 +
 +    if f().is_none() {
 +        return None;
 +    }
 +
 +    Some(0)
 +}
 +
 +fn func_returning_result() -> Result<i32, i32> {
 +    Ok(1)
 +}
 +
 +fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
 +    let _ = if let Ok(x) = x { x } else { return x };
 +
 +    if x.is_err() {
 +        return x;
 +    }
 +
 +    // No warning
 +    let y = if let Ok(x) = x {
 +        x
 +    } else {
 +        return Err(0);
 +    };
 +
 +    // issue #7859
 +    // no warning
 +    let _ = if let Ok(x) = func_returning_result() {
 +        x
 +    } else {
 +        return Err(0);
 +    };
 +
 +    // no warning
 +    if func_returning_result().is_err() {
 +        return func_returning_result();
 +    }
 +
 +    Ok(y)
 +}
 +
 +// see issue #8019
 +pub enum NotOption {
 +    None,
 +    First,
 +    AfterFirst,
 +}
 +
 +fn obj(_: i32) -> Result<(), NotOption> {
 +    Err(NotOption::First)
 +}
 +
 +fn f() -> NotOption {
 +    if obj(2).is_err() {
 +        return NotOption::None;
 +    }
 +    NotOption::First
 +}
 +
-     let copy_struct = CopyStruct { opt: Some(54) };
-     copy_struct.func();
++fn do_something() {}
 +
-     let move_struct = MoveStruct {
-         opt: Some(vec![42, 1337]),
-     };
-     move_struct.ref_func();
-     move_struct.clone().mov_func_reuse();
-     move_struct.mov_func_no_use();
++fn err_immediate_return() -> Result<i32, i32> {
++    if let Err(err) = func_returning_result() {
++        return Err(err);
++    }
++    Ok(1)
++}
 +
-     let so = SeemsOption::Some(45);
-     returns_something_similar_to_option(so);
++fn err_immediate_return_and_do_something() -> Result<i32, i32> {
++    if let Err(err) = func_returning_result() {
++        return Err(err);
++    }
++    do_something();
++    Ok(1)
++}
 +
-     func();
++// No warning
++fn no_immediate_return() -> Result<i32, i32> {
++    if let Err(err) = func_returning_result() {
++        do_something();
++        return Err(err);
++    }
++    Ok(1)
++}
 +
-     let _ = result_func(Ok(42));
-     let _ = f();
++// No warning
++fn mixed_result_and_option() -> Option<i32> {
++    if let Err(err) = func_returning_result() {
++        return Some(err);
++    }
++    None
++}
 +
++// No warning
++fn else_if_check() -> Result<i32, i32> {
++    if true {
++        Ok(1)
++    } else if let Err(e) = func_returning_result() {
++        Err(e)
++    } else {
++        Err(-1)
++    }
 +}
++
++// No warning
++#[allow(clippy::manual_map)]
++#[rustfmt::skip]
++fn option_map() -> Option<bool> {
++    if let Some(a) = Some(false) {
++        Some(!a)
++    } else {
++        None
++    }
++}
++
++fn main() {}
index 8d782b71dd6a47ae8368b0d8f8c7ba89b13b34b6,0000000000000000000000000000000000000000..1b6cd524b2f2339484878443a9e09f2acc00917a
mode 100644,000000..100644
--- /dev/null
@@@ -1,118 -1,0 +1,134 @@@
-   --> $DIR/question_mark.rs:6:5
 +error: this block may be rewritten with the `?` operator
-   --> $DIR/question_mark.rs:51:9
++  --> $DIR/question_mark.rs:7:5
 +   |
 +LL | /     if a.is_none() {
 +LL | |         return None;
 +LL | |     }
 +   | |_____^ help: replace it with: `a?;`
 +   |
 +   = note: `-D clippy::question-mark` implied by `-D warnings`
 +
 +error: this block may be rewritten with the `?` operator
-   --> $DIR/question_mark.rs:55:9
++  --> $DIR/question_mark.rs:52:9
 +   |
 +LL | /         if (self.opt).is_none() {
 +LL | |             return None;
 +LL | |         }
 +   | |_________^ help: replace it with: `(self.opt)?;`
 +
 +error: this block may be rewritten with the `?` operator
-   --> $DIR/question_mark.rs:59:17
++  --> $DIR/question_mark.rs:56:9
 +   |
 +LL | /         if self.opt.is_none() {
 +LL | |             return None
 +LL | |         }
 +   | |_________^ help: replace it with: `self.opt?;`
 +
 +error: this block may be rewritten with the `?` operator
- error: this if-let-else may be rewritten with the `?` operator
-   --> $DIR/question_mark.rs:65:17
++  --> $DIR/question_mark.rs:60:17
 +   |
 +LL |           let _ = if self.opt.is_none() {
 +   |  _________________^
 +LL | |             return None;
 +LL | |         } else {
 +LL | |             self.opt
 +LL | |         };
 +   | |_________^ help: replace it with: `Some(self.opt?)`
 +
-   --> $DIR/question_mark.rs:82:9
++error: this block may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:66:17
 +   |
 +LL |           let _ = if let Some(x) = self.opt {
 +   |  _________________^
 +LL | |             x
 +LL | |         } else {
 +LL | |             return None;
 +LL | |         };
 +   | |_________^ help: replace it with: `self.opt?`
 +
 +error: this block may be rewritten with the `?` operator
-   --> $DIR/question_mark.rs:90:9
++  --> $DIR/question_mark.rs:83:9
 +   |
 +LL | /         if self.opt.is_none() {
 +LL | |             return None;
 +LL | |         }
 +   | |_________^ help: replace it with: `self.opt.as_ref()?;`
 +
 +error: this block may be rewritten with the `?` operator
-   --> $DIR/question_mark.rs:98:9
++  --> $DIR/question_mark.rs:91:9
 +   |
 +LL | /         if self.opt.is_none() {
 +LL | |             return None;
 +LL | |         }
 +   | |_________^ help: replace it with: `self.opt.as_ref()?;`
 +
 +error: this block may be rewritten with the `?` operator
- error: this if-let-else may be rewritten with the `?` operator
-   --> $DIR/question_mark.rs:105:26
++  --> $DIR/question_mark.rs:99:9
 +   |
 +LL | /         if self.opt.is_none() {
 +LL | |             return None;
 +LL | |         }
 +   | |_________^ help: replace it with: `self.opt.as_ref()?;`
 +
- error: this if-let-else may be rewritten with the `?` operator
-   --> $DIR/question_mark.rs:115:17
++error: this block may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:106:26
 +   |
 +LL |           let v: &Vec<_> = if let Some(ref v) = self.opt {
 +   |  __________________________^
 +LL | |             v
 +LL | |         } else {
 +LL | |             return None;
 +LL | |         };
 +   | |_________^ help: replace it with: `self.opt.as_ref()?`
 +
-   --> $DIR/question_mark.rs:130:5
++error: this block may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:116:17
 +   |
 +LL |           let v = if let Some(v) = self.opt {
 +   |  _________________^
 +LL | |             v
 +LL | |         } else {
 +LL | |             return None;
 +LL | |         };
 +   | |_________^ help: replace it with: `self.opt?`
 +
 +error: this block may be rewritten with the `?` operator
- error: this if-let-else may be rewritten with the `?` operator
-   --> $DIR/question_mark.rs:142:13
++  --> $DIR/question_mark.rs:131:5
 +   |
 +LL | /     if f().is_none() {
 +LL | |         return None;
 +LL | |     }
 +   | |_____^ help: replace it with: `f()?;`
 +
-   --> $DIR/question_mark.rs:144:5
++error: this block may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:143:13
 +   |
 +LL |     let _ = if let Ok(x) = x { x } else { return x };
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?`
 +
 +error: this block may be rewritten with the `?` operator
- error: aborting due to 13 previous errors
++  --> $DIR/question_mark.rs:145:5
 +   |
 +LL | /     if x.is_err() {
 +LL | |         return x;
 +LL | |     }
 +   | |_____^ help: replace it with: `x?;`
 +
++error: this block may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:193:5
++   |
++LL | /     if let Err(err) = func_returning_result() {
++LL | |         return Err(err);
++LL | |     }
++   | |_____^ help: replace it with: `func_returning_result()?;`
++
++error: this block may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:200:5
++   |
++LL | /     if let Err(err) = func_returning_result() {
++LL | |         return Err(err);
++LL | |     }
++   | |_____^ help: replace it with: `func_returning_result()?;`
++
++error: aborting due to 15 previous errors
 +
index 9562b47f0c4ff28610caf5f78856225725642d77,0000000000000000000000000000000000000000..daef95a425c92b564e6a202bca3be4b3d5a80193
mode 100644,000000..100644
--- /dev/null
@@@ -1,127 -1,0 +1,127 @@@
-     mod mulitply_conflicit_trait {
 +#![feature(lint_reasons)]
 +#![warn(clippy::same_name_method)]
 +#![allow(dead_code, non_camel_case_types)]
 +
 +trait T1 {
 +    fn foo() {}
 +}
 +
 +trait T2 {
 +    fn foo() {}
 +}
 +
 +mod should_lint {
 +
 +    mod test_basic_case {
 +        use crate::T1;
 +
 +        struct S;
 +
 +        impl S {
 +            fn foo() {}
 +        }
 +
 +        impl T1 for S {
 +            fn foo() {}
 +        }
 +    }
 +
 +    mod test_derive {
 +
 +        #[derive(Clone)]
 +        struct S;
 +
 +        impl S {
 +            fn clone() {}
 +        }
 +    }
 +
 +    mod with_generic {
 +        use crate::T1;
 +
 +        struct S<U>(U);
 +
 +        impl<U> S<U> {
 +            fn foo() {}
 +        }
 +
 +        impl<U: Copy> T1 for S<U> {
 +            fn foo() {}
 +        }
 +    }
 +
 +    mod default_method {
 +        use crate::T1;
 +
 +        struct S;
 +
 +        impl S {
 +            fn foo() {}
 +        }
 +
 +        impl T1 for S {}
 +    }
 +
++    mod multiply_conflicit_trait {
 +        use crate::{T1, T2};
 +
 +        struct S;
 +
 +        impl S {
 +            fn foo() {}
 +        }
 +
 +        impl T1 for S {}
 +
 +        impl T2 for S {}
 +    }
 +}
 +
 +mod should_not_lint {
 +
 +    mod not_lint_two_trait_method {
 +        use crate::{T1, T2};
 +
 +        struct S;
 +
 +        impl T1 for S {
 +            fn foo() {}
 +        }
 +
 +        impl T2 for S {
 +            fn foo() {}
 +        }
 +    }
 +
 +    mod only_lint_on_method {
 +        trait T3 {
 +            type foo;
 +        }
 +
 +        struct S;
 +
 +        impl S {
 +            fn foo() {}
 +        }
 +        impl T3 for S {
 +            type foo = usize;
 +        }
 +    }
 +}
 +
 +mod check_expect_suppression {
 +    use crate::T1;
 +
 +    struct S;
 +
 +    impl S {
 +        #[expect(clippy::same_name_method)]
 +        fn foo() {}
 +    }
 +
 +    impl T1 for S {
 +        fn foo() {}
 +    }
 +}
 +
 +fn main() {}
index 5a2aee465d1bdee6d95cd79f1763aef9b73e4300,0000000000000000000000000000000000000000..385a9986aba047bfdfe30cf1fc3818e340eca0ab
mode 100644,000000..100644
--- /dev/null
@@@ -1,218 -1,0 +1,248 @@@
 +// run-rustfix
 +#![allow(dead_code, clippy::explicit_auto_deref)]
 +#![warn(clippy::search_is_some)]
 +
 +fn main() {
 +    let v = vec![3, 2, 1, 0, -1, -2, -3];
 +    let y = &&42;
 +
 +    // Check `find().is_some()`, single-line case.
 +    let _ = v.iter().any(|x| *x < 0);
 +    let _ = (0..1).any(|x| **y == x); // one dereference less
 +    let _ = (0..1).any(|x| x == 0);
 +    let _ = v.iter().any(|x| *x == 0);
 +    let _ = (4..5).any(|x| x == 1 || x == 3 || x == 5);
 +    let _ = (1..3).any(|x| [1, 2, 3].contains(&x));
 +    let _ = (1..3).any(|x| x == 0 || [1, 2, 3].contains(&x));
 +    let _ = (1..3).any(|x| [1, 2, 3].contains(&x) || x == 0);
 +    let _ = (1..3)
 +        .any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1);
 +
 +    // Check `position().is_some()`, single-line case.
 +    let _ = v.iter().any(|&x| x < 0);
 +
 +    // Check `rposition().is_some()`, single-line case.
 +    let _ = v.iter().any(|&x| x < 0);
 +
 +    let s1 = String::from("hello world");
 +    let s2 = String::from("world");
 +    // caller of `find()` is a `&`static str`
 +    let _ = "hello world".contains("world");
 +    let _ = "hello world".contains(&s2);
 +    let _ = "hello world".contains(&s2[2..]);
 +    // caller of `find()` is a `String`
 +    let _ = s1.contains("world");
 +    let _ = s1.contains(&s2);
 +    let _ = s1.contains(&s2[2..]);
 +    // caller of `find()` is slice of `String`
 +    let _ = s1[2..].contains("world");
 +    let _ = s1[2..].contains(&s2);
 +    let _ = s1[2..].contains(&s2[2..]);
 +}
 +
 +#[allow(clippy::clone_on_copy, clippy::map_clone)]
 +mod issue7392 {
 +    struct Player {
 +        hand: Vec<usize>,
 +    }
 +    fn filter() {
 +        let p = Player {
 +            hand: vec![1, 2, 3, 4, 5],
 +        };
 +        let filter_hand = vec![5];
 +        let _ = p
 +            .hand
 +            .iter()
 +            .filter(|c| filter_hand.iter().any(|cc| c == &cc))
 +            .map(|c| c.clone())
 +            .collect::<Vec<_>>();
 +    }
 +
 +    struct PlayerTuple {
 +        hand: Vec<(usize, char)>,
 +    }
 +    fn filter_tuple() {
 +        let p = PlayerTuple {
 +            hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
 +        };
 +        let filter_hand = vec![5];
 +        let _ = p
 +            .hand
 +            .iter()
 +            .filter(|(c, _)| filter_hand.iter().any(|cc| c == cc))
 +            .map(|c| c.clone())
 +            .collect::<Vec<_>>();
 +    }
 +
 +    fn field_projection() {
 +        struct Foo {
 +            foo: i32,
 +            bar: u32,
 +        }
 +        let vfoo = vec![Foo { foo: 1, bar: 2 }];
 +        let _ = vfoo.iter().any(|v| v.foo == 1 && v.bar == 2);
 +
 +        let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
 +        let _ = vfoo
 +            .iter()
 +            .any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2);
 +    }
 +
 +    fn index_projection() {
 +        let vfoo = vec![[0, 1, 2, 3]];
 +        let _ = vfoo.iter().any(|a| a[0] == 42);
 +    }
 +
 +    #[allow(clippy::match_like_matches_macro)]
 +    fn slice_projection() {
 +        let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
 +        let _ = vfoo.iter().any(|sub| sub[1..4].len() == 3);
 +    }
 +
 +    fn please(x: &u32) -> bool {
 +        *x == 9
 +    }
 +
 +    fn deref_enough(x: u32) -> bool {
 +        x == 78
 +    }
 +
 +    fn arg_no_deref(x: &&u32) -> bool {
 +        **x == 78
 +    }
 +
 +    fn more_projections() {
 +        let x = 19;
 +        let ppx: &u32 = &x;
 +        let _ = [ppx].iter().any(|ppp_x: &&u32| please(ppp_x));
 +        let _ = [String::from("Hey hey")].iter().any(|s| s.len() == 2);
 +
 +        let v = vec![3, 2, 1, 0];
 +        let _ = v.iter().any(|x| deref_enough(*x));
 +        let _ = v.iter().any(|x: &u32| deref_enough(*x));
 +
 +        #[allow(clippy::redundant_closure)]
 +        let _ = v.iter().any(|x| arg_no_deref(&x));
 +        #[allow(clippy::redundant_closure)]
 +        let _ = v.iter().any(|x: &u32| arg_no_deref(&x));
 +    }
 +
 +    fn field_index_projection() {
 +        struct FooDouble {
 +            bar: Vec<Vec<i32>>,
 +        }
 +        struct Foo {
 +            bar: Vec<i32>,
 +        }
 +        struct FooOuter {
 +            inner: Foo,
 +            inner_double: FooDouble,
 +        }
 +        let vfoo = vec![FooOuter {
 +            inner: Foo { bar: vec![0, 1, 2, 3] },
 +            inner_double: FooDouble {
 +                bar: vec![vec![0, 1, 2, 3]],
 +            },
 +        }];
 +        let _ = vfoo
 +            .iter()
 +            .any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2);
 +    }
 +
 +    fn index_field_projection() {
 +        struct Foo {
 +            bar: i32,
 +        }
 +        struct FooOuter {
 +            inner: Vec<Foo>,
 +        }
 +        let vfoo = vec![FooOuter {
 +            inner: vec![Foo { bar: 0 }],
 +        }];
 +        let _ = vfoo.iter().any(|v| v.inner[0].bar == 2);
 +    }
 +
 +    fn double_deref_index_projection() {
 +        let vfoo = vec![&&[0, 1, 2, 3]];
 +        let _ = vfoo.iter().any(|x| (**x)[0] == 9);
 +    }
 +
 +    fn method_call_by_ref() {
 +        struct Foo {
 +            bar: u32,
 +        }
 +        impl Foo {
 +            pub fn by_ref(&self, x: &u32) -> bool {
 +                *x == self.bar
 +            }
 +        }
 +        let vfoo = vec![Foo { bar: 1 }];
 +        let _ = vfoo.iter().any(|v| v.by_ref(&v.bar));
 +    }
 +
 +    fn ref_bindings() {
 +        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
 +        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
 +    }
 +
 +    fn test_string_1(s: &str) -> bool {
 +        s.is_empty()
 +    }
 +
 +    fn test_u32_1(s: &u32) -> bool {
 +        s.is_power_of_two()
 +    }
 +
 +    fn test_u32_2(s: u32) -> bool {
 +        s.is_power_of_two()
 +    }
 +
 +    fn projection_in_args_test() {
 +        // Index projections
 +        let lst = &[String::from("Hello"), String::from("world")];
 +        let v: Vec<&[String]> = vec![lst];
 +        let _ = v.iter().any(|s| s[0].is_empty());
 +        let _ = v.iter().any(|s| test_string_1(&s[0]));
 +
 +        // Field projections
 +        struct FieldProjection<'a> {
 +            field: &'a u32,
 +        }
 +        let field = 123456789;
 +        let instance = FieldProjection { field: &field };
 +        let v = vec![instance];
 +        let _ = v.iter().any(|fp| fp.field.is_power_of_two());
 +        let _ = v.iter().any(|fp| test_u32_1(fp.field));
 +        let _ = v.iter().any(|fp| test_u32_2(*fp.field));
 +    }
 +}
++
++mod issue9120 {
++    fn make_arg_no_deref_impl() -> impl Fn(&&u32) -> bool {
++        move |x: &&u32| **x == 78
++    }
++
++    fn make_arg_no_deref_dyn() -> Box<dyn Fn(&&u32) -> bool> {
++        Box::new(move |x: &&u32| **x == 78)
++    }
++
++    fn wrapper<T: Fn(&&u32) -> bool>(v: Vec<u32>, func: T) -> bool {
++        #[allow(clippy::redundant_closure)]
++        v.iter().any(|x: &u32| func(&x))
++    }
++
++    fn do_tests() {
++        let v = vec![3, 2, 1, 0];
++        let arg_no_deref_impl = make_arg_no_deref_impl();
++        let arg_no_deref_dyn = make_arg_no_deref_dyn();
++
++        #[allow(clippy::redundant_closure)]
++        let _ = v.iter().any(|x: &u32| arg_no_deref_impl(&x));
++
++        #[allow(clippy::redundant_closure)]
++        let _ = v.iter().any(|x: &u32| arg_no_deref_dyn(&x));
++
++        #[allow(clippy::redundant_closure)]
++        let _ = v.iter().any(|x: &u32| (*arg_no_deref_dyn)(&x));
++    }
++}
index 0e98ae18a217cb840dad5c974c1b221488c64b14,0000000000000000000000000000000000000000..67e190ee37837d471b9c7eb7f5d05b859a04dafd
mode 100644,000000..100644
--- /dev/null
@@@ -1,221 -1,0 +1,251 @@@
 +// run-rustfix
 +#![allow(dead_code, clippy::explicit_auto_deref)]
 +#![warn(clippy::search_is_some)]
 +
 +fn main() {
 +    let v = vec![3, 2, 1, 0, -1, -2, -3];
 +    let y = &&42;
 +
 +    // Check `find().is_some()`, single-line case.
 +    let _ = v.iter().find(|&x| *x < 0).is_some();
 +    let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
 +    let _ = (0..1).find(|x| *x == 0).is_some();
 +    let _ = v.iter().find(|x| **x == 0).is_some();
 +    let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_some();
 +    let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_some();
 +    let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_some();
 +    let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_some();
 +    let _ = (1..3)
 +        .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
 +        .is_some();
 +
 +    // Check `position().is_some()`, single-line case.
 +    let _ = v.iter().position(|&x| x < 0).is_some();
 +
 +    // Check `rposition().is_some()`, single-line case.
 +    let _ = v.iter().rposition(|&x| x < 0).is_some();
 +
 +    let s1 = String::from("hello world");
 +    let s2 = String::from("world");
 +    // caller of `find()` is a `&`static str`
 +    let _ = "hello world".find("world").is_some();
 +    let _ = "hello world".find(&s2).is_some();
 +    let _ = "hello world".find(&s2[2..]).is_some();
 +    // caller of `find()` is a `String`
 +    let _ = s1.find("world").is_some();
 +    let _ = s1.find(&s2).is_some();
 +    let _ = s1.find(&s2[2..]).is_some();
 +    // caller of `find()` is slice of `String`
 +    let _ = s1[2..].find("world").is_some();
 +    let _ = s1[2..].find(&s2).is_some();
 +    let _ = s1[2..].find(&s2[2..]).is_some();
 +}
 +
 +#[allow(clippy::clone_on_copy, clippy::map_clone)]
 +mod issue7392 {
 +    struct Player {
 +        hand: Vec<usize>,
 +    }
 +    fn filter() {
 +        let p = Player {
 +            hand: vec![1, 2, 3, 4, 5],
 +        };
 +        let filter_hand = vec![5];
 +        let _ = p
 +            .hand
 +            .iter()
 +            .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some())
 +            .map(|c| c.clone())
 +            .collect::<Vec<_>>();
 +    }
 +
 +    struct PlayerTuple {
 +        hand: Vec<(usize, char)>,
 +    }
 +    fn filter_tuple() {
 +        let p = PlayerTuple {
 +            hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
 +        };
 +        let filter_hand = vec![5];
 +        let _ = p
 +            .hand
 +            .iter()
 +            .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some())
 +            .map(|c| c.clone())
 +            .collect::<Vec<_>>();
 +    }
 +
 +    fn field_projection() {
 +        struct Foo {
 +            foo: i32,
 +            bar: u32,
 +        }
 +        let vfoo = vec![Foo { foo: 1, bar: 2 }];
 +        let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some();
 +
 +        let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
 +        let _ = vfoo
 +            .iter()
 +            .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
 +            .is_some();
 +    }
 +
 +    fn index_projection() {
 +        let vfoo = vec![[0, 1, 2, 3]];
 +        let _ = vfoo.iter().find(|a| a[0] == 42).is_some();
 +    }
 +
 +    #[allow(clippy::match_like_matches_macro)]
 +    fn slice_projection() {
 +        let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
 +        let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some();
 +    }
 +
 +    fn please(x: &u32) -> bool {
 +        *x == 9
 +    }
 +
 +    fn deref_enough(x: u32) -> bool {
 +        x == 78
 +    }
 +
 +    fn arg_no_deref(x: &&u32) -> bool {
 +        **x == 78
 +    }
 +
 +    fn more_projections() {
 +        let x = 19;
 +        let ppx: &u32 = &x;
 +        let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some();
 +        let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some();
 +
 +        let v = vec![3, 2, 1, 0];
 +        let _ = v.iter().find(|x| deref_enough(**x)).is_some();
 +        let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_some();
 +
 +        #[allow(clippy::redundant_closure)]
 +        let _ = v.iter().find(|x| arg_no_deref(x)).is_some();
 +        #[allow(clippy::redundant_closure)]
 +        let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_some();
 +    }
 +
 +    fn field_index_projection() {
 +        struct FooDouble {
 +            bar: Vec<Vec<i32>>,
 +        }
 +        struct Foo {
 +            bar: Vec<i32>,
 +        }
 +        struct FooOuter {
 +            inner: Foo,
 +            inner_double: FooDouble,
 +        }
 +        let vfoo = vec![FooOuter {
 +            inner: Foo { bar: vec![0, 1, 2, 3] },
 +            inner_double: FooDouble {
 +                bar: vec![vec![0, 1, 2, 3]],
 +            },
 +        }];
 +        let _ = vfoo
 +            .iter()
 +            .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
 +            .is_some();
 +    }
 +
 +    fn index_field_projection() {
 +        struct Foo {
 +            bar: i32,
 +        }
 +        struct FooOuter {
 +            inner: Vec<Foo>,
 +        }
 +        let vfoo = vec![FooOuter {
 +            inner: vec![Foo { bar: 0 }],
 +        }];
 +        let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_some();
 +    }
 +
 +    fn double_deref_index_projection() {
 +        let vfoo = vec![&&[0, 1, 2, 3]];
 +        let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_some();
 +    }
 +
 +    fn method_call_by_ref() {
 +        struct Foo {
 +            bar: u32,
 +        }
 +        impl Foo {
 +            pub fn by_ref(&self, x: &u32) -> bool {
 +                *x == self.bar
 +            }
 +        }
 +        let vfoo = vec![Foo { bar: 1 }];
 +        let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some();
 +    }
 +
 +    fn ref_bindings() {
 +        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some();
 +        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some();
 +    }
 +
 +    fn test_string_1(s: &str) -> bool {
 +        s.is_empty()
 +    }
 +
 +    fn test_u32_1(s: &u32) -> bool {
 +        s.is_power_of_two()
 +    }
 +
 +    fn test_u32_2(s: u32) -> bool {
 +        s.is_power_of_two()
 +    }
 +
 +    fn projection_in_args_test() {
 +        // Index projections
 +        let lst = &[String::from("Hello"), String::from("world")];
 +        let v: Vec<&[String]> = vec![lst];
 +        let _ = v.iter().find(|s| s[0].is_empty()).is_some();
 +        let _ = v.iter().find(|s| test_string_1(&s[0])).is_some();
 +
 +        // Field projections
 +        struct FieldProjection<'a> {
 +            field: &'a u32,
 +        }
 +        let field = 123456789;
 +        let instance = FieldProjection { field: &field };
 +        let v = vec![instance];
 +        let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some();
 +        let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some();
 +        let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
 +    }
 +}
++
++mod issue9120 {
++    fn make_arg_no_deref_impl() -> impl Fn(&&u32) -> bool {
++        move |x: &&u32| **x == 78
++    }
++
++    fn make_arg_no_deref_dyn() -> Box<dyn Fn(&&u32) -> bool> {
++        Box::new(move |x: &&u32| **x == 78)
++    }
++
++    fn wrapper<T: Fn(&&u32) -> bool>(v: Vec<u32>, func: T) -> bool {
++        #[allow(clippy::redundant_closure)]
++        v.iter().find(|x: &&u32| func(x)).is_some()
++    }
++
++    fn do_tests() {
++        let v = vec![3, 2, 1, 0];
++        let arg_no_deref_impl = make_arg_no_deref_impl();
++        let arg_no_deref_dyn = make_arg_no_deref_dyn();
++
++        #[allow(clippy::redundant_closure)]
++        let _ = v.iter().find(|x: &&u32| arg_no_deref_impl(x)).is_some();
++
++        #[allow(clippy::redundant_closure)]
++        let _ = v.iter().find(|x: &&u32| arg_no_deref_dyn(x)).is_some();
++
++        #[allow(clippy::redundant_closure)]
++        let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some();
++    }
++}
index 8b424f18ef5b50c60f10d3c69837d748a0778ff6,0000000000000000000000000000000000000000..c5c3c92c9182fc32aa3de5c6f9bdda18dee8d126
mode 100644,000000..100644
--- /dev/null
@@@ -1,268 -1,0 +1,292 @@@
- error: aborting due to 43 previous errors
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:10:22
 +   |
 +LL |     let _ = v.iter().find(|&x| *x < 0).is_some();
 +   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x < 0)`
 +   |
 +   = note: `-D clippy::search-is-some` implied by `-D warnings`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:11:20
 +   |
 +LL |     let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
 +   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| **y == x)`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:12:20
 +   |
 +LL |     let _ = (0..1).find(|x| *x == 0).is_some();
 +   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0)`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:13:22
 +   |
 +LL |     let _ = v.iter().find(|x| **x == 0).is_some();
 +   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x == 0)`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:14:20
 +   |
 +LL |     let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_some();
 +   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 1 || x == 3 || x == 5)`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:15:20
 +   |
 +LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_some();
 +   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x))`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:16:20
 +   |
 +LL |     let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_some();
 +   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0 || [1, 2, 3].contains(&x))`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:17:20
 +   |
 +LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_some();
 +   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x) || x == 0)`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:19:10
 +   |
 +LL |           .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
 +   |  __________^
 +LL | |         .is_some();
 +   | |__________________^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)`
 +
 +error: called `is_some()` after searching an `Iterator` with `position`
 +  --> $DIR/search_is_some_fixable_some.rs:23:22
 +   |
 +LL |     let _ = v.iter().position(|&x| x < 0).is_some();
 +   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
 +
 +error: called `is_some()` after searching an `Iterator` with `rposition`
 +  --> $DIR/search_is_some_fixable_some.rs:26:22
 +   |
 +LL |     let _ = v.iter().rposition(|&x| x < 0).is_some();
 +   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
 +
 +error: called `is_some()` after calling `find()` on a string
 +  --> $DIR/search_is_some_fixable_some.rs:31:27
 +   |
 +LL |     let _ = "hello world".find("world").is_some();
 +   |                           ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
 +
 +error: called `is_some()` after calling `find()` on a string
 +  --> $DIR/search_is_some_fixable_some.rs:32:27
 +   |
 +LL |     let _ = "hello world".find(&s2).is_some();
 +   |                           ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
 +
 +error: called `is_some()` after calling `find()` on a string
 +  --> $DIR/search_is_some_fixable_some.rs:33:27
 +   |
 +LL |     let _ = "hello world".find(&s2[2..]).is_some();
 +   |                           ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
 +
 +error: called `is_some()` after calling `find()` on a string
 +  --> $DIR/search_is_some_fixable_some.rs:35:16
 +   |
 +LL |     let _ = s1.find("world").is_some();
 +   |                ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
 +
 +error: called `is_some()` after calling `find()` on a string
 +  --> $DIR/search_is_some_fixable_some.rs:36:16
 +   |
 +LL |     let _ = s1.find(&s2).is_some();
 +   |                ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
 +
 +error: called `is_some()` after calling `find()` on a string
 +  --> $DIR/search_is_some_fixable_some.rs:37:16
 +   |
 +LL |     let _ = s1.find(&s2[2..]).is_some();
 +   |                ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
 +
 +error: called `is_some()` after calling `find()` on a string
 +  --> $DIR/search_is_some_fixable_some.rs:39:21
 +   |
 +LL |     let _ = s1[2..].find("world").is_some();
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
 +
 +error: called `is_some()` after calling `find()` on a string
 +  --> $DIR/search_is_some_fixable_some.rs:40:21
 +   |
 +LL |     let _ = s1[2..].find(&s2).is_some();
 +   |                     ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
 +
 +error: called `is_some()` after calling `find()` on a string
 +  --> $DIR/search_is_some_fixable_some.rs:41:21
 +   |
 +LL |     let _ = s1[2..].find(&s2[2..]).is_some();
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:57:44
 +   |
 +LL |             .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some())
 +   |                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|cc| c == &cc)`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:73:49
 +   |
 +LL |             .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some())
 +   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|cc| c == cc)`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:84:29
 +   |
 +LL |         let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some();
 +   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.foo == 1 && v.bar == 2)`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:89:14
 +   |
 +LL |               .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
 +   |  ______________^
 +LL | |             .is_some();
 +   | |______________________^ help: use `any()` instead: `any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:95:29
 +   |
 +LL |         let _ = vfoo.iter().find(|a| a[0] == 42).is_some();
 +   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|a| a[0] == 42)`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:101:29
 +   |
 +LL |         let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some();
 +   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|sub| sub[1..4].len() == 3)`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:119:30
 +   |
 +LL |         let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some();
 +   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|ppp_x: &&u32| please(ppp_x))`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:120:50
 +   |
 +LL |         let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some();
 +   |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| s.len() == 2)`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:123:26
 +   |
 +LL |         let _ = v.iter().find(|x| deref_enough(**x)).is_some();
 +   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| deref_enough(*x))`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:124:26
 +   |
 +LL |         let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_some();
 +   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| deref_enough(*x))`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:127:26
 +   |
 +LL |         let _ = v.iter().find(|x| arg_no_deref(x)).is_some();
 +   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| arg_no_deref(&x))`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:129:26
 +   |
 +LL |         let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_some();
 +   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref(&x))`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:151:14
 +   |
 +LL |               .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
 +   |  ______________^
 +LL | |             .is_some();
 +   | |______________________^ help: use `any()` instead: `any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:165:29
 +   |
 +LL |         let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_some();
 +   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.inner[0].bar == 2)`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:170:29
 +   |
 +LL |         let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_some();
 +   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| (**x)[0] == 9)`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:183:29
 +   |
 +LL |         let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some();
 +   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.by_ref(&v.bar))`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:187:55
 +   |
 +LL |         let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some();
 +   |                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:188:55
 +   |
 +LL |         let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some();
 +   |                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:207:26
 +   |
 +LL |         let _ = v.iter().find(|s| s[0].is_empty()).is_some();
 +   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| s[0].is_empty())`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:208:26
 +   |
 +LL |         let _ = v.iter().find(|s| test_string_1(&s[0])).is_some();
 +   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| test_string_1(&s[0]))`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:217:26
 +   |
 +LL |         let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some();
 +   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| fp.field.is_power_of_two())`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:218:26
 +   |
 +LL |         let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some();
 +   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_1(fp.field))`
 +
 +error: called `is_some()` after searching an `Iterator` with `find`
 +  --> $DIR/search_is_some_fixable_some.rs:219:26
 +   |
 +LL |         let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
 +   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_2(*fp.field))`
 +
++error: called `is_some()` after searching an `Iterator` with `find`
++  --> $DIR/search_is_some_fixable_some.rs:234:18
++   |
++LL |         v.iter().find(|x: &&u32| func(x)).is_some()
++   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| func(&x))`
++
++error: called `is_some()` after searching an `Iterator` with `find`
++  --> $DIR/search_is_some_fixable_some.rs:243:26
++   |
++LL |         let _ = v.iter().find(|x: &&u32| arg_no_deref_impl(x)).is_some();
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref_impl(&x))`
++
++error: called `is_some()` after searching an `Iterator` with `find`
++  --> $DIR/search_is_some_fixable_some.rs:246:26
++   |
++LL |         let _ = v.iter().find(|x: &&u32| arg_no_deref_dyn(x)).is_some();
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref_dyn(&x))`
++
++error: called `is_some()` after searching an `Iterator` with `find`
++  --> $DIR/search_is_some_fixable_some.rs:249:26
++   |
++LL |         let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some();
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| (*arg_no_deref_dyn)(&x))`
++
++error: aborting due to 47 previous errors
 +
index 3bd41d0626049da49e07ce5c0f50ce10c6f2c0d4,0000000000000000000000000000000000000000..43d76094d0e847e3e6f60c875d2cf2248adc330b
mode 100644,000000..100644
--- /dev/null
@@@ -1,269 -1,0 +1,281 @@@
- error: aborting due to 22 previous errors
 +error: `x` is shadowed by itself in `x`
 +  --> $DIR/shadow.rs:6:9
 +   |
 +LL |     let x = x;
 +   |         ^
 +   |
 +   = note: `-D clippy::shadow-same` implied by `-D warnings`
 +note: previous binding is here
 +  --> $DIR/shadow.rs:5:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `mut x` is shadowed by itself in `&x`
 +  --> $DIR/shadow.rs:7:13
 +   |
 +LL |     let mut x = &x;
 +   |             ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:6:9
 +   |
 +LL |     let x = x;
 +   |         ^
 +
 +error: `x` is shadowed by itself in `&mut x`
 +  --> $DIR/shadow.rs:8:9
 +   |
 +LL |     let x = &mut x;
 +   |         ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:7:9
 +   |
 +LL |     let mut x = &x;
 +   |         ^^^^^
 +
 +error: `x` is shadowed by itself in `*x`
 +  --> $DIR/shadow.rs:9:9
 +   |
 +LL |     let x = *x;
 +   |         ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:8:9
 +   |
 +LL |     let x = &mut x;
 +   |         ^
 +
 +error: `x` is shadowed
 +  --> $DIR/shadow.rs:14:9
 +   |
 +LL |     let x = x.0;
 +   |         ^
 +   |
 +   = note: `-D clippy::shadow-reuse` implied by `-D warnings`
 +note: previous binding is here
 +  --> $DIR/shadow.rs:13:9
 +   |
 +LL |     let x = ([[0]], ());
 +   |         ^
 +
 +error: `x` is shadowed
 +  --> $DIR/shadow.rs:15:9
 +   |
 +LL |     let x = x[0];
 +   |         ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:14:9
 +   |
 +LL |     let x = x.0;
 +   |         ^
 +
 +error: `x` is shadowed
 +  --> $DIR/shadow.rs:16:10
 +   |
 +LL |     let [x] = x;
 +   |          ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:15:9
 +   |
 +LL |     let x = x[0];
 +   |         ^
 +
 +error: `x` is shadowed
 +  --> $DIR/shadow.rs:17:9
 +   |
 +LL |     let x = Some(x);
 +   |         ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:16:10
 +   |
 +LL |     let [x] = x;
 +   |          ^
 +
 +error: `x` is shadowed
 +  --> $DIR/shadow.rs:18:9
 +   |
 +LL |     let x = foo(x);
 +   |         ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:17:9
 +   |
 +LL |     let x = Some(x);
 +   |         ^
 +
 +error: `x` is shadowed
 +  --> $DIR/shadow.rs:19:9
 +   |
 +LL |     let x = || x;
 +   |         ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:18:9
 +   |
 +LL |     let x = foo(x);
 +   |         ^
 +
 +error: `x` is shadowed
 +  --> $DIR/shadow.rs:20:9
 +   |
 +LL |     let x = Some(1).map(|_| x)?;
 +   |         ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:19:9
 +   |
 +LL |     let x = || x;
 +   |         ^
 +
 +error: `y` is shadowed
 +  --> $DIR/shadow.rs:22:9
 +   |
 +LL |     let y = match y {
 +   |         ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:21:9
 +   |
 +LL |     let y = 1;
 +   |         ^
 +
 +error: `x` shadows a previous, unrelated binding
 +  --> $DIR/shadow.rs:31:9
 +   |
 +LL |     let x = 2;
 +   |         ^
 +   |
 +   = note: `-D clippy::shadow-unrelated` implied by `-D warnings`
 +note: previous binding is here
 +  --> $DIR/shadow.rs:30:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `x` shadows a previous, unrelated binding
 +  --> $DIR/shadow.rs:36:13
 +   |
 +LL |         let x = 1;
 +   |             ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:35:10
 +   |
 +LL |     fn f(x: u32) {
 +   |          ^
 +
 +error: `x` shadows a previous, unrelated binding
 +  --> $DIR/shadow.rs:41:14
 +   |
 +LL |         Some(x) => {
 +   |              ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:38:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `x` shadows a previous, unrelated binding
 +  --> $DIR/shadow.rs:42:17
 +   |
 +LL |             let x = 1;
 +   |                 ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:41:14
 +   |
 +LL |         Some(x) => {
 +   |              ^
 +
 +error: `x` shadows a previous, unrelated binding
 +  --> $DIR/shadow.rs:46:17
 +   |
 +LL |     if let Some(x) = Some(1) {}
 +   |                 ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:38:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `x` shadows a previous, unrelated binding
 +  --> $DIR/shadow.rs:47:20
 +   |
 +LL |     while let Some(x) = Some(1) {}
 +   |                    ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:38:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `x` shadows a previous, unrelated binding
 +  --> $DIR/shadow.rs:48:15
 +   |
 +LL |     let _ = |[x]: [u32; 1]| {
 +   |               ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:38:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `x` shadows a previous, unrelated binding
 +  --> $DIR/shadow.rs:49:13
 +   |
 +LL |         let x = 1;
 +   |             ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:48:15
 +   |
 +LL |     let _ = |[x]: [u32; 1]| {
 +   |               ^
 +
 +error: `y` is shadowed
 +  --> $DIR/shadow.rs:52:17
 +   |
 +LL |     if let Some(y) = y {}
 +   |                 ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:51:9
 +   |
 +LL |     let y = Some(1);
 +   |         ^
 +
 +error: `_b` shadows a previous, unrelated binding
 +  --> $DIR/shadow.rs:88:9
 +   |
 +LL |     let _b = _a;
 +   |         ^^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:87:28
 +   |
 +LL | pub async fn foo2(_a: i32, _b: i64) {
 +   |                            ^^
 +
++error: `x` shadows a previous, unrelated binding
++  --> $DIR/shadow.rs:94:21
++   |
++LL |         if let Some(x) = Some(1) { x } else { 1 }
++   |                     ^
++   |
++note: previous binding is here
++  --> $DIR/shadow.rs:93:13
++   |
++LL |         let x = 1;
++   |             ^
++
++error: aborting due to 23 previous errors
 +
index 185e5009b60f1500db71b3e49cbad47f3cc09462,0000000000000000000000000000000000000000..84ecf1ea53ed73207e78abb145bee2ae7b8a47f3
mode 100644,000000..100644
--- /dev/null
@@@ -1,623 -1,0 +1,630 @@@
 +// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934
 +// // run-rustfix
 +
 +#![warn(clippy::significant_drop_in_scrutinee)]
 +#![allow(clippy::single_match)]
 +#![allow(clippy::match_single_binding)]
 +#![allow(unused_assignments)]
 +#![allow(dead_code)]
 +
 +use std::num::ParseIntError;
 +use std::ops::Deref;
 +use std::sync::atomic::{AtomicU64, Ordering};
 +use std::sync::RwLock;
 +use std::sync::{Mutex, MutexGuard};
 +
 +struct State {}
 +
 +impl State {
 +    fn foo(&self) -> bool {
 +        true
 +    }
 +
 +    fn bar(&self) {}
 +}
 +
 +fn should_not_trigger_lint_with_mutex_guard_outside_match() {
 +    let mutex = Mutex::new(State {});
 +
 +    // Should not trigger lint because the temporary should drop at the `;` on line before the match
 +    let is_foo = mutex.lock().unwrap().foo();
 +    match is_foo {
 +        true => {
 +            mutex.lock().unwrap().bar();
 +        },
 +        false => {},
 +    };
 +}
 +
 +fn should_not_trigger_lint_with_mutex_guard_when_taking_ownership_in_match() {
 +    let mutex = Mutex::new(State {});
 +
 +    // Should not trigger lint because the scrutinee is explicitly returning the MutexGuard,
 +    // so its lifetime should not be surprising.
 +    match mutex.lock() {
 +        Ok(guard) => {
 +            guard.foo();
 +            mutex.lock().unwrap().bar();
 +        },
 +        _ => {},
 +    };
 +}
 +
 +fn should_trigger_lint_with_mutex_guard_in_match_scrutinee() {
 +    let mutex = Mutex::new(State {});
 +
 +    // Should trigger lint because the lifetime of the temporary MutexGuard is surprising because it
 +    // is preserved until the end of the match, but there is no clear indication that this is the
 +    // case.
 +    match mutex.lock().unwrap().foo() {
 +        true => {
 +            mutex.lock().unwrap().bar();
 +        },
 +        false => {},
 +    };
 +}
 +
 +fn should_not_trigger_lint_with_mutex_guard_in_match_scrutinee_when_lint_allowed() {
 +    let mutex = Mutex::new(State {});
 +
 +    // Lint should not be triggered because it is "allowed" below.
 +    #[allow(clippy::significant_drop_in_scrutinee)]
 +    match mutex.lock().unwrap().foo() {
 +        true => {
 +            mutex.lock().unwrap().bar();
 +        },
 +        false => {},
 +    };
 +}
 +
 +fn should_not_trigger_lint_for_insignificant_drop() {
 +    // Should not trigger lint because there are no temporaries whose drops have a significant
 +    // side effect.
 +    match 1u64.to_string().is_empty() {
 +        true => {
 +            println!("It was empty")
 +        },
 +        false => {
 +            println!("It was not empty")
 +        },
 +    }
 +}
 +
 +struct StateWithMutex {
 +    m: Mutex<u64>,
 +}
 +
 +struct MutexGuardWrapper<'a> {
 +    mg: MutexGuard<'a, u64>,
 +}
 +
 +impl<'a> MutexGuardWrapper<'a> {
 +    fn get_the_value(&self) -> u64 {
 +        *self.mg.deref()
 +    }
 +}
 +
 +struct MutexGuardWrapperWrapper<'a> {
 +    mg: MutexGuardWrapper<'a>,
 +}
 +
 +impl<'a> MutexGuardWrapperWrapper<'a> {
 +    fn get_the_value(&self) -> u64 {
 +        *self.mg.mg.deref()
 +    }
 +}
 +
 +impl StateWithMutex {
 +    fn lock_m(&self) -> MutexGuardWrapper<'_> {
 +        MutexGuardWrapper {
 +            mg: self.m.lock().unwrap(),
 +        }
 +    }
 +
 +    fn lock_m_m(&self) -> MutexGuardWrapperWrapper<'_> {
 +        MutexGuardWrapperWrapper {
 +            mg: MutexGuardWrapper {
 +                mg: self.m.lock().unwrap(),
 +            },
 +        }
 +    }
 +
 +    fn foo(&self) -> bool {
 +        true
 +    }
 +
 +    fn bar(&self) {}
 +}
 +
 +fn should_trigger_lint_with_wrapped_mutex() {
 +    let s = StateWithMutex { m: Mutex::new(1) };
 +
 +    // Should trigger lint because a temporary contains a type with a significant drop and its
 +    // lifetime is not obvious. Additionally, it is not obvious from looking at the scrutinee that
 +    // the temporary contains such a type, making it potentially even more surprising.
 +    match s.lock_m().get_the_value() {
 +        1 => {
 +            println!("Got 1. Is it still 1?");
 +            println!("{}", s.lock_m().get_the_value());
 +        },
 +        2 => {
 +            println!("Got 2. Is it still 2?");
 +            println!("{}", s.lock_m().get_the_value());
 +        },
 +        _ => {},
 +    }
 +    println!("All done!");
 +}
 +
 +fn should_trigger_lint_with_double_wrapped_mutex() {
 +    let s = StateWithMutex { m: Mutex::new(1) };
 +
 +    // Should trigger lint because a temporary contains a type which further contains a type with a
 +    // significant drop and its lifetime is not obvious. Additionally, it is not obvious from
 +    // looking at the scrutinee that the temporary contains such a type, making it potentially even
 +    // more surprising.
 +    match s.lock_m_m().get_the_value() {
 +        1 => {
 +            println!("Got 1. Is it still 1?");
 +            println!("{}", s.lock_m().get_the_value());
 +        },
 +        2 => {
 +            println!("Got 2. Is it still 2?");
 +            println!("{}", s.lock_m().get_the_value());
 +        },
 +        _ => {},
 +    }
 +    println!("All done!");
 +}
 +
 +struct Counter {
 +    i: AtomicU64,
 +}
 +
 +#[clippy::has_significant_drop]
 +struct CounterWrapper<'a> {
 +    counter: &'a Counter,
 +}
 +
 +impl<'a> CounterWrapper<'a> {
 +    fn new(counter: &Counter) -> CounterWrapper {
 +        counter.i.fetch_add(1, Ordering::Relaxed);
 +        CounterWrapper { counter }
 +    }
 +}
 +
 +impl<'a> Drop for CounterWrapper<'a> {
 +    fn drop(&mut self) {
 +        self.counter.i.fetch_sub(1, Ordering::Relaxed);
 +    }
 +}
 +
 +impl Counter {
 +    fn temp_increment(&self) -> Vec<CounterWrapper> {
 +        vec![CounterWrapper::new(self), CounterWrapper::new(self)]
 +    }
 +}
 +
 +fn should_trigger_lint_for_vec() {
 +    let counter = Counter { i: AtomicU64::new(0) };
 +
 +    // Should trigger lint because the temporary in the scrutinee returns a collection of types
 +    // which have significant drops. The types with significant drops are also non-obvious when
 +    // reading the expression in the scrutinee.
 +    match counter.temp_increment().len() {
 +        2 => {
 +            let current_count = counter.i.load(Ordering::Relaxed);
 +            println!("Current count {}", current_count);
 +            assert_eq!(current_count, 0);
 +        },
 +        1 => {},
 +        3 => {},
 +        _ => {},
 +    };
 +}
 +
 +struct StateWithField {
 +    s: String,
 +}
 +
 +// Should trigger lint only on the type in the tuple which is created using a temporary
 +// with a significant drop. Additionally, this test ensures that the format of the tuple
 +// is preserved correctly in the suggestion.
 +fn should_trigger_lint_for_tuple_in_scrutinee() {
 +    let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
 +
 +    {
 +        match (mutex1.lock().unwrap().s.len(), true) {
 +            (3, _) => {
 +                println!("started");
 +                mutex1.lock().unwrap().s.len();
 +                println!("done");
 +            },
 +            (_, _) => {},
 +        };
 +
 +        match (true, mutex1.lock().unwrap().s.len(), true) {
 +            (_, 3, _) => {
 +                println!("started");
 +                mutex1.lock().unwrap().s.len();
 +                println!("done");
 +            },
 +            (_, _, _) => {},
 +        };
 +
 +        let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
 +        match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) {
 +            (3, _, 3) => {
 +                println!("started");
 +                mutex1.lock().unwrap().s.len();
 +                mutex2.lock().unwrap().s.len();
 +                println!("done");
 +            },
 +            (_, _, _) => {},
 +        };
 +
 +        let mutex3 = Mutex::new(StateWithField { s: "three".to_owned() });
 +        match mutex3.lock().unwrap().s.as_str() {
 +            "three" => {
 +                println!("started");
 +                mutex1.lock().unwrap().s.len();
 +                mutex2.lock().unwrap().s.len();
 +                println!("done");
 +            },
 +            _ => {},
 +        };
 +
 +        match (true, mutex3.lock().unwrap().s.as_str()) {
 +            (_, "three") => {
 +                println!("started");
 +                mutex1.lock().unwrap().s.len();
 +                mutex2.lock().unwrap().s.len();
 +                println!("done");
 +            },
 +            (_, _) => {},
 +        };
 +    }
 +}
 +
 +// Should trigger lint when either side of a binary operation creates a temporary with a
 +// significant drop.
 +// To avoid potential unnecessary copies or creating references that would trigger the significant
 +// drop problem, the lint recommends moving the entire binary operation.
 +fn should_trigger_lint_for_accessing_field_in_mutex_in_one_side_of_binary_op() {
 +    let mutex = Mutex::new(StateWithField { s: "state".to_owned() });
 +
 +    match mutex.lock().unwrap().s.len() > 1 {
 +        true => {
 +            mutex.lock().unwrap().s.len();
 +        },
 +        false => {},
 +    };
 +
 +    match 1 < mutex.lock().unwrap().s.len() {
 +        true => {
 +            mutex.lock().unwrap().s.len();
 +        },
 +        false => {},
 +    };
 +}
 +
 +// Should trigger lint when both sides of a binary operation creates a temporary with a
 +// significant drop.
 +// To avoid potential unnecessary copies or creating references that would trigger the significant
 +// drop problem, the lint recommends moving the entire binary operation.
 +fn should_trigger_lint_for_accessing_fields_in_mutex_in_both_sides_of_binary_op() {
 +    let mutex1 = Mutex::new(StateWithField { s: "state".to_owned() });
 +    let mutex2 = Mutex::new(StateWithField {
 +        s: "statewithfield".to_owned(),
 +    });
 +
 +    match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() {
 +        true => {
 +            println!(
 +                "{} < {}",
 +                mutex1.lock().unwrap().s.len(),
 +                mutex2.lock().unwrap().s.len()
 +            );
 +        },
 +        false => {},
 +    };
 +
 +    match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() {
 +        true => {
 +            println!(
 +                "{} >= {}",
 +                mutex1.lock().unwrap().s.len(),
 +                mutex2.lock().unwrap().s.len()
 +            );
 +        },
 +        false => {},
 +    };
 +}
 +
 +fn should_not_trigger_lint_for_closure_in_scrutinee() {
 +    let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
 +
 +    let get_mutex_guard = || mutex1.lock().unwrap().s.len();
 +
 +    // Should not trigger lint because the temporary with a significant drop will be dropped
 +    // at the end of the closure, so the MutexGuard will be unlocked and not have a potentially
 +    // surprising lifetime.
 +    match get_mutex_guard() > 1 {
 +        true => {
 +            mutex1.lock().unwrap().s.len();
 +        },
 +        false => {},
 +    };
 +}
 +
 +fn should_trigger_lint_for_return_from_closure_in_scrutinee() {
 +    let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
 +
 +    let get_mutex_guard = || mutex1.lock().unwrap();
 +
 +    // Should trigger lint because the temporary with a significant drop is returned from the
 +    // closure but not used directly in any match arms, so it has a potentially surprising lifetime.
 +    match get_mutex_guard().s.len() > 1 {
 +        true => {
 +            mutex1.lock().unwrap().s.len();
 +        },
 +        false => {},
 +    };
 +}
 +
 +fn should_trigger_lint_for_return_from_match_in_scrutinee() {
 +    let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
 +    let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
 +
 +    let i = 100;
 +
 +    // Should trigger lint because the nested match within the scrutinee returns a temporary with a
 +    // significant drop is but not used directly in any match arms, so it has a potentially
 +    // surprising lifetime.
 +    match match i {
 +        100 => mutex1.lock().unwrap(),
 +        _ => mutex2.lock().unwrap(),
 +    }
 +    .s
 +    .len()
 +        > 1
 +    {
 +        true => {
 +            mutex1.lock().unwrap().s.len();
 +        },
 +        false => {
 +            println!("nothing to do here");
 +        },
 +    };
 +}
 +
 +fn should_trigger_lint_for_return_from_if_in_scrutinee() {
 +    let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
 +    let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
 +
 +    let i = 100;
 +
 +    // Should trigger lint because the nested if-expression within the scrutinee returns a temporary
 +    // with a significant drop is but not used directly in any match arms, so it has a potentially
 +    // surprising lifetime.
 +    match if i > 1 {
 +        mutex1.lock().unwrap()
 +    } else {
 +        mutex2.lock().unwrap()
 +    }
 +    .s
 +    .len()
 +        > 1
 +    {
 +        true => {
 +            mutex1.lock().unwrap().s.len();
 +        },
 +        false => {},
 +    };
 +}
 +
 +fn should_not_trigger_lint_for_if_in_scrutinee() {
 +    let mutex = Mutex::new(StateWithField { s: "state".to_owned() });
 +
 +    let i = 100;
 +
 +    // Should not trigger the lint because the temporary with a significant drop *is* dropped within
 +    // the body of the if-expression nested within the match scrutinee, and therefore does not have
 +    // a potentially surprising lifetime.
 +    match if i > 1 {
 +        mutex.lock().unwrap().s.len() > 1
 +    } else {
 +        false
 +    } {
 +        true => {
 +            mutex.lock().unwrap().s.len();
 +        },
 +        false => {},
 +    };
 +}
 +
 +struct StateWithBoxedMutexGuard {
 +    u: Mutex<u64>,
 +}
 +
 +impl StateWithBoxedMutexGuard {
 +    fn new() -> StateWithBoxedMutexGuard {
 +        StateWithBoxedMutexGuard { u: Mutex::new(42) }
 +    }
 +    fn lock(&self) -> Box<MutexGuard<u64>> {
 +        Box::new(self.u.lock().unwrap())
 +    }
 +}
 +
 +fn should_trigger_lint_for_boxed_mutex_guard() {
 +    let s = StateWithBoxedMutexGuard::new();
 +
 +    // Should trigger lint because a temporary Box holding a type with a significant drop in a match
 +    // scrutinee may have a potentially surprising lifetime.
 +    match s.lock().deref().deref() {
 +        0 | 1 => println!("Value was less than 2"),
 +        _ => println!("Value is {}", s.lock().deref()),
 +    };
 +}
 +
 +struct StateStringWithBoxedMutexGuard {
 +    s: Mutex<String>,
 +}
 +
 +impl StateStringWithBoxedMutexGuard {
 +    fn new() -> StateStringWithBoxedMutexGuard {
 +        StateStringWithBoxedMutexGuard {
 +            s: Mutex::new("A String".to_owned()),
 +        }
 +    }
 +    fn lock(&self) -> Box<MutexGuard<String>> {
 +        Box::new(self.s.lock().unwrap())
 +    }
 +}
 +
 +fn should_trigger_lint_for_boxed_mutex_guard_holding_string() {
 +    let s = StateStringWithBoxedMutexGuard::new();
 +
 +    let matcher = String::from("A String");
 +
 +    // Should trigger lint because a temporary Box holding a type with a significant drop in a match
 +    // scrutinee may have a potentially surprising lifetime.
 +    match s.lock().deref().deref() {
 +        matcher => println!("Value is {}", s.lock().deref()),
 +        _ => println!("Value was not a match"),
 +    };
 +}
 +
 +struct StateWithIntField {
 +    i: u64,
 +}
 +
 +// Should trigger lint when either side of an assign expression contains a temporary with a
 +// significant drop, because the temporary's lifetime will be extended to the end of the match.
 +// To avoid potential unnecessary copies or creating references that would trigger the significant
 +// drop problem, the lint recommends moving the entire binary operation.
 +fn should_trigger_lint_in_assign_expr() {
 +    let mutex = Mutex::new(StateWithIntField { i: 10 });
 +
 +    let mut i = 100;
 +
 +    match mutex.lock().unwrap().i = i {
 +        _ => {
 +            println!("{}", mutex.lock().unwrap().i);
 +        },
 +    };
 +
 +    match i = mutex.lock().unwrap().i {
 +        _ => {
 +            println!("{}", mutex.lock().unwrap().i);
 +        },
 +    };
 +
 +    match mutex.lock().unwrap().i += 1 {
 +        _ => {
 +            println!("{}", mutex.lock().unwrap().i);
 +        },
 +    };
 +
 +    match i += mutex.lock().unwrap().i {
 +        _ => {
 +            println!("{}", mutex.lock().unwrap().i);
 +        },
 +    };
 +}
 +
 +#[derive(Debug)]
 +enum RecursiveEnum {
 +    Foo(Option<Box<RecursiveEnum>>),
 +}
 +
 +#[derive(Debug)]
 +enum GenericRecursiveEnum<T> {
 +    Foo(T, Option<Box<GenericRecursiveEnum<T>>>),
 +}
 +
 +fn should_not_cause_stack_overflow() {
 +    // Test that when a type recursively contains itself, a stack overflow does not occur when
 +    // checking sub-types for significant drops.
 +    let f = RecursiveEnum::Foo(Some(Box::new(RecursiveEnum::Foo(None))));
 +    match f {
 +        RecursiveEnum::Foo(Some(f)) => {
 +            println!("{:?}", f)
 +        },
 +        RecursiveEnum::Foo(f) => {
 +            println!("{:?}", f)
 +        },
 +    }
 +
 +    let f = GenericRecursiveEnum::Foo(1u64, Some(Box::new(GenericRecursiveEnum::Foo(2u64, None))));
 +    match f {
 +        GenericRecursiveEnum::Foo(i, Some(f)) => {
 +            println!("{} {:?}", i, f)
 +        },
 +        GenericRecursiveEnum::Foo(i, f) => {
 +            println!("{} {:?}", i, f)
 +        },
 +    }
 +}
 +
 +fn should_not_produce_lint_for_try_desugar() -> Result<u64, ParseIntError> {
 +    // TryDesugar (i.e. using `?` for a Result type) will turn into a match but is out of scope
 +    // for this lint
 +    let rwlock = RwLock::new("1".to_string());
 +    let result = rwlock.read().unwrap().parse::<u64>()?;
 +    println!("{}", result);
 +    rwlock.write().unwrap().push('2');
 +    Ok(result)
 +}
 +
 +struct ResultReturner {
 +    s: String,
 +}
 +
 +impl ResultReturner {
 +    fn to_number(&self) -> Result<i64, ParseIntError> {
 +        self.s.parse::<i64>()
 +    }
 +}
 +
 +fn should_trigger_lint_for_non_ref_move_and_clone_suggestion() {
 +    let rwlock = RwLock::<ResultReturner>::new(ResultReturner { s: "1".to_string() });
 +    match rwlock.read().unwrap().to_number() {
 +        Ok(n) => println!("Converted to number: {}", n),
 +        Err(e) => println!("Could not convert {} to number", e),
 +    };
 +}
 +
 +fn should_trigger_lint_for_read_write_lock_for_loop() {
 +    // For-in loops desugar to match expressions and are prone to the type of deadlock this lint is
 +    // designed to look for.
 +    let rwlock = RwLock::<Vec<String>>::new(vec!["1".to_string()]);
 +    for s in rwlock.read().unwrap().iter() {
 +        println!("{}", s);
 +    }
 +}
 +
 +fn do_bar(mutex: &Mutex<State>) {
 +    mutex.lock().unwrap().bar();
 +}
 +
 +fn should_trigger_lint_without_significant_drop_in_arm() {
 +    let mutex = Mutex::new(State {});
 +
 +    // Should trigger lint because the lifetime of the temporary MutexGuard is surprising because it
 +    // is preserved until the end of the match, but there is no clear indication that this is the
 +    // case.
 +    match mutex.lock().unwrap().foo() {
 +        true => do_bar(&mutex),
 +        false => {},
 +    };
 +}
 +
++fn should_not_trigger_on_significant_iterator_drop() {
++    let lines = std::io::stdin().lines();
++    for line in lines {
++        println!("foo: {}", line.unwrap());
++    }
++}
++
 +fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..74f05ec1f658a13de195ea294ec65ab302b2ec8e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++#![warn(clippy::std_instead_of_core)]
++#![allow(unused_imports)]
++
++extern crate alloc;
++
++#[warn(clippy::std_instead_of_core)]
++fn std_instead_of_core() {
++    // Regular import
++    use std::hash::Hasher;
++    // Absolute path
++    use ::std::hash::Hash;
++
++    // Multiple imports
++    use std::fmt::{Debug, Result};
++
++    // Function calls
++    let ptr = std::ptr::null::<u32>();
++    let ptr_mut = ::std::ptr::null_mut::<usize>();
++
++    // Types
++    let cell = std::cell::Cell::new(8u32);
++    let cell_absolute = ::std::cell::Cell::new(8u32);
++}
++
++#[warn(clippy::std_instead_of_alloc)]
++fn std_instead_of_alloc() {
++    use std::vec::Vec;
++}
++
++#[warn(clippy::alloc_instead_of_core)]
++fn alloc_instead_of_core() {
++    use alloc::slice::from_ref;
++}
++
++fn main() {
++    std_instead_of_core();
++    std_instead_of_alloc();
++    alloc_instead_of_core();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9f1644835c10d129537b3bf7963efd3b7cbd6243
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,85 @@@
++error: used import from `std` instead of `core`
++  --> $DIR/std_instead_of_core.rs:9:9
++   |
++LL |     use std::hash::Hasher;
++   |         ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::std-instead-of-core` implied by `-D warnings`
++   = help: consider importing the item from `core`
++
++error: used import from `std` instead of `core`
++  --> $DIR/std_instead_of_core.rs:11:9
++   |
++LL |     use ::std::hash::Hash;
++   |         ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider importing the item from `core`
++
++error: used import from `std` instead of `core`
++  --> $DIR/std_instead_of_core.rs:14:20
++   |
++LL |     use std::fmt::{Debug, Result};
++   |                    ^^^^^
++   |
++   = help: consider importing the item from `core`
++
++error: used import from `std` instead of `core`
++  --> $DIR/std_instead_of_core.rs:14:27
++   |
++LL |     use std::fmt::{Debug, Result};
++   |                           ^^^^^^
++   |
++   = help: consider importing the item from `core`
++
++error: used import from `std` instead of `core`
++  --> $DIR/std_instead_of_core.rs:17:15
++   |
++LL |     let ptr = std::ptr::null::<u32>();
++   |               ^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider importing the item from `core`
++
++error: used import from `std` instead of `core`
++  --> $DIR/std_instead_of_core.rs:18:19
++   |
++LL |     let ptr_mut = ::std::ptr::null_mut::<usize>();
++   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider importing the item from `core`
++
++error: used import from `std` instead of `core`
++  --> $DIR/std_instead_of_core.rs:21:16
++   |
++LL |     let cell = std::cell::Cell::new(8u32);
++   |                ^^^^^^^^^^^^^^^
++   |
++   = help: consider importing the item from `core`
++
++error: used import from `std` instead of `core`
++  --> $DIR/std_instead_of_core.rs:22:25
++   |
++LL |     let cell_absolute = ::std::cell::Cell::new(8u32);
++   |                         ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider importing the item from `core`
++
++error: used import from `std` instead of `alloc`
++  --> $DIR/std_instead_of_core.rs:27:9
++   |
++LL |     use std::vec::Vec;
++   |         ^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings`
++   = help: consider importing the item from `alloc`
++
++error: used import from `alloc` instead of `core`
++  --> $DIR/std_instead_of_core.rs:32:9
++   |
++LL |     use alloc::slice::from_ref;
++   |         ^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings`
++   = help: consider importing the item from `core`
++
++error: aborting due to 10 previous errors
++
index a21d4c5d637daec8678ce052656d1d86bff631aa,0000000000000000000000000000000000000000..a5751c58aab8f09f1b7c032f281d4281e001e2d5
mode 100644,000000..100644
--- /dev/null
@@@ -1,101 -1,0 +1,212 @@@
 +#![deny(clippy::trait_duplication_in_bounds)]
++#![allow(unused)]
 +
 +use std::collections::BTreeMap;
 +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
 +
 +fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
 +where
 +    T: Clone,
 +    T: Default,
 +{
 +    unimplemented!();
 +}
 +
 +fn good_bar<T: Clone + Default>(arg: T) {
 +    unimplemented!();
 +}
 +
 +fn good_foo<T>(arg: T)
 +where
 +    T: Clone + Default,
 +{
 +    unimplemented!();
 +}
 +
 +fn good_foobar<T: Default>(arg: T)
 +where
 +    T: Clone,
 +{
 +    unimplemented!();
 +}
 +
 +trait T: Default {
 +    fn f()
 +    where
 +        Self: Default;
 +}
 +
 +trait U: Default {
 +    fn f()
 +    where
 +        Self: Clone;
 +}
 +
 +trait ZZ: Default {
 +    fn g();
 +    fn h();
 +    fn f()
 +    where
 +        Self: Default + Clone;
 +}
 +
 +trait BadTrait: Default + Clone {
 +    fn f()
 +    where
 +        Self: Default + Clone;
 +    fn g()
 +    where
 +        Self: Default;
 +    fn h()
 +    where
 +        Self: Copy;
 +}
 +
 +#[derive(Default, Clone)]
 +struct Life;
 +
 +impl T for Life {
 +    // this should not warn
 +    fn f() {}
 +}
 +
 +impl U for Life {
 +    // this should not warn
 +    fn f() {}
 +}
 +
 +// should not warn
 +trait Iter: Iterator {
 +    fn into_group_btreemap<K, V>(self) -> BTreeMap<K, Vec<V>>
 +    where
 +        Self: Iterator<Item = (K, V)> + Sized,
 +        K: Ord + Eq,
 +    {
 +        unimplemented!();
 +    }
 +}
 +
 +struct Foo;
 +
 +trait FooIter: Iterator<Item = Foo> {
 +    fn bar()
 +    where
 +        Self: Iterator<Item = Foo>,
 +    {
 +    }
 +}
 +
 +// This should not lint
 +fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
 +
++mod repeated_where_clauses_or_trait_bounds {
++    fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
++        unimplemented!();
++    }
++
++    fn bad_bar<T, U>(arg0: T, arg1: U)
++    where
++        T: Clone + Clone + Clone + Copy,
++        U: Clone + Copy,
++    {
++        unimplemented!();
++    }
++
++    fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) {
++        unimplemented!();
++    }
++
++    fn good_foo<T, U>(arg0: T, arg1: U)
++    where
++        T: Clone + Copy,
++        U: Clone + Copy,
++    {
++        unimplemented!();
++    }
++
++    trait GoodSelfTraitBound: Clone + Copy {
++        fn f();
++    }
++
++    trait GoodSelfWhereClause {
++        fn f()
++        where
++            Self: Clone + Copy;
++    }
++
++    trait BadSelfTraitBound: Clone + Clone + Clone {
++        fn f();
++    }
++
++    trait BadSelfWhereClause {
++        fn f()
++        where
++            Self: Clone + Clone + Clone;
++    }
++
++    trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> {
++        fn f();
++    }
++
++    trait GoodWhereClause<T, U> {
++        fn f()
++        where
++            T: Clone + Copy,
++            U: Clone + Copy;
++    }
++
++    trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
++        fn f();
++    }
++
++    trait BadWhereClause<T, U> {
++        fn f()
++        where
++            T: Clone + Clone + Clone + Copy,
++            U: Clone + Copy;
++    }
++
++    struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> {
++        t: T,
++        u: U,
++    }
++
++    impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> {
++        // this should not warn
++        fn f() {}
++    }
++
++    struct GoodStructWhereClause;
++
++    impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause
++    where
++        T: Clone + Copy,
++        U: Clone + Copy,
++    {
++        // this should not warn
++        fn f() {}
++    }
++
++    fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
++
++    trait GenericTrait<T> {}
++
++    // This should not warn but currently does see #8757
++    fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
++        unimplemented!();
++    }
++
++    fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
++        unimplemented!();
++    }
++
++    mod foo {
++        pub trait Clone {}
++    }
++
++    fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
++        unimplemented!();
++    }
++}
++
 +fn main() {}
index 6f8c8e47dfbf1fe56589a06f80017513e69167e8,0000000000000000000000000000000000000000..7ef04e52708f4c91e16c67c6dddf6cc786779c9b
mode 100644,000000..100644
--- /dev/null
@@@ -1,71 -1,0 +1,167 @@@
-   --> $DIR/trait_duplication_in_bounds.rs:6:15
 +error: this trait bound is already specified in the where clause
-   --> $DIR/trait_duplication_in_bounds.rs:6:23
++  --> $DIR/trait_duplication_in_bounds.rs:7:15
 +   |
 +LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
 +   |               ^^^^^
 +   |
 +note: the lint level is defined here
 +  --> $DIR/trait_duplication_in_bounds.rs:1:9
 +   |
 +LL | #![deny(clippy::trait_duplication_in_bounds)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   = help: consider removing this trait bound
 +
 +error: this trait bound is already specified in the where clause
-   --> $DIR/trait_duplication_in_bounds.rs:35:15
++  --> $DIR/trait_duplication_in_bounds.rs:7:23
 +   |
 +LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
 +   |                       ^^^^^^^
 +   |
 +   = help: consider removing this trait bound
 +
 +error: this trait bound is already specified in trait declaration
-   --> $DIR/trait_duplication_in_bounds.rs:49:15
++  --> $DIR/trait_duplication_in_bounds.rs:36:15
 +   |
 +LL |         Self: Default;
 +   |               ^^^^^^^
 +   |
 +   = help: consider removing this trait bound
 +
 +error: this trait bound is already specified in trait declaration
-   --> $DIR/trait_duplication_in_bounds.rs:55:15
++  --> $DIR/trait_duplication_in_bounds.rs:50:15
 +   |
 +LL |         Self: Default + Clone;
 +   |               ^^^^^^^
 +   |
 +   = help: consider removing this trait bound
 +
 +error: this trait bound is already specified in trait declaration
-   --> $DIR/trait_duplication_in_bounds.rs:55:25
++  --> $DIR/trait_duplication_in_bounds.rs:56:15
 +   |
 +LL |         Self: Default + Clone;
 +   |               ^^^^^^^
 +   |
 +   = help: consider removing this trait bound
 +
 +error: this trait bound is already specified in trait declaration
-   --> $DIR/trait_duplication_in_bounds.rs:58:15
++  --> $DIR/trait_duplication_in_bounds.rs:56:25
 +   |
 +LL |         Self: Default + Clone;
 +   |                         ^^^^^
 +   |
 +   = help: consider removing this trait bound
 +
 +error: this trait bound is already specified in trait declaration
-   --> $DIR/trait_duplication_in_bounds.rs:93:15
++  --> $DIR/trait_duplication_in_bounds.rs:59:15
 +   |
 +LL |         Self: Default;
 +   |               ^^^^^^^
 +   |
 +   = help: consider removing this trait bound
 +
 +error: this trait bound is already specified in trait declaration
- error: aborting due to 8 previous errors
++  --> $DIR/trait_duplication_in_bounds.rs:94:15
 +   |
 +LL |         Self: Iterator<Item = Foo>,
 +   |               ^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider removing this trait bound
 +
++error: this trait bound is already specified in the where clause
++  --> $DIR/trait_duplication_in_bounds.rs:103:19
++   |
++LL |     fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
++   |                   ^^^^^
++   |
++   = help: consider removing this trait bound
++
++error: these bounds contain repeated elements
++  --> $DIR/trait_duplication_in_bounds.rs:103:19
++   |
++LL |     fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
++   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
++
++error: this trait bound is already specified in the where clause
++  --> $DIR/trait_duplication_in_bounds.rs:109:12
++   |
++LL |         T: Clone + Clone + Clone + Copy,
++   |            ^^^^^
++   |
++   = help: consider removing this trait bound
++
++error: these where clauses contain repeated elements
++  --> $DIR/trait_duplication_in_bounds.rs:109:12
++   |
++LL |         T: Clone + Clone + Clone + Copy,
++   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
++
++error: these bounds contain repeated elements
++  --> $DIR/trait_duplication_in_bounds.rs:137:30
++   |
++LL |     trait BadSelfTraitBound: Clone + Clone + Clone {
++   |                              ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
++
++error: these where clauses contain repeated elements
++  --> $DIR/trait_duplication_in_bounds.rs:144:19
++   |
++LL |             Self: Clone + Clone + Clone;
++   |                   ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
++
++error: this trait bound is already specified in the where clause
++  --> $DIR/trait_duplication_in_bounds.rs:158:28
++   |
++LL |     trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
++   |                            ^^^^^
++   |
++   = help: consider removing this trait bound
++
++error: these bounds contain repeated elements
++  --> $DIR/trait_duplication_in_bounds.rs:158:28
++   |
++LL |     trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
++   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
++
++error: these where clauses contain repeated elements
++  --> $DIR/trait_duplication_in_bounds.rs:165:16
++   |
++LL |             T: Clone + Clone + Clone + Copy,
++   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
++
++error: this trait bound is already specified in the where clause
++  --> $DIR/trait_duplication_in_bounds.rs:195:24
++   |
++LL |     fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
++   |                        ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider removing this trait bound
++
++error: this trait bound is already specified in the where clause
++  --> $DIR/trait_duplication_in_bounds.rs:199:23
++   |
++LL |     fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
++   |                       ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider removing this trait bound
++
++error: these bounds contain repeated elements
++  --> $DIR/trait_duplication_in_bounds.rs:199:23
++   |
++LL |     fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
++   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u32> + GenericTrait<u64>`
++
++error: this trait bound is already specified in the where clause
++  --> $DIR/trait_duplication_in_bounds.rs:207:26
++   |
++LL |     fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
++   |                          ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider removing this trait bound
++
++error: these bounds contain repeated elements
++  --> $DIR/trait_duplication_in_bounds.rs:207:26
++   |
++LL |     fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + foo::Clone`
++
++error: aborting due to 22 previous errors
 +
index b425cdd6cbfd441d45b3cacce9a3f2c60daa5b7e,0000000000000000000000000000000000000000..539239fc18f9449f8b582b68a930f39ae2fcf664
mode 100644,000000..100644
--- /dev/null
@@@ -1,77 -1,0 +1,77 @@@
- // These two warnings currrently cover the cases transmutes_expressible_as_ptr_casts
 +// run-rustfix
 +#![warn(clippy::transmutes_expressible_as_ptr_casts)]
++// These two warnings currently cover the cases transmutes_expressible_as_ptr_casts
 +// would otherwise be responsible for
 +#![warn(clippy::useless_transmute)]
 +#![warn(clippy::transmute_ptr_to_ptr)]
 +#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)]
 +
 +use std::mem::{size_of, transmute};
 +
 +// rustc_typeck::check::cast contains documentation about when a cast `e as U` is
 +// valid, which we quote from below.
 +fn main() {
 +    // We should see an error message for each transmute, and no error messages for
 +    // the casts, since the casts are the recommended fixes.
 +
 +    // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast
 +    let _ptr_i32_transmute = unsafe { usize::MAX as *const i32 };
 +    let ptr_i32 = usize::MAX as *const i32;
 +
 +    // e has type *T, U is *U_0, and either U_0: Sized ...
 +    let _ptr_i8_transmute = unsafe { ptr_i32 as *const i8 };
 +    let _ptr_i8 = ptr_i32 as *const i8;
 +
 +    let slice_ptr = &[0, 1, 2, 3] as *const [i32];
 +
 +    // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast
 +    let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u32] };
 +    let _ptr_to_unsized = slice_ptr as *const [u32];
 +    // TODO: We could try testing vtable casts here too, but maybe
 +    // we should wait until std::raw::TraitObject is stabilized?
 +
 +    // e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast
 +    let _usize_from_int_ptr_transmute = unsafe { ptr_i32 as usize };
 +    let _usize_from_int_ptr = ptr_i32 as usize;
 +
 +    let array_ref: &[i32; 4] = &[1, 2, 3, 4];
 +
 +    // e has type &[T; n] and U is *const T; array-ptr-cast
 +    let _array_ptr_transmute = unsafe { array_ref as *const [i32; 4] };
 +    let _array_ptr = array_ref as *const [i32; 4];
 +
 +    fn foo(_: usize) -> u8 {
 +        42
 +    }
 +
 +    // e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast
 +    let _usize_ptr_transmute = unsafe { foo as *const usize };
 +    let _usize_ptr_transmute = foo as *const usize;
 +
 +    // e is a function pointer type and U is an integer; fptr-addr-cast
 +    let _usize_from_fn_ptr_transmute = unsafe { foo as usize };
 +    let _usize_from_fn_ptr = foo as *const usize;
 +}
 +
 +// If a ref-to-ptr cast of this form where the pointer type points to a type other
 +// than the referenced type, calling `CastCheck::do_check` has been observed to
 +// cause an ICE error message. `do_check` is currently called inside the
 +// `transmutes_expressible_as_ptr_casts` check, but other, more specific lints
 +// currently prevent it from being called in these cases. This test is meant to
 +// fail if the ordering of the checks ever changes enough to cause these cases to
 +// fall through into `do_check`.
 +fn trigger_do_check_to_emit_error(in_param: &[i32; 1]) -> *const u8 {
 +    unsafe { in_param as *const [i32; 1] as *const u8 }
 +}
 +
 +#[repr(C)]
 +struct Single(u64);
 +
 +#[repr(C)]
 +struct Pair(u32, u32);
 +
 +fn cannot_be_expressed_as_pointer_cast(in_param: Single) -> Pair {
 +    assert_eq!(size_of::<Single>(), size_of::<Pair>());
 +
 +    unsafe { transmute::<Single, Pair>(in_param) }
 +}
index 8fd57c5965279d96766ee8748195cca562277399,0000000000000000000000000000000000000000..b9e446dc89a95f2160736ec83b80689642c4c0e5
mode 100644,000000..100644
--- /dev/null
@@@ -1,77 -1,0 +1,77 @@@
- // These two warnings currrently cover the cases transmutes_expressible_as_ptr_casts
 +// run-rustfix
 +#![warn(clippy::transmutes_expressible_as_ptr_casts)]
++// These two warnings currently cover the cases transmutes_expressible_as_ptr_casts
 +// would otherwise be responsible for
 +#![warn(clippy::useless_transmute)]
 +#![warn(clippy::transmute_ptr_to_ptr)]
 +#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)]
 +
 +use std::mem::{size_of, transmute};
 +
 +// rustc_typeck::check::cast contains documentation about when a cast `e as U` is
 +// valid, which we quote from below.
 +fn main() {
 +    // We should see an error message for each transmute, and no error messages for
 +    // the casts, since the casts are the recommended fixes.
 +
 +    // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast
 +    let _ptr_i32_transmute = unsafe { transmute::<usize, *const i32>(usize::MAX) };
 +    let ptr_i32 = usize::MAX as *const i32;
 +
 +    // e has type *T, U is *U_0, and either U_0: Sized ...
 +    let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) };
 +    let _ptr_i8 = ptr_i32 as *const i8;
 +
 +    let slice_ptr = &[0, 1, 2, 3] as *const [i32];
 +
 +    // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast
 +    let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u32]>(slice_ptr) };
 +    let _ptr_to_unsized = slice_ptr as *const [u32];
 +    // TODO: We could try testing vtable casts here too, but maybe
 +    // we should wait until std::raw::TraitObject is stabilized?
 +
 +    // e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast
 +    let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) };
 +    let _usize_from_int_ptr = ptr_i32 as usize;
 +
 +    let array_ref: &[i32; 4] = &[1, 2, 3, 4];
 +
 +    // e has type &[T; n] and U is *const T; array-ptr-cast
 +    let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) };
 +    let _array_ptr = array_ref as *const [i32; 4];
 +
 +    fn foo(_: usize) -> u8 {
 +        42
 +    }
 +
 +    // e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast
 +    let _usize_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, *const usize>(foo) };
 +    let _usize_ptr_transmute = foo as *const usize;
 +
 +    // e is a function pointer type and U is an integer; fptr-addr-cast
 +    let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, usize>(foo) };
 +    let _usize_from_fn_ptr = foo as *const usize;
 +}
 +
 +// If a ref-to-ptr cast of this form where the pointer type points to a type other
 +// than the referenced type, calling `CastCheck::do_check` has been observed to
 +// cause an ICE error message. `do_check` is currently called inside the
 +// `transmutes_expressible_as_ptr_casts` check, but other, more specific lints
 +// currently prevent it from being called in these cases. This test is meant to
 +// fail if the ordering of the checks ever changes enough to cause these cases to
 +// fall through into `do_check`.
 +fn trigger_do_check_to_emit_error(in_param: &[i32; 1]) -> *const u8 {
 +    unsafe { transmute::<&[i32; 1], *const u8>(in_param) }
 +}
 +
 +#[repr(C)]
 +struct Single(u64);
 +
 +#[repr(C)]
 +struct Pair(u32, u32);
 +
 +fn cannot_be_expressed_as_pointer_cast(in_param: Single) -> Pair {
 +    assert_eq!(size_of::<Single>(), size_of::<Pair>());
 +
 +    unsafe { transmute::<Single, Pair>(in_param) }
 +}
index d11432f9046111d4ff77f7279250bc7f10083975,0000000000000000000000000000000000000000..2eca1f4701c9fdaeccb0da0160b67bea538f4da1
mode 100644,000000..100644
--- /dev/null
@@@ -1,85 -1,0 +1,97 @@@
 +#![deny(clippy::type_repetition_in_bounds)]
 +
 +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
 +
 +pub fn foo<T>(_t: T)
 +where
 +    T: Copy,
 +    T: Clone,
 +{
 +    unimplemented!();
 +}
 +
 +pub fn bar<T, U>(_t: T, _u: U)
 +where
 +    T: Copy,
 +    U: Clone,
 +{
 +    unimplemented!();
 +}
 +
 +// Threshold test (see #4380)
 +trait LintBounds
 +where
 +    Self: Clone,
 +    Self: Copy + Default + Ord,
 +    Self: Add<Output = Self> + AddAssign + Sub<Output = Self> + SubAssign,
 +    Self: Mul<Output = Self> + MulAssign + Div<Output = Self> + DivAssign,
 +{
 +}
 +
 +trait LotsOfBounds
 +where
 +    Self: Clone + Copy + Default + Ord,
 +    Self: Add<Output = Self> + AddAssign + Sub<Output = Self> + SubAssign,
 +    Self: Mul<Output = Self> + MulAssign + Div<Output = Self> + DivAssign,
 +{
 +}
 +
 +// Generic distinction (see #4323)
 +mod issue4323 {
 +    pub struct Foo<A>(A);
 +    pub struct Bar<A, B> {
 +        a: Foo<A>,
 +        b: Foo<B>,
 +    }
 +
 +    impl<A, B> Unpin for Bar<A, B>
 +    where
 +        Foo<A>: Unpin,
 +        Foo<B>: Unpin,
 +    {
 +    }
 +}
 +
 +// Extern macros shouldn't lint (see #4326)
 +extern crate serde;
 +mod issue4326 {
 +    use serde::{Deserialize, Serialize};
 +
 +    trait Foo {}
 +    impl Foo for String {}
 +
 +    #[derive(Debug, Serialize, Deserialize)]
 +    struct Bar<S>
 +    where
 +        S: Foo,
 +    {
 +        foo: S,
 +    }
 +}
 +
 +// Issue #7360
 +struct Foo<T, U>
 +where
 +    T: Clone,
 +    U: Clone,
 +{
 +    t: T,
 +    u: U,
 +}
 +
++// Check for the `?` in `?Sized`
++pub fn f<T: ?Sized>()
++where
++    T: Clone,
++{
++}
++pub fn g<T: Clone>()
++where
++    T: ?Sized,
++{
++}
++
 +// This should not lint
 +fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
 +
 +fn main() {}
index 148c19c7d0701dc2910de718d175a03122d26e03,0000000000000000000000000000000000000000..1d88714814d477007bc0dc6afc60f0fee9bb95a4
mode 100644,000000..100644
--- /dev/null
@@@ -1,23 -1,0 +1,39 @@@
- error: aborting due to 2 previous errors
 +error: this type has already been used as a bound predicate
 +  --> $DIR/type_repetition_in_bounds.rs:8:5
 +   |
 +LL |     T: Clone,
 +   |     ^^^^^^^^
 +   |
 +note: the lint level is defined here
 +  --> $DIR/type_repetition_in_bounds.rs:1:9
 +   |
 +LL | #![deny(clippy::type_repetition_in_bounds)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   = help: consider combining the bounds: `T: Copy + Clone`
 +
 +error: this type has already been used as a bound predicate
 +  --> $DIR/type_repetition_in_bounds.rs:25:5
 +   |
 +LL |     Self: Copy + Default + Ord,
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord`
 +
++error: this type has already been used as a bound predicate
++  --> $DIR/type_repetition_in_bounds.rs:85:5
++   |
++LL |     T: Clone,
++   |     ^^^^^^^^
++   |
++   = help: consider combining the bounds: `T: ?Sized + Clone`
++
++error: this type has already been used as a bound predicate
++  --> $DIR/type_repetition_in_bounds.rs:90:5
++   |
++LL |     T: ?Sized,
++   |     ^^^^^^^^^
++   |
++   = help: consider combining the bounds: `T: Clone + ?Sized`
++
++error: aborting due to 4 previous errors
 +
index 33b6a82f9d2c32cefabdc09ca2c4ec6786039e4b,0000000000000000000000000000000000000000..08aee4332151446a6fdd17603bd651cae7ee7306
mode 100644,000000..100644
--- /dev/null
@@@ -1,488 -1,0 +1,493 @@@
-         // 
 +// aux-build:proc_macro_unsafe.rs
 +
 +#![warn(clippy::undocumented_unsafe_blocks)]
 +#![allow(clippy::let_unit_value, clippy::missing_safety_doc)]
 +
 +extern crate proc_macro_unsafe;
 +
 +// Valid comments
 +
 +fn nested_local() {
 +    let _ = {
 +        let _ = {
 +            // SAFETY:
 +            let _ = unsafe {};
 +        };
 +    };
 +}
 +
 +fn deep_nest() {
 +    let _ = {
 +        let _ = {
 +            // SAFETY:
 +            let _ = unsafe {};
 +
 +            // Safety:
 +            unsafe {};
 +
 +            let _ = {
 +                let _ = {
 +                    let _ = {
 +                        let _ = {
 +                            let _ = {
 +                                // Safety:
 +                                let _ = unsafe {};
 +
 +                                // SAFETY:
 +                                unsafe {};
 +                            };
 +                        };
 +                    };
 +
 +                    // Safety:
 +                    unsafe {};
 +                };
 +            };
 +        };
 +
 +        // Safety:
 +        unsafe {};
 +    };
 +
 +    // SAFETY:
 +    unsafe {};
 +}
 +
 +fn local_tuple_expression() {
 +    // Safety:
 +    let _ = (42, unsafe {});
 +}
 +
 +fn line_comment() {
 +    // Safety:
 +    unsafe {}
 +}
 +
 +fn line_comment_newlines() {
 +    // SAFETY:
 +
 +    unsafe {}
 +}
 +
 +fn line_comment_empty() {
 +    // Safety:
 +    //
 +    //
 +    //
 +    unsafe {}
 +}
 +
 +fn line_comment_with_extras() {
 +    // This is a description
 +    // Safety:
 +    unsafe {}
 +}
 +
 +fn block_comment() {
 +    /* Safety: */
 +    unsafe {}
 +}
 +
 +fn block_comment_newlines() {
 +    /* SAFETY: */
 +
 +    unsafe {}
 +}
 +
 +fn block_comment_with_extras() {
 +    /* This is a description
 +     * SAFETY:
 +     */
 +    unsafe {}
 +}
 +
 +fn block_comment_terminator_same_line() {
 +    /* This is a description
 +     * Safety: */
 +    unsafe {}
 +}
 +
 +fn buried_safety() {
 +    // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
 +    // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
 +    // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
 +    // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
 +    // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
 +    // laborum. Safety:
 +    // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi
 +    // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio
 +    // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl
 +    // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus.
 +    unsafe {}
 +}
 +
 +fn safety_with_prepended_text() {
 +    // This is a test. safety:
 +    unsafe {}
 +}
 +
 +fn local_line_comment() {
 +    // Safety:
 +    let _ = unsafe {};
 +}
 +
 +fn local_block_comment() {
 +    /* SAFETY: */
 +    let _ = unsafe {};
 +}
 +
 +fn comment_array() {
 +    // Safety:
 +    let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
 +}
 +
 +fn comment_tuple() {
 +    // sAFETY:
 +    let _ = (42, unsafe {}, "test", unsafe {});
 +}
 +
 +fn comment_unary() {
 +    // SAFETY:
 +    let _ = *unsafe { &42 };
 +}
 +
 +#[allow(clippy::match_single_binding)]
 +fn comment_match() {
 +    // SAFETY:
 +    let _ = match unsafe {} {
 +        _ => {},
 +    };
 +}
 +
 +fn comment_addr_of() {
 +    // Safety:
 +    let _ = &unsafe {};
 +}
 +
 +fn comment_repeat() {
 +    // Safety:
 +    let _ = [unsafe {}; 5];
 +}
 +
 +fn comment_macro_call() {
 +    macro_rules! t {
 +        ($b:expr) => {
 +            $b
 +        };
 +    }
 +
 +    t!(
 +        // SAFETY:
 +        unsafe {}
 +    );
 +}
 +
 +fn comment_macro_def() {
 +    macro_rules! t {
 +        () => {
 +            // Safety:
 +            unsafe {}
 +        };
 +    }
 +
 +    t!();
 +}
 +
 +fn non_ascii_comment() {
 +    // ॐ᧻໒ SaFeTy: ௵∰
 +    unsafe {};
 +}
 +
 +fn local_commented_block() {
 +    let _ =
 +        // safety:
 +        unsafe {};
 +}
 +
 +fn local_nest() {
 +    // safety:
 +    let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})];
 +}
 +
 +fn in_fn_call(x: *const u32) {
 +    fn f(x: u32) {}
 +
 +    // Safety: reason
 +    f(unsafe { *x });
 +}
 +
 +fn multi_in_fn_call(x: *const u32) {
 +    fn f(x: u32, y: u32) {}
 +
 +    // Safety: reason
 +    f(unsafe { *x }, unsafe { *x });
 +}
 +
 +fn in_multiline_fn_call(x: *const u32) {
 +    fn f(x: u32, y: u32) {}
 +
 +    f(
 +        // Safety: reason
 +        unsafe { *x },
 +        0,
 +    );
 +}
 +
 +fn in_macro_call(x: *const u32) {
 +    // Safety: reason
 +    println!("{}", unsafe { *x });
 +}
 +
 +fn in_multiline_macro_call(x: *const u32) {
 +    println!(
 +        "{}",
 +        // Safety: reason
 +        unsafe { *x },
 +    );
 +}
 +
 +fn from_proc_macro() {
 +    proc_macro_unsafe::unsafe_block!(token);
 +}
 +
++fn in_closure(x: *const u32) {
++    // Safety: reason
++    let _ = || unsafe { *x };
++}
++
 +// Invalid comments
 +
 +#[rustfmt::skip]
 +fn inline_block_comment() {
 +    /* Safety: */ unsafe {}
 +}
 +
 +fn no_comment() {
 +    unsafe {}
 +}
 +
 +fn no_comment_array() {
 +    let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
 +}
 +
 +fn no_comment_tuple() {
 +    let _ = (42, unsafe {}, "test", unsafe {});
 +}
 +
 +fn no_comment_unary() {
 +    let _ = *unsafe { &42 };
 +}
 +
 +#[allow(clippy::match_single_binding)]
 +fn no_comment_match() {
 +    let _ = match unsafe {} {
 +        _ => {},
 +    };
 +}
 +
 +fn no_comment_addr_of() {
 +    let _ = &unsafe {};
 +}
 +
 +fn no_comment_repeat() {
 +    let _ = [unsafe {}; 5];
 +}
 +
 +fn local_no_comment() {
 +    let _ = unsafe {};
 +}
 +
 +fn no_comment_macro_call() {
 +    macro_rules! t {
 +        ($b:expr) => {
 +            $b
 +        };
 +    }
 +
 +    t!(unsafe {});
 +}
 +
 +fn no_comment_macro_def() {
 +    macro_rules! t {
 +        () => {
 +            unsafe {}
 +        };
 +    }
 +
 +    t!();
 +}
 +
 +fn trailing_comment() {
 +    unsafe {} // SAFETY:
 +}
 +
 +fn internal_comment() {
 +    unsafe {
 +        // SAFETY:
 +    }
 +}
 +
 +fn interference() {
 +    // SAFETY
 +
 +    let _ = 42;
 +
 +    unsafe {};
 +}
 +
 +pub fn print_binary_tree() {
 +    println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
 +}
 +
 +mod unsafe_impl_smoke_test {
 +    unsafe trait A {}
 +
 +    // error: no safety comment
 +    unsafe impl A for () {}
 +
 +    // Safety: ok
 +    unsafe impl A for (i32) {}
 +
 +    mod sub_mod {
 +        // error:
 +        unsafe impl B for (u32) {}
 +        unsafe trait B {}
 +    }
 +
 +    #[rustfmt::skip]
 +    mod sub_mod2 {
-         // 
++        //
 +        // SAFETY: ok
++        //
 +
 +        unsafe impl B for (u32) {}
 +        unsafe trait B {}
 +    }
 +}
 +
 +mod unsafe_impl_from_macro {
 +    unsafe trait T {}
 +
 +    // error
 +    macro_rules! no_safety_comment {
 +        ($t:ty) => {
 +            unsafe impl T for $t {}
 +        };
 +    }
 +
 +    // ok
 +    no_safety_comment!(());
 +
 +    // ok
 +    macro_rules! with_safety_comment {
 +        ($t:ty) => {
 +            // SAFETY:
 +            unsafe impl T for $t {}
 +        };
 +    }
 +
 +    // ok
 +    with_safety_comment!((i32));
 +}
 +
 +mod unsafe_impl_macro_and_not_macro {
 +    unsafe trait T {}
 +
 +    // error
 +    macro_rules! no_safety_comment {
 +        ($t:ty) => {
 +            unsafe impl T for $t {}
 +        };
 +    }
 +
 +    // ok
 +    no_safety_comment!(());
 +
 +    // error
 +    unsafe impl T for (i32) {}
 +
 +    // ok
 +    no_safety_comment!(u32);
 +
 +    // error
 +    unsafe impl T for (bool) {}
 +}
 +
 +#[rustfmt::skip]
 +mod unsafe_impl_valid_comment {
 +    unsafe trait SaFety {}
 +    // SaFety:
 +    unsafe impl SaFety for () {}
 +
 +    unsafe trait MultiLineComment {}
 +    // The following impl is safe
 +    // ...
 +    // Safety: reason
 +    unsafe impl MultiLineComment for () {}
 +
 +    unsafe trait NoAscii {}
 +    // 安全 SAFETY: 以下のコードは安全です
 +    unsafe impl NoAscii for () {}
 +
 +    unsafe trait InlineAndPrecedingComment {}
 +    // SAFETY:
 +    /* comment */ unsafe impl InlineAndPrecedingComment for () {}
 +
 +    unsafe trait BuriedSafety {}
 +    // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
 +    // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
 +    // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
 +    // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
 +    // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
 +    // laborum. Safety:
 +    // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi
 +    // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio
 +    // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl
 +    // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus.
 +    unsafe impl BuriedSafety for () {}
 +
 +    unsafe trait MultiLineBlockComment {}
 +    /* This is a description
 +     * Safety: */
 +    unsafe impl MultiLineBlockComment for () {}
 +}
 +
 +#[rustfmt::skip]
 +mod unsafe_impl_invalid_comment {
 +    unsafe trait NoComment {}
 +
 +    unsafe impl NoComment for () {}
 +
 +    unsafe trait InlineComment {}
 +
 +    /* SAFETY: */ unsafe impl InlineComment for () {}
 +
 +    unsafe trait TrailingComment {}
 +
 +    unsafe impl TrailingComment for () {} // SAFETY:
 +
 +    unsafe trait Interference {}
 +    // SAFETY:
 +    const BIG_NUMBER: i32 = 1000000;
 +    unsafe impl Interference for () {}
 +}
 +
 +unsafe trait ImplInFn {}
 +
 +fn impl_in_fn() {
 +    // error
 +    unsafe impl ImplInFn for () {}
 +
 +    // SAFETY: ok
 +    unsafe impl ImplInFn for (i32) {}
 +}
 +
 +unsafe trait CrateRoot {}
 +
 +// error
 +unsafe impl CrateRoot for () {}
 +
 +// SAFETY: ok
 +unsafe impl CrateRoot for (i32) {}
 +
 +fn main() {}
index b79949e9d06d6af18a3602f980fe15f5fe30a6ec,0000000000000000000000000000000000000000..c6a2127443befce4441fcc68018784fc0b362377
mode 100644,000000..100644
--- /dev/null
@@@ -1,267 -1,0 +1,267 @@@
-   --> $DIR/undocumented_unsafe_blocks.rs:257:19
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:261:5
++  --> $DIR/undocumented_unsafe_blocks.rs:262:19
 +   |
 +LL |     /* Safety: */ unsafe {}
 +   |                   ^^^^^^^^^
 +   |
 +   = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:265:14
++  --> $DIR/undocumented_unsafe_blocks.rs:266:5
 +   |
 +LL |     unsafe {}
 +   |     ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:265:29
++  --> $DIR/undocumented_unsafe_blocks.rs:270:14
 +   |
 +LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
 +   |              ^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:265:48
++  --> $DIR/undocumented_unsafe_blocks.rs:270:29
 +   |
 +LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
 +   |                             ^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:269:18
++  --> $DIR/undocumented_unsafe_blocks.rs:270:48
 +   |
 +LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
 +   |                                                ^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:269:37
++  --> $DIR/undocumented_unsafe_blocks.rs:274:18
 +   |
 +LL |     let _ = (42, unsafe {}, "test", unsafe {});
 +   |                  ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:273:14
++  --> $DIR/undocumented_unsafe_blocks.rs:274:37
 +   |
 +LL |     let _ = (42, unsafe {}, "test", unsafe {});
 +   |                                     ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:278:19
++  --> $DIR/undocumented_unsafe_blocks.rs:278:14
 +   |
 +LL |     let _ = *unsafe { &42 };
 +   |              ^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:284:14
++  --> $DIR/undocumented_unsafe_blocks.rs:283:19
 +   |
 +LL |     let _ = match unsafe {} {
 +   |                   ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:288:14
++  --> $DIR/undocumented_unsafe_blocks.rs:289:14
 +   |
 +LL |     let _ = &unsafe {};
 +   |              ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:292:13
++  --> $DIR/undocumented_unsafe_blocks.rs:293:14
 +   |
 +LL |     let _ = [unsafe {}; 5];
 +   |              ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:302:8
++  --> $DIR/undocumented_unsafe_blocks.rs:297:13
 +   |
 +LL |     let _ = unsafe {};
 +   |             ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:308:13
++  --> $DIR/undocumented_unsafe_blocks.rs:307:8
 +   |
 +LL |     t!(unsafe {});
 +   |        ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:316:5
++  --> $DIR/undocumented_unsafe_blocks.rs:313:13
 +   |
 +LL |             unsafe {}
 +   |             ^^^^^^^^^
 +...
 +LL |     t!();
 +   |     ---- in this macro invocation
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +   = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:320:5
++  --> $DIR/undocumented_unsafe_blocks.rs:321:5
 +   |
 +LL |     unsafe {} // SAFETY:
 +   |     ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:330:5
++  --> $DIR/undocumented_unsafe_blocks.rs:325:5
 +   |
 +LL |     unsafe {
 +   |     ^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:334:20
++  --> $DIR/undocumented_unsafe_blocks.rs:335:5
 +   |
 +LL |     unsafe {};
 +   |     ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:341:5
++  --> $DIR/undocumented_unsafe_blocks.rs:339:20
 +   |
 +LL |     println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
 +   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:348:9
++  --> $DIR/undocumented_unsafe_blocks.rs:346:5
 +   |
 +LL |     unsafe impl A for () {}
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:369:13
++  --> $DIR/undocumented_unsafe_blocks.rs:353:9
 +   |
 +LL |         unsafe impl B for (u32) {}
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:394:13
++  --> $DIR/undocumented_unsafe_blocks.rs:374:13
 +   |
 +LL |             unsafe impl T for $t {}
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^
 +...
 +LL |     no_safety_comment!(());
 +   |     ---------------------- in this macro invocation
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +   = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: unsafe impl missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:402:5
++  --> $DIR/undocumented_unsafe_blocks.rs:399:13
 +   |
 +LL |             unsafe impl T for $t {}
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^
 +...
 +LL |     no_safety_comment!(());
 +   |     ---------------------- in this macro invocation
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +   = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: unsafe impl missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:394:13
++  --> $DIR/undocumented_unsafe_blocks.rs:407:5
 +   |
 +LL |     unsafe impl T for (i32) {}
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:408:5
++  --> $DIR/undocumented_unsafe_blocks.rs:399:13
 +   |
 +LL |             unsafe impl T for $t {}
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^
 +...
 +LL |     no_safety_comment!(u32);
 +   |     ----------------------- in this macro invocation
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +   = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: unsafe impl missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:454:5
++  --> $DIR/undocumented_unsafe_blocks.rs:413:5
 +   |
 +LL |     unsafe impl T for (bool) {}
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:458:19
++  --> $DIR/undocumented_unsafe_blocks.rs:459:5
 +   |
 +LL |     unsafe impl NoComment for () {}
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:462:5
++  --> $DIR/undocumented_unsafe_blocks.rs:463:19
 +   |
 +LL |     /* SAFETY: */ unsafe impl InlineComment for () {}
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:467:5
++  --> $DIR/undocumented_unsafe_blocks.rs:467:5
 +   |
 +LL |     unsafe impl TrailingComment for () {} // SAFETY:
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:474:5
++  --> $DIR/undocumented_unsafe_blocks.rs:472:5
 +   |
 +LL |     unsafe impl Interference for () {}
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
-   --> $DIR/undocumented_unsafe_blocks.rs:483:1
++  --> $DIR/undocumented_unsafe_blocks.rs:479:5
 +   |
 +LL |     unsafe impl ImplInFn for () {}
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
++  --> $DIR/undocumented_unsafe_blocks.rs:488:1
 +   |
 +LL | unsafe impl CrateRoot for () {}
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: aborting due to 31 previous errors
 +
index 65fcdc43061bfe8557441050c3cc73c54121ccb5,0000000000000000000000000000000000000000..eed817968832193aa2d7588043480134c3e04d13
mode 100644,000000..100644
--- /dev/null
@@@ -1,130 -1,0 +1,132 @@@
 +// run-rustfix
 +#![warn(clippy::unnecessary_lazy_evaluations)]
 +#![allow(clippy::redundant_closure)]
 +#![allow(clippy::bind_instead_of_map)]
 +#![allow(clippy::map_identity)]
 +
 +struct Deep(Option<usize>);
 +
 +#[derive(Copy, Clone)]
 +struct SomeStruct {
 +    some_field: usize,
 +}
 +
 +impl SomeStruct {
 +    fn return_some_field(&self) -> usize {
 +        self.some_field
 +    }
 +}
 +
 +fn some_call<T: Default>() -> T {
 +    T::default()
 +}
 +
 +fn main() {
 +    let astronomers_pi = 10;
 +    let ext_arr: [usize; 1] = [2];
 +    let ext_str = SomeStruct { some_field: 10 };
 +
 +    let mut opt = Some(42);
 +    let ext_opt = Some(42);
 +    let nested_opt = Some(Some(42));
 +    let nested_tuple_opt = Some(Some((42, 43)));
++    let cond = true;
 +
 +    // Should lint - Option
 +    let _ = opt.unwrap_or(2);
 +    let _ = opt.unwrap_or(astronomers_pi);
 +    let _ = opt.unwrap_or(ext_str.some_field);
 +    let _ = opt.unwrap_or_else(|| ext_arr[0]);
 +    let _ = opt.and(ext_opt);
 +    let _ = opt.or(ext_opt);
 +    let _ = opt.or(None);
 +    let _ = opt.get_or_insert(2);
 +    let _ = opt.ok_or(2);
 +    let _ = nested_tuple_opt.unwrap_or(Some((1, 2)));
++    let _ = cond.then_some(astronomers_pi);
 +
 +    // Cases when unwrap is not called on a simple variable
 +    let _ = Some(10).unwrap_or(2);
 +    let _ = Some(10).and(ext_opt);
 +    let _: Option<usize> = None.or(ext_opt);
 +    let _ = None.get_or_insert(2);
 +    let _: Result<usize, usize> = None.ok_or(2);
 +    let _: Option<usize> = None.or(None);
 +
 +    let mut deep = Deep(Some(42));
 +    let _ = deep.0.unwrap_or(2);
 +    let _ = deep.0.and(ext_opt);
 +    let _ = deep.0.or(None);
 +    let _ = deep.0.get_or_insert(2);
 +    let _ = deep.0.ok_or(2);
 +
 +    // Should not lint - Option
 +    let _ = opt.unwrap_or_else(|| ext_str.return_some_field());
 +    let _ = nested_opt.unwrap_or_else(|| Some(some_call()));
 +    let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call())));
 +    let _ = opt.or_else(some_call);
 +    let _ = opt.or_else(|| some_call());
 +    let _: Result<usize, usize> = opt.ok_or_else(|| some_call());
 +    let _: Result<usize, usize> = opt.ok_or_else(some_call);
 +    let _ = deep.0.get_or_insert_with(|| some_call());
 +    let _ = deep.0.or_else(some_call);
 +    let _ = deep.0.or_else(|| some_call());
 +    let _ = opt.ok_or_else(|| ext_arr[0]);
 +
 +    // should not lint, bind_instead_of_map takes priority
 +    let _ = Some(10).and_then(|idx| Some(ext_arr[idx]));
 +    let _ = Some(10).and_then(|idx| Some(idx));
 +
 +    // should lint, bind_instead_of_map doesn't apply
 +    let _: Option<usize> = None.or(Some(3));
 +    let _ = deep.0.or(Some(3));
 +    let _ = opt.or(Some(3));
 +
 +    // Should lint - Result
 +    let res: Result<usize, usize> = Err(5);
 +    let res2: Result<usize, SomeStruct> = Err(SomeStruct { some_field: 5 });
 +
 +    let _ = res2.unwrap_or(2);
 +    let _ = res2.unwrap_or(astronomers_pi);
 +    let _ = res2.unwrap_or(ext_str.some_field);
 +
 +    // Should not lint - Result
 +    let _ = res.unwrap_or_else(|err| err);
 +    let _ = res.unwrap_or_else(|err| ext_arr[err]);
 +    let _ = res2.unwrap_or_else(|err| err.some_field);
 +    let _ = res2.unwrap_or_else(|err| err.return_some_field());
 +    let _ = res2.unwrap_or_else(|_| ext_str.return_some_field());
 +
 +    // should not lint, bind_instead_of_map takes priority
 +    let _: Result<usize, usize> = res.and_then(|x| Ok(x));
 +    let _: Result<usize, usize> = res.or_else(|err| Err(err));
 +
 +    let _: Result<usize, usize> = res.and_then(|_| Ok(2));
 +    let _: Result<usize, usize> = res.and_then(|_| Ok(astronomers_pi));
 +    let _: Result<usize, usize> = res.and_then(|_| Ok(ext_str.some_field));
 +
 +    let _: Result<usize, usize> = res.or_else(|_| Err(2));
 +    let _: Result<usize, usize> = res.or_else(|_| Err(astronomers_pi));
 +    let _: Result<usize, usize> = res.or_else(|_| Err(ext_str.some_field));
 +
 +    // should lint, bind_instead_of_map doesn't apply
 +    let _: Result<usize, usize> = res.and(Err(2));
 +    let _: Result<usize, usize> = res.and(Err(astronomers_pi));
 +    let _: Result<usize, usize> = res.and(Err(ext_str.some_field));
 +
 +    let _: Result<usize, usize> = res.or(Ok(2));
 +    let _: Result<usize, usize> = res.or(Ok(astronomers_pi));
 +    let _: Result<usize, usize> = res.or(Ok(ext_str.some_field));
 +    let _: Result<usize, usize> = res.
 +        // some lines
 +        // some lines
 +        // some lines
 +        // some lines
 +        // some lines
 +        // some lines
 +        or(Ok(ext_str.some_field));
 +
 +    // neither bind_instead_of_map nor unnecessary_lazy_eval applies here
 +    let _: Result<usize, usize> = res.and_then(|x| Err(x));
 +    let _: Result<usize, usize> = res.or_else(|err| Ok(err));
 +}
index 206080ed69ada8dd7735ed9b6551cd7b12dc672b,0000000000000000000000000000000000000000..1588db79b38a8c2345a5a623466a705998fedb97
mode 100644,000000..100644
--- /dev/null
@@@ -1,130 -1,0 +1,132 @@@
 +// run-rustfix
 +#![warn(clippy::unnecessary_lazy_evaluations)]
 +#![allow(clippy::redundant_closure)]
 +#![allow(clippy::bind_instead_of_map)]
 +#![allow(clippy::map_identity)]
 +
 +struct Deep(Option<usize>);
 +
 +#[derive(Copy, Clone)]
 +struct SomeStruct {
 +    some_field: usize,
 +}
 +
 +impl SomeStruct {
 +    fn return_some_field(&self) -> usize {
 +        self.some_field
 +    }
 +}
 +
 +fn some_call<T: Default>() -> T {
 +    T::default()
 +}
 +
 +fn main() {
 +    let astronomers_pi = 10;
 +    let ext_arr: [usize; 1] = [2];
 +    let ext_str = SomeStruct { some_field: 10 };
 +
 +    let mut opt = Some(42);
 +    let ext_opt = Some(42);
 +    let nested_opt = Some(Some(42));
 +    let nested_tuple_opt = Some(Some((42, 43)));
++    let cond = true;
 +
 +    // Should lint - Option
 +    let _ = opt.unwrap_or_else(|| 2);
 +    let _ = opt.unwrap_or_else(|| astronomers_pi);
 +    let _ = opt.unwrap_or_else(|| ext_str.some_field);
 +    let _ = opt.unwrap_or_else(|| ext_arr[0]);
 +    let _ = opt.and_then(|_| ext_opt);
 +    let _ = opt.or_else(|| ext_opt);
 +    let _ = opt.or_else(|| None);
 +    let _ = opt.get_or_insert_with(|| 2);
 +    let _ = opt.ok_or_else(|| 2);
 +    let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
++    let _ = cond.then(|| astronomers_pi);
 +
 +    // Cases when unwrap is not called on a simple variable
 +    let _ = Some(10).unwrap_or_else(|| 2);
 +    let _ = Some(10).and_then(|_| ext_opt);
 +    let _: Option<usize> = None.or_else(|| ext_opt);
 +    let _ = None.get_or_insert_with(|| 2);
 +    let _: Result<usize, usize> = None.ok_or_else(|| 2);
 +    let _: Option<usize> = None.or_else(|| None);
 +
 +    let mut deep = Deep(Some(42));
 +    let _ = deep.0.unwrap_or_else(|| 2);
 +    let _ = deep.0.and_then(|_| ext_opt);
 +    let _ = deep.0.or_else(|| None);
 +    let _ = deep.0.get_or_insert_with(|| 2);
 +    let _ = deep.0.ok_or_else(|| 2);
 +
 +    // Should not lint - Option
 +    let _ = opt.unwrap_or_else(|| ext_str.return_some_field());
 +    let _ = nested_opt.unwrap_or_else(|| Some(some_call()));
 +    let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call())));
 +    let _ = opt.or_else(some_call);
 +    let _ = opt.or_else(|| some_call());
 +    let _: Result<usize, usize> = opt.ok_or_else(|| some_call());
 +    let _: Result<usize, usize> = opt.ok_or_else(some_call);
 +    let _ = deep.0.get_or_insert_with(|| some_call());
 +    let _ = deep.0.or_else(some_call);
 +    let _ = deep.0.or_else(|| some_call());
 +    let _ = opt.ok_or_else(|| ext_arr[0]);
 +
 +    // should not lint, bind_instead_of_map takes priority
 +    let _ = Some(10).and_then(|idx| Some(ext_arr[idx]));
 +    let _ = Some(10).and_then(|idx| Some(idx));
 +
 +    // should lint, bind_instead_of_map doesn't apply
 +    let _: Option<usize> = None.or_else(|| Some(3));
 +    let _ = deep.0.or_else(|| Some(3));
 +    let _ = opt.or_else(|| Some(3));
 +
 +    // Should lint - Result
 +    let res: Result<usize, usize> = Err(5);
 +    let res2: Result<usize, SomeStruct> = Err(SomeStruct { some_field: 5 });
 +
 +    let _ = res2.unwrap_or_else(|_| 2);
 +    let _ = res2.unwrap_or_else(|_| astronomers_pi);
 +    let _ = res2.unwrap_or_else(|_| ext_str.some_field);
 +
 +    // Should not lint - Result
 +    let _ = res.unwrap_or_else(|err| err);
 +    let _ = res.unwrap_or_else(|err| ext_arr[err]);
 +    let _ = res2.unwrap_or_else(|err| err.some_field);
 +    let _ = res2.unwrap_or_else(|err| err.return_some_field());
 +    let _ = res2.unwrap_or_else(|_| ext_str.return_some_field());
 +
 +    // should not lint, bind_instead_of_map takes priority
 +    let _: Result<usize, usize> = res.and_then(|x| Ok(x));
 +    let _: Result<usize, usize> = res.or_else(|err| Err(err));
 +
 +    let _: Result<usize, usize> = res.and_then(|_| Ok(2));
 +    let _: Result<usize, usize> = res.and_then(|_| Ok(astronomers_pi));
 +    let _: Result<usize, usize> = res.and_then(|_| Ok(ext_str.some_field));
 +
 +    let _: Result<usize, usize> = res.or_else(|_| Err(2));
 +    let _: Result<usize, usize> = res.or_else(|_| Err(astronomers_pi));
 +    let _: Result<usize, usize> = res.or_else(|_| Err(ext_str.some_field));
 +
 +    // should lint, bind_instead_of_map doesn't apply
 +    let _: Result<usize, usize> = res.and_then(|_| Err(2));
 +    let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
 +    let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
 +
 +    let _: Result<usize, usize> = res.or_else(|_| Ok(2));
 +    let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
 +    let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
 +    let _: Result<usize, usize> = res.
 +        // some lines
 +        // some lines
 +        // some lines
 +        // some lines
 +        // some lines
 +        // some lines
 +        or_else(|_| Ok(ext_str.some_field));
 +
 +    // neither bind_instead_of_map nor unnecessary_lazy_eval applies here
 +    let _: Result<usize, usize> = res.and_then(|x| Err(x));
 +    let _: Result<usize, usize> = res.or_else(|err| Ok(err));
 +}
index 7e4dd7730e71530b79aa78b6f41937a13c90b143,0000000000000000000000000000000000000000..83dc7fd832c38f8a7974f6175fbb3ac91fd8a179
mode 100644,000000..100644
--- /dev/null
@@@ -1,275 -1,0 +1,283 @@@
-   --> $DIR/unnecessary_lazy_eval.rs:35:13
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:36:13
++  --> $DIR/unnecessary_lazy_eval.rs:36:13
 +   |
 +LL |     let _ = opt.unwrap_or_else(|| 2);
 +   |             ^^^^--------------------
 +   |                 |
 +   |                 help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 +   |
 +   = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:37:13
++  --> $DIR/unnecessary_lazy_eval.rs:37:13
 +   |
 +LL |     let _ = opt.unwrap_or_else(|| astronomers_pi);
 +   |             ^^^^---------------------------------
 +   |                 |
 +   |                 help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:39:13
++  --> $DIR/unnecessary_lazy_eval.rs:38:13
 +   |
 +LL |     let _ = opt.unwrap_or_else(|| ext_str.some_field);
 +   |             ^^^^-------------------------------------
 +   |                 |
 +   |                 help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:40:13
++  --> $DIR/unnecessary_lazy_eval.rs:40:13
 +   |
 +LL |     let _ = opt.and_then(|_| ext_opt);
 +   |             ^^^^---------------------
 +   |                 |
 +   |                 help: use `and(..)` instead: `and(ext_opt)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:41:13
++  --> $DIR/unnecessary_lazy_eval.rs:41:13
 +   |
 +LL |     let _ = opt.or_else(|| ext_opt);
 +   |             ^^^^-------------------
 +   |                 |
 +   |                 help: use `or(..)` instead: `or(ext_opt)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:42:13
++  --> $DIR/unnecessary_lazy_eval.rs:42:13
 +   |
 +LL |     let _ = opt.or_else(|| None);
 +   |             ^^^^----------------
 +   |                 |
 +   |                 help: use `or(..)` instead: `or(None)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:43:13
++  --> $DIR/unnecessary_lazy_eval.rs:43:13
 +   |
 +LL |     let _ = opt.get_or_insert_with(|| 2);
 +   |             ^^^^------------------------
 +   |                 |
 +   |                 help: use `get_or_insert(..)` instead: `get_or_insert(2)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:44:13
++  --> $DIR/unnecessary_lazy_eval.rs:44:13
 +   |
 +LL |     let _ = opt.ok_or_else(|| 2);
 +   |             ^^^^----------------
 +   |                 |
 +   |                 help: use `ok_or(..)` instead: `ok_or(2)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:47:13
++  --> $DIR/unnecessary_lazy_eval.rs:45:13
 +   |
 +LL |     let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
 +   |             ^^^^^^^^^^^^^^^^^-------------------------------
 +   |                              |
 +   |                              help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))`
 +
++error: unnecessary closure used with `bool::then`
++  --> $DIR/unnecessary_lazy_eval.rs:46:13
++   |
++LL |     let _ = cond.then(|| astronomers_pi);
++   |             ^^^^^-----------------------
++   |                  |
++   |                  help: use `then_some(..)` instead: `then_some(astronomers_pi)`
++
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:48:13
++  --> $DIR/unnecessary_lazy_eval.rs:49:13
 +   |
 +LL |     let _ = Some(10).unwrap_or_else(|| 2);
 +   |             ^^^^^^^^^--------------------
 +   |                      |
 +   |                      help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:49:28
++  --> $DIR/unnecessary_lazy_eval.rs:50:13
 +   |
 +LL |     let _ = Some(10).and_then(|_| ext_opt);
 +   |             ^^^^^^^^^---------------------
 +   |                      |
 +   |                      help: use `and(..)` instead: `and(ext_opt)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:50:13
++  --> $DIR/unnecessary_lazy_eval.rs:51:28
 +   |
 +LL |     let _: Option<usize> = None.or_else(|| ext_opt);
 +   |                            ^^^^^-------------------
 +   |                                 |
 +   |                                 help: use `or(..)` instead: `or(ext_opt)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:51:35
++  --> $DIR/unnecessary_lazy_eval.rs:52:13
 +   |
 +LL |     let _ = None.get_or_insert_with(|| 2);
 +   |             ^^^^^------------------------
 +   |                  |
 +   |                  help: use `get_or_insert(..)` instead: `get_or_insert(2)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:52:28
++  --> $DIR/unnecessary_lazy_eval.rs:53:35
 +   |
 +LL |     let _: Result<usize, usize> = None.ok_or_else(|| 2);
 +   |                                   ^^^^^----------------
 +   |                                        |
 +   |                                        help: use `ok_or(..)` instead: `ok_or(2)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:55:13
++  --> $DIR/unnecessary_lazy_eval.rs:54:28
 +   |
 +LL |     let _: Option<usize> = None.or_else(|| None);
 +   |                            ^^^^^----------------
 +   |                                 |
 +   |                                 help: use `or(..)` instead: `or(None)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:56:13
++  --> $DIR/unnecessary_lazy_eval.rs:57:13
 +   |
 +LL |     let _ = deep.0.unwrap_or_else(|| 2);
 +   |             ^^^^^^^--------------------
 +   |                    |
 +   |                    help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:57:13
++  --> $DIR/unnecessary_lazy_eval.rs:58:13
 +   |
 +LL |     let _ = deep.0.and_then(|_| ext_opt);
 +   |             ^^^^^^^---------------------
 +   |                    |
 +   |                    help: use `and(..)` instead: `and(ext_opt)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:58:13
++  --> $DIR/unnecessary_lazy_eval.rs:59:13
 +   |
 +LL |     let _ = deep.0.or_else(|| None);
 +   |             ^^^^^^^----------------
 +   |                    |
 +   |                    help: use `or(..)` instead: `or(None)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:59:13
++  --> $DIR/unnecessary_lazy_eval.rs:60:13
 +   |
 +LL |     let _ = deep.0.get_or_insert_with(|| 2);
 +   |             ^^^^^^^------------------------
 +   |                    |
 +   |                    help: use `get_or_insert(..)` instead: `get_or_insert(2)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:79:28
++  --> $DIR/unnecessary_lazy_eval.rs:61:13
 +   |
 +LL |     let _ = deep.0.ok_or_else(|| 2);
 +   |             ^^^^^^^----------------
 +   |                    |
 +   |                    help: use `ok_or(..)` instead: `ok_or(2)`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:80:13
++  --> $DIR/unnecessary_lazy_eval.rs:81:28
 +   |
 +LL |     let _: Option<usize> = None.or_else(|| Some(3));
 +   |                            ^^^^^-------------------
 +   |                                 |
 +   |                                 help: use `or(..)` instead: `or(Some(3))`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:81:13
++  --> $DIR/unnecessary_lazy_eval.rs:82:13
 +   |
 +LL |     let _ = deep.0.or_else(|| Some(3));
 +   |             ^^^^^^^-------------------
 +   |                    |
 +   |                    help: use `or(..)` instead: `or(Some(3))`
 +
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:87:13
++  --> $DIR/unnecessary_lazy_eval.rs:83:13
 +   |
 +LL |     let _ = opt.or_else(|| Some(3));
 +   |             ^^^^-------------------
 +   |                 |
 +   |                 help: use `or(..)` instead: `or(Some(3))`
 +
 +error: unnecessary closure used to substitute value for `Result::Err`
-   --> $DIR/unnecessary_lazy_eval.rs:88:13
++  --> $DIR/unnecessary_lazy_eval.rs:89:13
 +   |
 +LL |     let _ = res2.unwrap_or_else(|_| 2);
 +   |             ^^^^^---------------------
 +   |                  |
 +   |                  help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 +
 +error: unnecessary closure used to substitute value for `Result::Err`
-   --> $DIR/unnecessary_lazy_eval.rs:89:13
++  --> $DIR/unnecessary_lazy_eval.rs:90:13
 +   |
 +LL |     let _ = res2.unwrap_or_else(|_| astronomers_pi);
 +   |             ^^^^^----------------------------------
 +   |                  |
 +   |                  help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
 +
 +error: unnecessary closure used to substitute value for `Result::Err`
-   --> $DIR/unnecessary_lazy_eval.rs:111:35
++  --> $DIR/unnecessary_lazy_eval.rs:91:13
 +   |
 +LL |     let _ = res2.unwrap_or_else(|_| ext_str.some_field);
 +   |             ^^^^^--------------------------------------
 +   |                  |
 +   |                  help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
 +
 +error: unnecessary closure used to substitute value for `Result::Err`
-   --> $DIR/unnecessary_lazy_eval.rs:112:35
++  --> $DIR/unnecessary_lazy_eval.rs:113:35
 +   |
 +LL |     let _: Result<usize, usize> = res.and_then(|_| Err(2));
 +   |                                   ^^^^--------------------
 +   |                                       |
 +   |                                       help: use `and(..)` instead: `and(Err(2))`
 +
 +error: unnecessary closure used to substitute value for `Result::Err`
-   --> $DIR/unnecessary_lazy_eval.rs:113:35
++  --> $DIR/unnecessary_lazy_eval.rs:114:35
 +   |
 +LL |     let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
 +   |                                   ^^^^---------------------------------
 +   |                                       |
 +   |                                       help: use `and(..)` instead: `and(Err(astronomers_pi))`
 +
 +error: unnecessary closure used to substitute value for `Result::Err`
-   --> $DIR/unnecessary_lazy_eval.rs:115:35
++  --> $DIR/unnecessary_lazy_eval.rs:115:35
 +   |
 +LL |     let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
 +   |                                   ^^^^-------------------------------------
 +   |                                       |
 +   |                                       help: use `and(..)` instead: `and(Err(ext_str.some_field))`
 +
 +error: unnecessary closure used to substitute value for `Result::Err`
-   --> $DIR/unnecessary_lazy_eval.rs:116:35
++  --> $DIR/unnecessary_lazy_eval.rs:117:35
 +   |
 +LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(2));
 +   |                                   ^^^^------------------
 +   |                                       |
 +   |                                       help: use `or(..)` instead: `or(Ok(2))`
 +
 +error: unnecessary closure used to substitute value for `Result::Err`
-   --> $DIR/unnecessary_lazy_eval.rs:117:35
++  --> $DIR/unnecessary_lazy_eval.rs:118:35
 +   |
 +LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
 +   |                                   ^^^^-------------------------------
 +   |                                       |
 +   |                                       help: use `or(..)` instead: `or(Ok(astronomers_pi))`
 +
 +error: unnecessary closure used to substitute value for `Result::Err`
-   --> $DIR/unnecessary_lazy_eval.rs:118:35
++  --> $DIR/unnecessary_lazy_eval.rs:119:35
 +   |
 +LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
 +   |                                   ^^^^-----------------------------------
 +   |                                       |
 +   |                                       help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
 +
 +error: unnecessary closure used to substitute value for `Result::Err`
- error: aborting due to 33 previous errors
++  --> $DIR/unnecessary_lazy_eval.rs:120:35
 +   |
 +LL |       let _: Result<usize, usize> = res.
 +   |  ___________________________________^
 +LL | |         // some lines
 +LL | |         // some lines
 +LL | |         // some lines
 +...  |
 +LL | |         // some lines
 +LL | |         or_else(|_| Ok(ext_str.some_field));
 +   | |_________----------------------------------^
 +   |           |
 +   |           help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
 +
++error: aborting due to 34 previous errors
 +
index e9ff64431e19d154f614b9128a63028bb52c1ae3,0000000000000000000000000000000000000000..c57c46736342b6814b1bb919126521ae516ca28f
mode 100644,000000..100644
--- /dev/null
@@@ -1,411 -1,0 +1,453 @@@
-     clippy::manual_find
 +// run-rustfix
 +
 +#![warn(clippy::while_let_on_iterator)]
 +#![allow(
 +    clippy::never_loop,
 +    unreachable_code,
 +    unused_mut,
 +    dead_code,
 +    clippy::equatable_if_let,
-                 if !(3..=7).contains(&i) {
++    clippy::manual_find,
++    clippy::redundant_closure_call
 +)]
 +
 +fn base() {
 +    let mut iter = 1..20;
 +    for x in iter {
 +        println!("{}", x);
 +    }
 +
 +    let mut iter = 1..20;
 +    for x in iter {
 +        println!("{}", x);
 +    }
 +
 +    let mut iter = 1..20;
 +    for _ in iter {}
 +
 +    let mut iter = 1..20;
 +    while let None = iter.next() {} // this is fine (if nonsensical)
 +
 +    let mut iter = 1..20;
 +    if let Some(x) = iter.next() {
 +        // also fine
 +        println!("{}", x)
 +    }
 +
 +    // the following shouldn't warn because it can't be written with a for loop
 +    let mut iter = 1u32..20;
 +    while let Some(_) = iter.next() {
 +        println!("next: {:?}", iter.next())
 +    }
 +
 +    // neither can this
 +    let mut iter = 1u32..20;
 +    while let Some(_) = iter.next() {
 +        println!("next: {:?}", iter.next());
 +    }
 +
 +    // or this
 +    let mut iter = 1u32..20;
 +    while let Some(_) = iter.next() {
 +        iter = 1..20;
 +    }
 +}
 +
 +// Issue #1188
 +fn refutable() {
 +    let a = [42, 1337];
 +    let mut b = a.iter();
 +
 +    // consume all the 42s
 +    while let Some(&42) = b.next() {}
 +
 +    let a = [(1, 2, 3)];
 +    let mut b = a.iter();
 +
 +    while let Some(&(1, 2, 3)) = b.next() {}
 +
 +    let a = [Some(42)];
 +    let mut b = a.iter();
 +
 +    while let Some(&None) = b.next() {}
 +
 +    /* This gives “refutable pattern in `for` loop binding: `&_` not covered”
 +    for &42 in b {}
 +    for &(1, 2, 3) in b {}
 +    for &Option::None in b.next() {}
 +    // */
 +}
 +
 +fn refutable2() {
 +    // Issue 3780
 +    {
 +        let v = vec![1, 2, 3];
 +        let mut it = v.windows(2);
 +        while let Some([x, y]) = it.next() {
 +            println!("x: {}", x);
 +            println!("y: {}", y);
 +        }
 +
 +        let mut it = v.windows(2);
 +        while let Some([x, ..]) = it.next() {
 +            println!("x: {}", x);
 +        }
 +
 +        let mut it = v.windows(2);
 +        while let Some([.., y]) = it.next() {
 +            println!("y: {}", y);
 +        }
 +
 +        let mut it = v.windows(2);
 +        for [..] in it {}
 +
 +        let v = vec![[1], [2], [3]];
 +        let mut it = v.iter();
 +        while let Some([1]) = it.next() {}
 +
 +        let mut it = v.iter();
 +        for [_x] in it {}
 +    }
 +
 +    // binding
 +    {
 +        let v = vec![1, 2, 3];
 +        let mut it = v.iter();
 +        while let Some(x @ 1) = it.next() {
 +            println!("{}", x);
 +        }
 +
 +        let v = vec![[1], [2], [3]];
 +        let mut it = v.iter();
 +        for x @ [_] in it {
 +            println!("{:?}", x);
 +        }
 +    }
 +
 +    // false negative
 +    {
 +        let v = vec![1, 2, 3];
 +        let mut it = v.iter().map(Some);
 +        while let Some(Some(_) | None) = it.next() {
 +            println!("1");
 +        }
 +    }
 +}
 +
 +fn nested_loops() {
 +    let a = [42, 1337];
 +
 +    loop {
 +        let mut y = a.iter();
 +        for _ in y {
 +            // use a for loop here
 +        }
 +    }
 +}
 +
 +fn issue1121() {
 +    use std::collections::HashSet;
 +    let mut values = HashSet::new();
 +    values.insert(1);
 +
 +    while let Some(&value) = values.iter().next() {
 +        values.remove(&value);
 +    }
 +}
 +
 +fn issue2965() {
 +    // This should not cause an ICE
 +
 +    use std::collections::HashSet;
 +    let mut values = HashSet::new();
 +    values.insert(1);
 +
 +    while let Some(..) = values.iter().next() {}
 +}
 +
 +fn issue3670() {
 +    let array = [Some(0), None, Some(1)];
 +    let mut iter = array.iter();
 +
 +    while let Some(elem) = iter.next() {
 +        let _ = elem.or_else(|| *iter.next()?);
 +    }
 +}
 +
 +fn issue1654() {
 +    // should not lint if the iterator is generated on every iteration
 +    use std::collections::HashSet;
 +    let mut values = HashSet::new();
 +    values.insert(1);
 +
 +    while let Some(..) = values.iter().next() {
 +        values.remove(&1);
 +    }
 +
 +    while let Some(..) = values.iter().map(|x| x + 1).next() {}
 +
 +    let chars = "Hello, World!".char_indices();
 +    while let Some((i, ch)) = chars.clone().next() {
 +        println!("{}: {}", i, ch);
 +    }
 +}
 +
 +fn issue6491() {
 +    // Used in outer loop, needs &mut
 +    let mut it = 1..40;
 +    while let Some(n) = it.next() {
 +        for m in it.by_ref() {
 +            if m % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +        println!("n still is {}", n);
 +    }
 +
 +    // This is fine, inner loop uses a new iterator.
 +    let mut it = 1..40;
 +    for n in it {
 +        let mut it = 1..40;
 +        for m in it {
 +            if m % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +
 +        // Weird binding shouldn't change anything.
 +        let (mut it, _) = (1..40, 0);
 +        for m in it {
 +            if m % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +
 +        // Used after the loop, needs &mut.
 +        let mut it = 1..40;
 +        for m in it.by_ref() {
 +            if m % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +        println!("next item {}", it.next().unwrap());
 +
 +        println!("n still is {}", n);
 +    }
 +}
 +
 +fn issue6231() {
 +    // Closure in the outer loop, needs &mut
 +    let mut it = 1..40;
 +    let mut opt = Some(0);
 +    while let Some(n) = opt.take().or_else(|| it.next()) {
 +        for m in it.by_ref() {
 +            if n % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +        println!("n still is {}", n);
 +    }
 +}
 +
 +fn issue1924() {
 +    struct S<T>(T);
 +    impl<T: Iterator<Item = u32>> S<T> {
 +        fn f(&mut self) -> Option<u32> {
 +            // Used as a field.
 +            for i in self.0.by_ref() {
++                if !(3..8).contains(&i) {
 +                    return Some(i);
 +                }
 +            }
 +            None
 +        }
 +
 +        fn f2(&mut self) -> Option<u32> {
 +            // Don't lint, self borrowed inside the loop
 +            while let Some(i) = self.0.next() {
 +                if i == 1 {
 +                    return self.f();
 +                }
 +            }
 +            None
 +        }
 +    }
 +    impl<T: Iterator<Item = u32>> S<(S<T>, Option<u32>)> {
 +        fn f3(&mut self) -> Option<u32> {
 +            // Don't lint, self borrowed inside the loop
 +            while let Some(i) = self.0.0.0.next() {
 +                if i == 1 {
 +                    return self.0.0.f();
 +                }
 +            }
 +            while let Some(i) = self.0.0.0.next() {
 +                if i == 1 {
 +                    return self.f3();
 +                }
 +            }
 +            // This one is fine, a different field is borrowed
 +            for i in self.0.0.0.by_ref() {
 +                if i == 1 {
 +                    return self.0.1.take();
 +                } else {
 +                    self.0.1 = Some(i);
 +                }
 +            }
 +            None
 +        }
 +    }
 +
 +    struct S2<T>(T, u32);
 +    impl<T: Iterator<Item = u32>> Iterator for S2<T> {
 +        type Item = u32;
 +        fn next(&mut self) -> Option<u32> {
 +            self.0.next()
 +        }
 +    }
 +
 +    // Don't lint, field of the iterator is accessed in the loop
 +    let mut it = S2(1..40, 0);
 +    while let Some(n) = it.next() {
 +        if n == it.1 {
 +            break;
 +        }
 +    }
 +
 +    // Needs &mut, field of the iterator is accessed after the loop
 +    let mut it = S2(1..40, 0);
 +    for n in it.by_ref() {
 +        if n == 0 {
 +            break;
 +        }
 +    }
 +    println!("iterator field {}", it.1);
 +}
 +
 +fn issue7249() {
 +    let mut it = 0..10;
 +    let mut x = || {
 +        // Needs &mut, the closure can be called multiple times
 +        for x in it.by_ref() {
 +            if x % 2 == 0 {
 +                break;
 +            }
 +        }
 +    };
 +    x();
 +    x();
 +}
 +
 +fn issue7510() {
 +    let mut it = 0..10;
 +    let it = &mut it;
 +    // Needs to reborrow `it` as the binding isn't mutable
 +    for x in it.by_ref() {
 +        if x % 2 == 0 {
 +            break;
 +        }
 +    }
 +    println!("{}", it.next().unwrap());
 +
 +    struct S<T>(T);
 +    let mut it = 0..10;
 +    let it = S(&mut it);
 +    // Needs to reborrow `it.0` as the binding isn't mutable
 +    for x in it.0.by_ref() {
 +        if x % 2 == 0 {
 +            break;
 +        }
 +    }
 +    println!("{}", it.0.next().unwrap());
 +}
 +
 +fn exact_match_with_single_field() {
 +    struct S<T>(T);
 +    let mut s = S(0..10);
 +    // Don't lint. `s.0` is used inside the loop.
 +    while let Some(_) = s.0.next() {
 +        let _ = &mut s.0;
 +    }
 +}
 +
 +fn custom_deref() {
 +    struct S1<T> {
 +        x: T,
 +    }
 +    struct S2<T>(S1<T>);
 +    impl<T> core::ops::Deref for S2<T> {
 +        type Target = S1<T>;
 +        fn deref(&self) -> &Self::Target {
 +            &self.0
 +        }
 +    }
 +    impl<T> core::ops::DerefMut for S2<T> {
 +        fn deref_mut(&mut self) -> &mut Self::Target {
 +            &mut self.0
 +        }
 +    }
 +
 +    let mut s = S2(S1 { x: 0..10 });
 +    for x in s.x.by_ref() {
 +        println!("{}", x);
 +    }
 +}
 +
 +fn issue_8113() {
 +    let mut x = [0..10];
 +    for x in x[0].by_ref() {
 +        println!("{}", x);
 +    }
 +}
 +
++fn fn_once_closure() {
++    let mut it = 0..10;
++    (|| {
++        for x in it {
++            if x % 2 == 0 {
++                break;
++            }
++        }
++    })();
++
++    fn f(_: impl FnOnce()) {}
++    let mut it = 0..10;
++    f(|| {
++        for x in it {
++            if x % 2 == 0 {
++                break;
++            }
++        }
++    });
++
++    fn f2(_: impl FnMut()) {}
++    let mut it = 0..10;
++    f2(|| {
++        for x in it.by_ref() {
++            if x % 2 == 0 {
++                break;
++            }
++        }
++    });
++
++    fn f3(_: fn()) {}
++    f3(|| {
++        let mut it = 0..10;
++        for x in it {
++            if x % 2 == 0 {
++                break;
++            }
++        }
++    })
++}
++
 +fn main() {
 +    let mut it = 0..20;
 +    for _ in it {
 +        println!("test");
 +    }
 +}
index 03da39526b2fdd735098337004d9becf9c35b8bc,0000000000000000000000000000000000000000..8b9a2dbcce3a7c62149ad36a72137281ce61ca53
mode 100644,000000..100644
--- /dev/null
@@@ -1,411 -1,0 +1,453 @@@
-     clippy::manual_find
 +// run-rustfix
 +
 +#![warn(clippy::while_let_on_iterator)]
 +#![allow(
 +    clippy::never_loop,
 +    unreachable_code,
 +    unused_mut,
 +    dead_code,
 +    clippy::equatable_if_let,
-                 if i < 3 || i > 7 {
++    clippy::manual_find,
++    clippy::redundant_closure_call
 +)]
 +
 +fn base() {
 +    let mut iter = 1..20;
 +    while let Option::Some(x) = iter.next() {
 +        println!("{}", x);
 +    }
 +
 +    let mut iter = 1..20;
 +    while let Some(x) = iter.next() {
 +        println!("{}", x);
 +    }
 +
 +    let mut iter = 1..20;
 +    while let Some(_) = iter.next() {}
 +
 +    let mut iter = 1..20;
 +    while let None = iter.next() {} // this is fine (if nonsensical)
 +
 +    let mut iter = 1..20;
 +    if let Some(x) = iter.next() {
 +        // also fine
 +        println!("{}", x)
 +    }
 +
 +    // the following shouldn't warn because it can't be written with a for loop
 +    let mut iter = 1u32..20;
 +    while let Some(_) = iter.next() {
 +        println!("next: {:?}", iter.next())
 +    }
 +
 +    // neither can this
 +    let mut iter = 1u32..20;
 +    while let Some(_) = iter.next() {
 +        println!("next: {:?}", iter.next());
 +    }
 +
 +    // or this
 +    let mut iter = 1u32..20;
 +    while let Some(_) = iter.next() {
 +        iter = 1..20;
 +    }
 +}
 +
 +// Issue #1188
 +fn refutable() {
 +    let a = [42, 1337];
 +    let mut b = a.iter();
 +
 +    // consume all the 42s
 +    while let Some(&42) = b.next() {}
 +
 +    let a = [(1, 2, 3)];
 +    let mut b = a.iter();
 +
 +    while let Some(&(1, 2, 3)) = b.next() {}
 +
 +    let a = [Some(42)];
 +    let mut b = a.iter();
 +
 +    while let Some(&None) = b.next() {}
 +
 +    /* This gives “refutable pattern in `for` loop binding: `&_` not covered”
 +    for &42 in b {}
 +    for &(1, 2, 3) in b {}
 +    for &Option::None in b.next() {}
 +    // */
 +}
 +
 +fn refutable2() {
 +    // Issue 3780
 +    {
 +        let v = vec![1, 2, 3];
 +        let mut it = v.windows(2);
 +        while let Some([x, y]) = it.next() {
 +            println!("x: {}", x);
 +            println!("y: {}", y);
 +        }
 +
 +        let mut it = v.windows(2);
 +        while let Some([x, ..]) = it.next() {
 +            println!("x: {}", x);
 +        }
 +
 +        let mut it = v.windows(2);
 +        while let Some([.., y]) = it.next() {
 +            println!("y: {}", y);
 +        }
 +
 +        let mut it = v.windows(2);
 +        while let Some([..]) = it.next() {}
 +
 +        let v = vec![[1], [2], [3]];
 +        let mut it = v.iter();
 +        while let Some([1]) = it.next() {}
 +
 +        let mut it = v.iter();
 +        while let Some([_x]) = it.next() {}
 +    }
 +
 +    // binding
 +    {
 +        let v = vec![1, 2, 3];
 +        let mut it = v.iter();
 +        while let Some(x @ 1) = it.next() {
 +            println!("{}", x);
 +        }
 +
 +        let v = vec![[1], [2], [3]];
 +        let mut it = v.iter();
 +        while let Some(x @ [_]) = it.next() {
 +            println!("{:?}", x);
 +        }
 +    }
 +
 +    // false negative
 +    {
 +        let v = vec![1, 2, 3];
 +        let mut it = v.iter().map(Some);
 +        while let Some(Some(_) | None) = it.next() {
 +            println!("1");
 +        }
 +    }
 +}
 +
 +fn nested_loops() {
 +    let a = [42, 1337];
 +
 +    loop {
 +        let mut y = a.iter();
 +        while let Some(_) = y.next() {
 +            // use a for loop here
 +        }
 +    }
 +}
 +
 +fn issue1121() {
 +    use std::collections::HashSet;
 +    let mut values = HashSet::new();
 +    values.insert(1);
 +
 +    while let Some(&value) = values.iter().next() {
 +        values.remove(&value);
 +    }
 +}
 +
 +fn issue2965() {
 +    // This should not cause an ICE
 +
 +    use std::collections::HashSet;
 +    let mut values = HashSet::new();
 +    values.insert(1);
 +
 +    while let Some(..) = values.iter().next() {}
 +}
 +
 +fn issue3670() {
 +    let array = [Some(0), None, Some(1)];
 +    let mut iter = array.iter();
 +
 +    while let Some(elem) = iter.next() {
 +        let _ = elem.or_else(|| *iter.next()?);
 +    }
 +}
 +
 +fn issue1654() {
 +    // should not lint if the iterator is generated on every iteration
 +    use std::collections::HashSet;
 +    let mut values = HashSet::new();
 +    values.insert(1);
 +
 +    while let Some(..) = values.iter().next() {
 +        values.remove(&1);
 +    }
 +
 +    while let Some(..) = values.iter().map(|x| x + 1).next() {}
 +
 +    let chars = "Hello, World!".char_indices();
 +    while let Some((i, ch)) = chars.clone().next() {
 +        println!("{}: {}", i, ch);
 +    }
 +}
 +
 +fn issue6491() {
 +    // Used in outer loop, needs &mut
 +    let mut it = 1..40;
 +    while let Some(n) = it.next() {
 +        while let Some(m) = it.next() {
 +            if m % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +        println!("n still is {}", n);
 +    }
 +
 +    // This is fine, inner loop uses a new iterator.
 +    let mut it = 1..40;
 +    while let Some(n) = it.next() {
 +        let mut it = 1..40;
 +        while let Some(m) = it.next() {
 +            if m % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +
 +        // Weird binding shouldn't change anything.
 +        let (mut it, _) = (1..40, 0);
 +        while let Some(m) = it.next() {
 +            if m % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +
 +        // Used after the loop, needs &mut.
 +        let mut it = 1..40;
 +        while let Some(m) = it.next() {
 +            if m % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +        println!("next item {}", it.next().unwrap());
 +
 +        println!("n still is {}", n);
 +    }
 +}
 +
 +fn issue6231() {
 +    // Closure in the outer loop, needs &mut
 +    let mut it = 1..40;
 +    let mut opt = Some(0);
 +    while let Some(n) = opt.take().or_else(|| it.next()) {
 +        while let Some(m) = it.next() {
 +            if n % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +        println!("n still is {}", n);
 +    }
 +}
 +
 +fn issue1924() {
 +    struct S<T>(T);
 +    impl<T: Iterator<Item = u32>> S<T> {
 +        fn f(&mut self) -> Option<u32> {
 +            // Used as a field.
 +            while let Some(i) = self.0.next() {
++                if !(3..8).contains(&i) {
 +                    return Some(i);
 +                }
 +            }
 +            None
 +        }
 +
 +        fn f2(&mut self) -> Option<u32> {
 +            // Don't lint, self borrowed inside the loop
 +            while let Some(i) = self.0.next() {
 +                if i == 1 {
 +                    return self.f();
 +                }
 +            }
 +            None
 +        }
 +    }
 +    impl<T: Iterator<Item = u32>> S<(S<T>, Option<u32>)> {
 +        fn f3(&mut self) -> Option<u32> {
 +            // Don't lint, self borrowed inside the loop
 +            while let Some(i) = self.0.0.0.next() {
 +                if i == 1 {
 +                    return self.0.0.f();
 +                }
 +            }
 +            while let Some(i) = self.0.0.0.next() {
 +                if i == 1 {
 +                    return self.f3();
 +                }
 +            }
 +            // This one is fine, a different field is borrowed
 +            while let Some(i) = self.0.0.0.next() {
 +                if i == 1 {
 +                    return self.0.1.take();
 +                } else {
 +                    self.0.1 = Some(i);
 +                }
 +            }
 +            None
 +        }
 +    }
 +
 +    struct S2<T>(T, u32);
 +    impl<T: Iterator<Item = u32>> Iterator for S2<T> {
 +        type Item = u32;
 +        fn next(&mut self) -> Option<u32> {
 +            self.0.next()
 +        }
 +    }
 +
 +    // Don't lint, field of the iterator is accessed in the loop
 +    let mut it = S2(1..40, 0);
 +    while let Some(n) = it.next() {
 +        if n == it.1 {
 +            break;
 +        }
 +    }
 +
 +    // Needs &mut, field of the iterator is accessed after the loop
 +    let mut it = S2(1..40, 0);
 +    while let Some(n) = it.next() {
 +        if n == 0 {
 +            break;
 +        }
 +    }
 +    println!("iterator field {}", it.1);
 +}
 +
 +fn issue7249() {
 +    let mut it = 0..10;
 +    let mut x = || {
 +        // Needs &mut, the closure can be called multiple times
 +        while let Some(x) = it.next() {
 +            if x % 2 == 0 {
 +                break;
 +            }
 +        }
 +    };
 +    x();
 +    x();
 +}
 +
 +fn issue7510() {
 +    let mut it = 0..10;
 +    let it = &mut it;
 +    // Needs to reborrow `it` as the binding isn't mutable
 +    while let Some(x) = it.next() {
 +        if x % 2 == 0 {
 +            break;
 +        }
 +    }
 +    println!("{}", it.next().unwrap());
 +
 +    struct S<T>(T);
 +    let mut it = 0..10;
 +    let it = S(&mut it);
 +    // Needs to reborrow `it.0` as the binding isn't mutable
 +    while let Some(x) = it.0.next() {
 +        if x % 2 == 0 {
 +            break;
 +        }
 +    }
 +    println!("{}", it.0.next().unwrap());
 +}
 +
 +fn exact_match_with_single_field() {
 +    struct S<T>(T);
 +    let mut s = S(0..10);
 +    // Don't lint. `s.0` is used inside the loop.
 +    while let Some(_) = s.0.next() {
 +        let _ = &mut s.0;
 +    }
 +}
 +
 +fn custom_deref() {
 +    struct S1<T> {
 +        x: T,
 +    }
 +    struct S2<T>(S1<T>);
 +    impl<T> core::ops::Deref for S2<T> {
 +        type Target = S1<T>;
 +        fn deref(&self) -> &Self::Target {
 +            &self.0
 +        }
 +    }
 +    impl<T> core::ops::DerefMut for S2<T> {
 +        fn deref_mut(&mut self) -> &mut Self::Target {
 +            &mut self.0
 +        }
 +    }
 +
 +    let mut s = S2(S1 { x: 0..10 });
 +    while let Some(x) = s.x.next() {
 +        println!("{}", x);
 +    }
 +}
 +
 +fn issue_8113() {
 +    let mut x = [0..10];
 +    while let Some(x) = x[0].next() {
 +        println!("{}", x);
 +    }
 +}
 +
++fn fn_once_closure() {
++    let mut it = 0..10;
++    (|| {
++        while let Some(x) = it.next() {
++            if x % 2 == 0 {
++                break;
++            }
++        }
++    })();
++
++    fn f(_: impl FnOnce()) {}
++    let mut it = 0..10;
++    f(|| {
++        while let Some(x) = it.next() {
++            if x % 2 == 0 {
++                break;
++            }
++        }
++    });
++
++    fn f2(_: impl FnMut()) {}
++    let mut it = 0..10;
++    f2(|| {
++        while let Some(x) = it.next() {
++            if x % 2 == 0 {
++                break;
++            }
++        }
++    });
++
++    fn f3(_: fn()) {}
++    f3(|| {
++        let mut it = 0..10;
++        while let Some(x) = it.next() {
++            if x % 2 == 0 {
++                break;
++            }
++        }
++    })
++}
++
 +fn main() {
 +    let mut it = 0..20;
 +    while let Some(..) = it.next() {
 +        println!("test");
 +    }
 +}
index 42859243855a6920d7ce5fdae686ae969dbbd0cf,0000000000000000000000000000000000000000..3236765e1db0faa069607cd734d99a4c2db548d6
mode 100644,000000..100644
--- /dev/null
@@@ -1,144 -1,0 +1,160 @@@
-   --> $DIR/while_let_on_iterator.rs:15:5
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:20:5
++  --> $DIR/while_let_on_iterator.rs:16:5
 +   |
 +LL |     while let Option::Some(x) = iter.next() {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
 +   |
 +   = note: `-D clippy::while-let-on-iterator` implied by `-D warnings`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:25:5
++  --> $DIR/while_let_on_iterator.rs:21:5
 +   |
 +LL |     while let Some(x) = iter.next() {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:101:9
++  --> $DIR/while_let_on_iterator.rs:26:5
 +   |
 +LL |     while let Some(_) = iter.next() {}
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:108:9
++  --> $DIR/while_let_on_iterator.rs:102:9
 +   |
 +LL |         while let Some([..]) = it.next() {}
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:121:9
++  --> $DIR/while_let_on_iterator.rs:109:9
 +   |
 +LL |         while let Some([_x]) = it.next() {}
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:141:9
++  --> $DIR/while_let_on_iterator.rs:122:9
 +   |
 +LL |         while let Some(x @ [_]) = it.next() {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:198:9
++  --> $DIR/while_let_on_iterator.rs:142:9
 +   |
 +LL |         while let Some(_) = y.next() {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:209:5
++  --> $DIR/while_let_on_iterator.rs:199:9
 +   |
 +LL |         while let Some(m) = it.next() {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:211:9
++  --> $DIR/while_let_on_iterator.rs:210:5
 +   |
 +LL |     while let Some(n) = it.next() {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:220:9
++  --> $DIR/while_let_on_iterator.rs:212:9
 +   |
 +LL |         while let Some(m) = it.next() {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:229:9
++  --> $DIR/while_let_on_iterator.rs:221:9
 +   |
 +LL |         while let Some(m) = it.next() {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:246:9
++  --> $DIR/while_let_on_iterator.rs:230:9
 +   |
 +LL |         while let Some(m) = it.next() {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:261:13
++  --> $DIR/while_let_on_iterator.rs:247:9
 +   |
 +LL |         while let Some(m) = it.next() {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
 +
 +error: this loop could be written as a `for` loop
- error: manual `!RangeInclusive::contains` implementation
-   --> $DIR/while_let_on_iterator.rs:262:20
-    |
- LL |                 if i < 3 || i > 7 {
-    |                    ^^^^^^^^^^^^^^ help: use: `!(3..=7).contains(&i)`
-    |
-    = note: `-D clippy::manual-range-contains` implied by `-D warnings`
++  --> $DIR/while_let_on_iterator.rs:262:13
 +   |
 +LL |             while let Some(i) = self.0.next() {
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.by_ref()`
 +
-   --> $DIR/while_let_on_iterator.rs:293:13
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:322:5
++  --> $DIR/while_let_on_iterator.rs:294:13
 +   |
 +LL |             while let Some(i) = self.0.0.0.next() {
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.0.0.by_ref()`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:334:9
++  --> $DIR/while_let_on_iterator.rs:323:5
 +   |
 +LL |     while let Some(n) = it.next() {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it.by_ref()`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:348:5
++  --> $DIR/while_let_on_iterator.rs:335:9
 +   |
 +LL |         while let Some(x) = it.next() {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:359:5
++  --> $DIR/while_let_on_iterator.rs:349:5
 +   |
 +LL |     while let Some(x) = it.next() {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:394:5
++  --> $DIR/while_let_on_iterator.rs:360:5
 +   |
 +LL |     while let Some(x) = it.0.next() {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:401:5
++  --> $DIR/while_let_on_iterator.rs:395:5
 +   |
 +LL |     while let Some(x) = s.x.next() {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in s.x.by_ref()`
 +
 +error: this loop could be written as a `for` loop
-   --> $DIR/while_let_on_iterator.rs:408:5
++  --> $DIR/while_let_on_iterator.rs:402:5
 +   |
 +LL |     while let Some(x) = x[0].next() {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in x[0].by_ref()`
 +
 +error: this loop could be written as a `for` loop
- error: aborting due to 23 previous errors
++  --> $DIR/while_let_on_iterator.rs:410:9
++   |
++LL |         while let Some(x) = it.next() {
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it`
++
++error: this loop could be written as a `for` loop
++  --> $DIR/while_let_on_iterator.rs:420:9
++   |
++LL |         while let Some(x) = it.next() {
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it`
++
++error: this loop could be written as a `for` loop
++  --> $DIR/while_let_on_iterator.rs:430:9
++   |
++LL |         while let Some(x) = it.next() {
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
++
++error: this loop could be written as a `for` loop
++  --> $DIR/while_let_on_iterator.rs:440:9
++   |
++LL |         while let Some(x) = it.next() {
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it`
++
++error: this loop could be written as a `for` loop
++  --> $DIR/while_let_on_iterator.rs:450:5
 +   |
 +LL |     while let Some(..) = it.next() {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
 +
++error: aborting due to 26 previous errors
 +
index b6f47ae906b73709b6096e5ffeeb4caa36dedab3,0000000000000000000000000000000000000000..ef55f1c31a88bef188aba44728b4e9bdda0f890f
mode 100644,000000..100644
--- /dev/null
@@@ -1,245 -1,0 +1,245 @@@
-     mod should_be_replaced_futher_inside {
 +// edition:2015
 +// run-rustfix
 +// aux-build:wildcard_imports_helper.rs
 +
 +// the 2015 edition here is needed because edition 2018 changed the module system
 +// (see https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html) which means the lint
 +// no longer detects some of the cases starting with Rust 2018.
 +// FIXME: We should likely add another edition 2021 test case for this lint
 +
 +#![warn(clippy::wildcard_imports)]
 +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)]
 +#![warn(unused_imports)]
 +
 +extern crate wildcard_imports_helper;
 +
 +use crate::fn_mod::foo;
 +use crate::mod_mod::inner_mod;
 +use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod};
 +#[macro_use]
 +use crate::struct_mod::{A, inner_struct_mod};
 +
 +#[allow(unused_imports)]
 +use wildcard_imports_helper::inner::inner_for_self_import;
 +use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar;
 +use wildcard_imports_helper::{ExternA, extern_foo};
 +
 +use std::io::prelude::*;
 +use wildcard_imports_helper::prelude::v1::*;
 +
 +struct ReadFoo;
 +
 +impl Read for ReadFoo {
 +    fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
 +        Ok(0)
 +    }
 +}
 +
 +mod fn_mod {
 +    pub fn foo() {}
 +}
 +
 +mod mod_mod {
 +    pub mod inner_mod {
 +        pub fn foo() {}
 +    }
 +}
 +
 +mod multi_fn_mod {
 +    pub fn multi_foo() {}
 +    pub fn multi_bar() {}
 +    pub fn multi_baz() {}
 +    pub mod multi_inner_mod {
 +        pub fn foo() {}
 +    }
 +}
 +
 +mod struct_mod {
 +    pub struct A;
 +    pub struct B;
 +    pub mod inner_struct_mod {
 +        pub struct C;
 +    }
 +
 +    #[macro_export]
 +    macro_rules! double_struct_import_test {
 +        () => {
 +            let _ = A;
 +        };
 +    }
 +}
 +
 +fn main() {
 +    foo();
 +    multi_foo();
 +    multi_bar();
 +    multi_inner_mod::foo();
 +    inner_mod::foo();
 +    extern_foo();
 +    inner_extern_bar();
 +
 +    let _ = A;
 +    let _ = inner_struct_mod::C;
 +    let _ = ExternA;
 +    let _ = PreludeModAnywhere;
 +
 +    double_struct_import_test!();
 +    double_struct_import_test!();
 +}
 +
 +mod in_fn_test {
 +    pub use self::inner_exported::*;
 +    #[allow(unused_imports)]
 +    pub(crate) use self::inner_exported2::*;
 +
 +    fn test_intern() {
 +        use crate::fn_mod::foo;
 +
 +        foo();
 +    }
 +
 +    fn test_extern() {
 +        use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo};
 +        use wildcard_imports_helper::{ExternA, extern_foo};
 +
 +        inner_for_self_import::inner_extern_foo();
 +        inner_extern_foo();
 +
 +        extern_foo();
 +
 +        let _ = ExternA;
 +    }
 +
 +    fn test_inner_nested() {
 +        use self::{inner::inner_foo, inner2::inner_bar};
 +
 +        inner_foo();
 +        inner_bar();
 +    }
 +
 +    fn test_extern_reexported() {
 +        use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported};
 +
 +        extern_exported();
 +        let _ = ExternExportedStruct;
 +        let _ = ExternExportedEnum::A;
 +    }
 +
 +    mod inner_exported {
 +        pub fn exported() {}
 +        pub struct ExportedStruct;
 +        pub enum ExportedEnum {
 +            A,
 +        }
 +    }
 +
 +    mod inner_exported2 {
 +        pub(crate) fn exported2() {}
 +    }
 +
 +    mod inner {
 +        pub fn inner_foo() {}
 +    }
 +
 +    mod inner2 {
 +        pub fn inner_bar() {}
 +    }
 +}
 +
 +fn test_reexported() {
 +    use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported};
 +
 +    exported();
 +    let _ = ExportedStruct;
 +    let _ = ExportedEnum::A;
 +}
 +
 +#[rustfmt::skip]
 +fn test_weird_formatting() {
 +    use crate:: in_fn_test::exported;
 +    use crate:: fn_mod::foo;
 +
 +    exported();
 +    foo();
 +}
 +
 +mod super_imports {
 +    fn foofoo() {}
 +
 +    mod should_be_replaced {
 +        use super::foofoo;
 +
 +        fn with_super() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass {
 +        use super::*;
 +
 +        fn with_super() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass_inside_function {
 +        fn with_super_inside_function() {
 +            use super::*;
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass_further_inside {
 +        fn insidefoo() {}
 +        mod inner {
 +            use super::*;
 +            fn with_super() {
 +                let _ = insidefoo();
 +            }
 +        }
 +    }
 +
++    mod should_be_replaced_further_inside {
 +        fn insidefoo() {}
 +        mod inner {
 +            use super::insidefoo;
 +            fn with_super() {
 +                let _ = insidefoo();
 +            }
 +        }
 +    }
 +
 +    mod use_explicit_should_be_replaced {
 +        use super_imports::foofoo;
 +
 +        fn with_explicit() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod use_double_super_should_be_replaced {
 +        mod inner {
 +            use super::super::foofoo;
 +
 +            fn with_double_super() {
 +                let _ = foofoo();
 +            }
 +        }
 +    }
 +
 +    mod use_super_explicit_should_be_replaced {
 +        use super::super::super_imports::foofoo;
 +
 +        fn with_super_explicit() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod attestation_should_be_replaced {
 +        use super::foofoo;
 +
 +        fn with_explicit() {
 +            let _ = foofoo();
 +        }
 +    }
 +}
index eb404b7a3de5f661cbb2af6b428ca042830ac985,0000000000000000000000000000000000000000..b81285142069b0e5d14557805ec8211ca5a4cff2
mode 100644,000000..100644
--- /dev/null
@@@ -1,246 -1,0 +1,246 @@@
-     mod should_be_replaced_futher_inside {
 +// edition:2015
 +// run-rustfix
 +// aux-build:wildcard_imports_helper.rs
 +
 +// the 2015 edition here is needed because edition 2018 changed the module system
 +// (see https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html) which means the lint
 +// no longer detects some of the cases starting with Rust 2018.
 +// FIXME: We should likely add another edition 2021 test case for this lint
 +
 +#![warn(clippy::wildcard_imports)]
 +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)]
 +#![warn(unused_imports)]
 +
 +extern crate wildcard_imports_helper;
 +
 +use crate::fn_mod::*;
 +use crate::mod_mod::*;
 +use crate::multi_fn_mod::*;
 +#[macro_use]
 +use crate::struct_mod::*;
 +
 +#[allow(unused_imports)]
 +use wildcard_imports_helper::inner::inner_for_self_import;
 +use wildcard_imports_helper::inner::inner_for_self_import::*;
 +use wildcard_imports_helper::*;
 +
 +use std::io::prelude::*;
 +use wildcard_imports_helper::prelude::v1::*;
 +
 +struct ReadFoo;
 +
 +impl Read for ReadFoo {
 +    fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
 +        Ok(0)
 +    }
 +}
 +
 +mod fn_mod {
 +    pub fn foo() {}
 +}
 +
 +mod mod_mod {
 +    pub mod inner_mod {
 +        pub fn foo() {}
 +    }
 +}
 +
 +mod multi_fn_mod {
 +    pub fn multi_foo() {}
 +    pub fn multi_bar() {}
 +    pub fn multi_baz() {}
 +    pub mod multi_inner_mod {
 +        pub fn foo() {}
 +    }
 +}
 +
 +mod struct_mod {
 +    pub struct A;
 +    pub struct B;
 +    pub mod inner_struct_mod {
 +        pub struct C;
 +    }
 +
 +    #[macro_export]
 +    macro_rules! double_struct_import_test {
 +        () => {
 +            let _ = A;
 +        };
 +    }
 +}
 +
 +fn main() {
 +    foo();
 +    multi_foo();
 +    multi_bar();
 +    multi_inner_mod::foo();
 +    inner_mod::foo();
 +    extern_foo();
 +    inner_extern_bar();
 +
 +    let _ = A;
 +    let _ = inner_struct_mod::C;
 +    let _ = ExternA;
 +    let _ = PreludeModAnywhere;
 +
 +    double_struct_import_test!();
 +    double_struct_import_test!();
 +}
 +
 +mod in_fn_test {
 +    pub use self::inner_exported::*;
 +    #[allow(unused_imports)]
 +    pub(crate) use self::inner_exported2::*;
 +
 +    fn test_intern() {
 +        use crate::fn_mod::*;
 +
 +        foo();
 +    }
 +
 +    fn test_extern() {
 +        use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
 +        use wildcard_imports_helper::*;
 +
 +        inner_for_self_import::inner_extern_foo();
 +        inner_extern_foo();
 +
 +        extern_foo();
 +
 +        let _ = ExternA;
 +    }
 +
 +    fn test_inner_nested() {
 +        use self::{inner::*, inner2::*};
 +
 +        inner_foo();
 +        inner_bar();
 +    }
 +
 +    fn test_extern_reexported() {
 +        use wildcard_imports_helper::*;
 +
 +        extern_exported();
 +        let _ = ExternExportedStruct;
 +        let _ = ExternExportedEnum::A;
 +    }
 +
 +    mod inner_exported {
 +        pub fn exported() {}
 +        pub struct ExportedStruct;
 +        pub enum ExportedEnum {
 +            A,
 +        }
 +    }
 +
 +    mod inner_exported2 {
 +        pub(crate) fn exported2() {}
 +    }
 +
 +    mod inner {
 +        pub fn inner_foo() {}
 +    }
 +
 +    mod inner2 {
 +        pub fn inner_bar() {}
 +    }
 +}
 +
 +fn test_reexported() {
 +    use crate::in_fn_test::*;
 +
 +    exported();
 +    let _ = ExportedStruct;
 +    let _ = ExportedEnum::A;
 +}
 +
 +#[rustfmt::skip]
 +fn test_weird_formatting() {
 +    use crate:: in_fn_test::  * ;
 +    use crate:: fn_mod::
 +        *;
 +
 +    exported();
 +    foo();
 +}
 +
 +mod super_imports {
 +    fn foofoo() {}
 +
 +    mod should_be_replaced {
 +        use super::*;
 +
 +        fn with_super() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass {
 +        use super::*;
 +
 +        fn with_super() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass_inside_function {
 +        fn with_super_inside_function() {
 +            use super::*;
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass_further_inside {
 +        fn insidefoo() {}
 +        mod inner {
 +            use super::*;
 +            fn with_super() {
 +                let _ = insidefoo();
 +            }
 +        }
 +    }
 +
++    mod should_be_replaced_further_inside {
 +        fn insidefoo() {}
 +        mod inner {
 +            use super::*;
 +            fn with_super() {
 +                let _ = insidefoo();
 +            }
 +        }
 +    }
 +
 +    mod use_explicit_should_be_replaced {
 +        use super_imports::*;
 +
 +        fn with_explicit() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod use_double_super_should_be_replaced {
 +        mod inner {
 +            use super::super::*;
 +
 +            fn with_double_super() {
 +                let _ = foofoo();
 +            }
 +        }
 +    }
 +
 +    mod use_super_explicit_should_be_replaced {
 +        use super::super::super_imports::*;
 +
 +        fn with_super_explicit() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod attestation_should_be_replaced {
 +        use super::*;
 +
 +        fn with_explicit() {
 +            let _ = foofoo();
 +        }
 +    }
 +}
index a4bb26b9f900587151545521e862ec67db9701b2,0000000000000000000000000000000000000000..ab98f9b4154ac0e78f3375090d0d08a3f77c5954
mode 100644,000000..100644
--- /dev/null
@@@ -1,57 -1,0 +1,57 @@@
-                 "isDefault": true,
-             },
 +{
 +    "version": "2.0.0",
 +    "tasks": [
 +        {
 +            "label": "cargo check",
 +            "type": "shell",
 +            "command": "cargo check",
 +            "problemMatcher": [],
 +            "group": {
 +                "kind": "build",
-             "group": "none",
++                "isDefault": true
++            }
 +        },
 +        {
 +            "label": "cargo dev fmt",
 +            "type": "shell",
 +            "command": "cargo dev fmt",
 +            "problemMatcher": [],
-                     "RUST_BACKTRACE": "1",
++            "group": "none"
 +        },
 +        {
 +            "label": "cargo uitest",
 +            "type": "shell",
 +            "command": "cargo uitest",
 +            "options": {
 +                "env": {
-                     // See: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md#testing
 +                    // This task will usually execute all UI tests inside `tests/ui` you can
 +                    // optionally uncomment the line below and only run a specific test.
 +                    //
-                 },
++                    // See: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/adding_lints.md#testing
 +                    //
 +                    // "TESTNAME": "<TODO>",
-                 "isDefault": true,
++                    "RUST_BACKTRACE": "1"
++                }
 +            },
 +            "problemMatcher": [],
 +            "group": {
 +                "kind": "test",
-             "group": "test",
++                "isDefault": true
 +            }
 +        },
 +        {
 +            "label": "cargo test",
 +            "type": "shell",
 +            "command": "cargo test",
 +            "problemMatcher": [],
-             "group": "none",
-         },
-     ],
++            "group": "test"
 +        },
 +        {
 +            "label": "cargo dev bless",
 +            "type": "shell",
 +            "command": "cargo dev bless",
 +            "problemMatcher": [],
++            "group": "none"
++        }
++    ]
 +}
index 6183089911bdc0d35c0e5845b7a941da8129c185,0000000000000000000000000000000000000000..c5d602ea3035f7e0a93a79df3bb715084681141c
mode 100644,000000..100644
--- /dev/null
@@@ -1,569 -1,0 +1,570 @@@
-     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/styles/github.min.css"/>
 +<!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"/>
-     <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/highlight.min.js"></script>
-     <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/languages/rust.min.js"></script>
++    <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="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 6909fbcae09ff7c2d89fd748d140aee29ac8dbb9,0000000000000000000000000000000000000000..366e7c8843f229507b1c3f3a4949d2939bd651a9
mode 100644,000000..100644
--- /dev/null
@@@ -1,386 -1,0 +1,389 @@@
-                         hljs.highlight(lang, str, true).value +
 +(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];
 +            };
 +
 +            var 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.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);
 +}