]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit '27afd6ade4bb1123a8bf82001629b69d23d62aff' into clippyup
authorflip1995 <philipp.krones@embecosm.com>
Wed, 8 Sep 2021 14:31:47 +0000 (16:31 +0200)
committerflip1995 <philipp.krones@embecosm.com>
Wed, 8 Sep 2021 14:31:47 +0000 (16:31 +0200)
179 files changed:
1  2 
src/tools/clippy/.cargo/config
src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.md
src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.md
src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md
src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md
src/tools/clippy/.github/ISSUE_TEMPLATE/ice.md
src/tools/clippy/.github/workflows/clippy_dev.yml
src/tools/clippy/CHANGELOG.md
src/tools/clippy/Cargo.toml
src/tools/clippy/README.md
src/tools/clippy/clippy_dev/src/lib.rs
src/tools/clippy/clippy_dev/src/main.rs
src/tools/clippy/clippy_lints/Cargo.toml
src/tools/clippy/clippy_lints/src/approx_const.rs
src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
src/tools/clippy/clippy_lints/src/bytecount.rs
src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
src/tools/clippy/clippy_lints/src/collapsible_match.rs
src/tools/clippy/clippy_lints/src/copies.rs
src/tools/clippy/clippy_lints/src/derivable_impls.rs
src/tools/clippy/clippy_lints/src/derive.rs
src/tools/clippy/clippy_lints/src/entry.rs
src/tools/clippy/clippy_lints/src/eval_order_dependence.rs
src/tools/clippy/clippy_lints/src/feature_name.rs
src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
src/tools/clippy/clippy_lints/src/functions/must_use.rs
src/tools/clippy/clippy_lints/src/if_let_mutex.rs
src/tools/clippy/clippy_lints/src/if_let_some_result.rs
src/tools/clippy/clippy_lints/src/let_if_seq.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs
src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
src/tools/clippy/clippy_lints/src/loops/mod.rs
src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
src/tools/clippy/clippy_lints/src/loops/needless_collect.rs
src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
src/tools/clippy/clippy_lints/src/loops/never_loop.rs
src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs
src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
src/tools/clippy/clippy_lints/src/macro_use.rs
src/tools/clippy/clippy_lints/src/manual_map.rs
src/tools/clippy/clippy_lints/src/matches.rs
src/tools/clippy/clippy_lints/src/mem_forget.rs
src/tools/clippy/clippy_lints/src/mem_replace.rs
src/tools/clippy/clippy_lints/src/methods/filter_next.rs
src/tools/clippy/clippy_lints/src/methods/manual_split_once.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/suspicious_splitn.rs
src/tools/clippy/clippy_lints/src/methods/utils.rs
src/tools/clippy/clippy_lints/src/misc.rs
src/tools/clippy/clippy_lints/src/module_style.rs
src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs
src/tools/clippy/clippy_lints/src/needless_option_as_deref.rs
src/tools/clippy/clippy_lints/src/no_effect.rs
src/tools/clippy/clippy_lints/src/open_options.rs
src/tools/clippy/clippy_lints/src/option_if_let_else.rs
src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
src/tools/clippy/clippy_lints/src/question_mark.rs
src/tools/clippy/clippy_lints/src/redundant_clone.rs
src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
src/tools/clippy/clippy_lints/src/returns.rs
src/tools/clippy/clippy_lints/src/strings.rs
src/tools/clippy/clippy_lints/src/to_string_in_display.rs
src/tools/clippy/clippy_lints/src/types/mod.rs
src/tools/clippy/clippy_lints/src/types/rc_mutex.rs
src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs
src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs
src/tools/clippy/clippy_lints/src/unused_async.rs
src/tools/clippy/clippy_lints/src/unused_io_amount.rs
src/tools/clippy/clippy_lints/src/unused_self.rs
src/tools/clippy/clippy_lints/src/unwrap.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/utils/inspector.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/vec.rs
src/tools/clippy/clippy_utils/Cargo.toml
src/tools/clippy/clippy_utils/src/diagnostics.rs
src/tools/clippy/clippy_utils/src/higher.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/qualify_min_const_fn.rs
src/tools/clippy/clippy_utils/src/sugg.rs
src/tools/clippy/clippy_utils/src/ty.rs
src/tools/clippy/clippy_utils/src/usage.rs
src/tools/clippy/clippy_utils/src/visitors.rs
src/tools/clippy/doc/adding_lints.md
src/tools/clippy/doc/common_tools_writing_lints.md
src/tools/clippy/lintcheck/Cargo.toml
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/fmt.rs
src/tools/clippy/tests/integration.rs
src/tools/clippy/tests/lint_message_convention.rs
src/tools/clippy/tests/missing-test-files.rs
src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.toml
src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.rs
src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.stderr
src/tools/clippy/tests/ui-cargo/feature_name/pass/Cargo.toml
src/tools/clippy/tests/ui-cargo/feature_name/pass/src/main.rs
src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.toml
src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner.rs
src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff.rs
src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff/most.rs
src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/mod.rs
src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.rs
src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.stderr
src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml
src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/bad/mod.rs
src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.rs
src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr
src/tools/clippy/tests/ui-cargo/module_style/pass_mod/Cargo.toml
src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/bad/mod.rs
src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/main.rs
src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/foo.rs
src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/inner/mod.rs
src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/mod.rs
src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml
src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/good.rs
src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/main.rs
src/tools/clippy/tests/ui/approx_const.rs
src/tools/clippy/tests/ui/approx_const.stderr
src/tools/clippy/tests/ui/auxiliary/option_helpers.rs
src/tools/clippy/tests/ui/bool_assert_comparison.rs
src/tools/clippy/tests/ui/bool_assert_comparison.stderr
src/tools/clippy/tests/ui/box_vec.rs
src/tools/clippy/tests/ui/box_vec.stderr
src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr
src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr
src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
src/tools/clippy/tests/ui/derivable_impls.rs
src/tools/clippy/tests/ui/derivable_impls.stderr
src/tools/clippy/tests/ui/entry.fixed
src/tools/clippy/tests/ui/entry.rs
src/tools/clippy/tests/ui/entry.stderr
src/tools/clippy/tests/ui/entry_btree.fixed
src/tools/clippy/tests/ui/entry_btree.rs
src/tools/clippy/tests/ui/entry_btree.stderr
src/tools/clippy/tests/ui/linkedlist.rs
src/tools/clippy/tests/ui/linkedlist.stderr
src/tools/clippy/tests/ui/manual_flatten.rs
src/tools/clippy/tests/ui/manual_map_option_2.fixed
src/tools/clippy/tests/ui/manual_map_option_2.rs
src/tools/clippy/tests/ui/manual_map_option_2.stderr
src/tools/clippy/tests/ui/manual_split_once.fixed
src/tools/clippy/tests/ui/manual_split_once.rs
src/tools/clippy/tests/ui/manual_split_once.stderr
src/tools/clippy/tests/ui/mem_replace.fixed
src/tools/clippy/tests/ui/mem_replace.rs
src/tools/clippy/tests/ui/mem_replace.stderr
src/tools/clippy/tests/ui/methods.rs
src/tools/clippy/tests/ui/min_rust_version_attr.rs
src/tools/clippy/tests/ui/min_rust_version_attr.stderr
src/tools/clippy/tests/ui/mut_range_bound.rs
src/tools/clippy/tests/ui/mut_range_bound.stderr
src/tools/clippy/tests/ui/needless_option_as_deref.fixed
src/tools/clippy/tests/ui/needless_option_as_deref.rs
src/tools/clippy/tests/ui/needless_option_as_deref.stderr
src/tools/clippy/tests/ui/option_if_let_else.fixed
src/tools/clippy/tests/ui/option_if_let_else.rs
src/tools/clippy/tests/ui/option_if_let_else.stderr
src/tools/clippy/tests/ui/proc_macro.stderr
src/tools/clippy/tests/ui/rc_mutex.rs
src/tools/clippy/tests/ui/rc_mutex.stderr
src/tools/clippy/tests/ui/redundant_allocation.rs
src/tools/clippy/tests/ui/redundant_allocation.stderr
src/tools/clippy/tests/ui/unnecessary_operation.fixed
src/tools/clippy/tests/ui/unnecessary_operation.stderr
src/tools/clippy/tests/versioncheck.rs

index e95ea224cb681361993f8643bd2be639d420a288,0000000000000000000000000000000000000000..84ae36a46d71de51a394f27bc3fe2533c2ccec60
mode 100644,000000..100644
--- /dev/null
@@@ -1,8 -1,0 +1,9 @@@
- rustflags = ["-Zunstable-options"]
 +[alias]
 +uitest = "test --test compile-test"
 +dev = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
 +lintcheck = "run --target-dir lintcheck/target --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml  -- "
 +collect-metadata = "test --test dogfood --features metadata-collector-lint -- run_metadata_collection_lint --ignored"
 +
 +[build]
++# -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests
++rustflags = ["-Zunstable-options", "-Zbinary-dep-depinfo"]
index 9aef3ebe637a1d1b325266165389786032876f93,0000000000000000000000000000000000000000..2891d5e5da1e1ec7490817e6089a867ad1c47ff4
mode 100644,000000..100644
--- /dev/null
@@@ -1,4 -1,0 +1,18 @@@
 +---
 +name: Blank Issue
 +about: Create a blank issue.
 +---
++
++
++<!--
++Additional labels can be added to this issue by including the following command
++(without the space after the @ symbol):
++
++`@rustbot label +<label>`
++
++Common labels for this issue type are:
++* C-an-interesting-project
++* C-enhancement
++* C-question
++* C-tracking-issue
++-->
index 2bc87db123d4d38d126ba80a597f43b0d3e8524b,0000000000000000000000000000000000000000..87c18cdee66c5d24d96ddf95aafe305a9b2beaf7
mode 100644,000000..100644
--- /dev/null
@@@ -1,47 -1,0 +1,43 @@@
- - `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20)
- - `rustc -Vv`:
-   ```
-   rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-   binary: rustc
-   commit-hash: f455e46eae1a227d735091091144601b467e1565
-   commit-date: 2020-06-20
-   host: x86_64-unknown-linux-gnu
-   release: 1.46.0-nightly
-   LLVM version: 10.0
-   ```
 +---
 +name: Bug Report
 +about: Create a bug report for Clippy
 +labels: C-bug
 +---
 +<!--
 +Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
 +along with any information you feel relevant to replicating the bug.
 +-->
 +
 +I tried this code:
 +
 +```rust
 +<code>
 +```
 +
 +I expected to see this happen: *explanation*
 +
 +Instead, this happened: *explanation*
 +
 +### Meta
 +
- Include a backtrace in the code block by setting `RUST_BACKTRACE=1` in your
- environment. E.g. `RUST_BACKTRACE=1 cargo clippy`.
++**Rust version (`rustc -Vv`):**
++
++```
++rustc 1.46.0-nightly (f455e46ea 2020-06-20)
++binary: rustc
++commit-hash: f455e46eae1a227d735091091144601b467e1565
++commit-date: 2020-06-20
++host: x86_64-unknown-linux-gnu
++release: 1.46.0-nightly
++LLVM version: 10.0
++```
 +
 +<!--
- <details><summary>Backtrace</summary>
-   <p>
-   
-   ```
-   <backtrace>
-   ```
-   
-   </p>
- </details>
++Additional labels can be added to this issue by including the following command
++(without the space after the @ symbol):
++
++`@rustbot label +<label>`
++
++Common labels for this issue type are:
++* `I-suggestion-causes-error`
 +-->
index 53341c7a928afd77a9ccac06bb3f4b9b992e31c2,0000000000000000000000000000000000000000..d9ea2db34edd7cf74a9789f17801005a3e0762e9
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,35 @@@
- - `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20)
- - `rustc -Vv`:
-   ```
-   rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-   binary: rustc
-   commit-hash: f455e46eae1a227d735091091144601b467e1565
-   commit-date: 2020-06-20
-   host: x86_64-unknown-linux-gnu
-   release: 1.46.0-nightly
-   LLVM version: 10.0
-   ```
 +---
 +name: Bug Report (False Negative)
 +about: Create a bug report about missing warnings from a lint
 +labels: C-bug, I-false-negative
 +---
 +<!--
 +Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
 +along with any information you feel relevant to replicating the bug.
 +-->
 +Lint name:
 +
 +
 +I tried this code:
 +
 +```rust
 +<code>
 +```
 +
 +I expected to see this happen: *explanation*
 +
 +Instead, this happened: *explanation*
 +
 +### Meta
 +
++**Rust version (`rustc -Vv`):**
++
++```
++rustc 1.46.0-nightly (f455e46ea 2020-06-20)
++binary: rustc
++commit-hash: f455e46eae1a227d735091091144601b467e1565
++commit-date: 2020-06-20
++host: x86_64-unknown-linux-gnu
++release: 1.46.0-nightly
++LLVM version: 10.0
++```
index 34fd6f4bdb7105f86b548c51fc864a54b12c79fa,0000000000000000000000000000000000000000..4170b9ff2dbe5d149854fa8c2167efc4fec45a52
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,44 @@@
- - `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20)
- - `rustc -Vv`:
-   ```
-   rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-   binary: rustc
-   commit-hash: f455e46eae1a227d735091091144601b467e1565
-   commit-date: 2020-06-20
-   host: x86_64-unknown-linux-gnu
-   release: 1.46.0-nightly
-   LLVM version: 10.0
-   ```
 +---
 +name: Bug Report (False Positive)
 +about: Create a bug report about a wrongly emitted lint warning
 +labels: C-bug, I-false-positive
 +---
 +<!--
 +Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
 +along with any information you feel relevant to replicating the bug.
 +-->
 +Lint name:
 +
 +
 +I tried this code:
 +
 +```rust
 +<code>
 +```
 +
 +I expected to see this happen: *explanation*
 +
 +Instead, this happened: *explanation*
 +
 +### Meta
 +
++**Rust version (`rustc -Vv`):**
++```
++rustc 1.46.0-nightly (f455e46ea 2020-06-20)
++binary: rustc
++commit-hash: f455e46eae1a227d735091091144601b467e1565
++commit-date: 2020-06-20
++host: x86_64-unknown-linux-gnu
++release: 1.46.0-nightly
++LLVM version: 10.0
++```
++
++<!--
++Additional labels can be added to this issue by including the following command
++(without the space after the @ symbol):
++
++`@rustbot label +<label>`
++
++Common labels for this issue type are:
++* I-suggestion-causes-error
++-->
index 0b7cd1ed0fb60fb7921be8e6393a04de1e4f80cb,0000000000000000000000000000000000000000..6c1bed663c6cb37f74bb9dfd9f4226560bc8837c
mode 100644,000000..100644
--- /dev/null
@@@ -1,53 -1,0 +1,52 @@@
- - `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20)
- - `rustc -Vv`:
-   ```
-   rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-   binary: rustc
-   commit-hash: f455e46eae1a227d735091091144601b467e1565
-   commit-date: 2020-06-20
-   host: x86_64-unknown-linux-gnu
-   release: 1.46.0-nightly
-   LLVM version: 10.0
-   ```
 +---
 +name: Internal Compiler Error
 +about: Create a report for an internal compiler error in Clippy.
 +labels: C-bug, I-ICE
 +---
 +<!--
 +Thank you for finding an Internal Compiler Error! 🧊  If possible, try to provide
 +a minimal verifiable example. You can read "Rust Bug Minimization Patterns" for
 +how to create smaller examples.
 +
 +http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/
 +
 +-->
 +
 +### Code
 +
 +```rust
 +<code>
 +```
 +
 +### Meta
 +
++**Rust version (`rustc -Vv`):**
++```
++rustc 1.46.0-nightly (f455e46ea 2020-06-20)
++binary: rustc
++commit-hash: f455e46eae1a227d735091091144601b467e1565
++commit-date: 2020-06-20
++host: x86_64-unknown-linux-gnu
++release: 1.46.0-nightly
++LLVM version: 10.0
++```
 +
 +### Error output
 +
 +```
 +<output>
 +```
 +
 +<!--
 +Include a backtrace in the code block by setting `RUST_BACKTRACE=1` in your
 +environment. E.g. `RUST_BACKTRACE=1 cargo clippy`.
 +-->
 +<details><summary>Backtrace</summary>
 +  <p>
 +  
 +  ```
 +  <backtrace>
 +  ```
 +  
 +  </p>
 +</details>
index 95da775b7bc360ce4fcdb59e6fbb6a3935aa4766,0000000000000000000000000000000000000000..9a5416153abdb62856d4ad34305f806294aa43f9
mode 100644,000000..100644
--- /dev/null
@@@ -1,78 -1,0 +1,75 @@@
-     - name: Test limit_stderr_length
-       run: cargo dev limit_stderr_length
 +name: Clippy Dev Test
 +
 +on:
 +  push:
 +    branches:
 +      - auto
 +      - try
 +  pull_request:
 +    # Only run on paths, that get checked by the clippy_dev tool
 +    paths:
 +    - 'CHANGELOG.md'
 +    - 'README.md'
 +    - '**.stderr'
 +    - '**.rs'
 +
 +env:
 +  RUST_BACKTRACE: 1
 +
 +jobs:
 +  clippy_dev:
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +    # Setup
 +    - name: Checkout
 +      uses: actions/checkout@v2.3.3
 +
 +    - name: remove toolchain file
 +      run: rm rust-toolchain
 +
 +    - name: rust-toolchain
 +      uses: actions-rs/toolchain@v1.0.6
 +      with:
 +        toolchain: nightly
 +        target: x86_64-unknown-linux-gnu
 +        profile: minimal
 +        components: rustfmt
 +        default: true
 +
 +    # Run
 +    - name: Build
 +      run: cargo build --features deny-warnings
 +      working-directory: clippy_dev
 +
 +    - name: Test update_lints
 +      run: cargo dev update_lints --check
 +
 +    - name: Test fmt
 +      run: cargo dev fmt --check
 +
 +  # These jobs doesn't actually test anything, but they're only used to tell
 +  # bors the build completed, as there is no practical way to detect when a
 +  # workflow is successful listening to webhooks only.
 +  #
 +  # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
 +
 +  end-success:
 +    name: bors dev test finished
 +    if: github.event.pusher.name == 'bors' && success()
 +    runs-on: ubuntu-latest
 +    needs: [clippy_dev]
 +
 +    steps:
 +      - name: Mark the job as successful
 +        run: exit 0
 +
 +  end-failure:
 +    name: bors dev test finished
 +    if: github.event.pusher.name == 'bors' && (failure() || cancelled())
 +    runs-on: ubuntu-latest
 +    needs: [clippy_dev]
 +
 +    steps:
 +      - name: Mark the job as a failure
 +        run: exit 1
index 2b89170073be513bca6869b916212e05d9fb2935,0000000000000000000000000000000000000000..f5ac2f7c9f8c92a596aeb79401441bb8eaf4c3b3
mode 100644,000000..100644
--- /dev/null
@@@ -1,3039 -1,0 +1,3045 @@@
- * [`invalid_atomic_ordering`]: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update`
 +# Changelog
 +
 +All notable changes to this project will be documented in this file.
 +See [Changelog Update](doc/changelog_update.md) if you want to update this
 +document.
 +
 +## Unreleased / In Rust Nightly
 +
 +[74d1561...master](https://github.com/rust-lang/rust-clippy/compare/74d1561...master)
 +
 +## Rust 1.55
 +
 +Current beta, release 2021-09-09
 +
 +[3ae8faf...74d1561](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...74d1561)
 +
 +### Important Changes
 +
 +* Stabilized `cargo clippy --fix` :tada:
 +  [#7405](https://github.com/rust-lang/rust-clippy/pull/7405)
 +
 +### New Lints
 +
 +* [`rc_mutex`]
 +  [#7316](https://github.com/rust-lang/rust-clippy/pull/7316)
 +* [`nonstandard_macro_braces`]
 +  [#7299](https://github.com/rust-lang/rust-clippy/pull/7299)
 +* [`strlen_on_c_strings`]
 +  [#7243](https://github.com/rust-lang/rust-clippy/pull/7243)
 +* [`self_named_constructors`]
 +  [#7403](https://github.com/rust-lang/rust-clippy/pull/7403)
 +* [`disallowed_script_idents`]
 +  [#7400](https://github.com/rust-lang/rust-clippy/pull/7400)
 +* [`disallowed_type`]
 +  [#7315](https://github.com/rust-lang/rust-clippy/pull/7315)
 +* [`missing_enforced_import_renames`]
 +  [#7300](https://github.com/rust-lang/rust-clippy/pull/7300)
 +* [`extend_with_drain`]
 +  [#7270](https://github.com/rust-lang/rust-clippy/pull/7270)
 +
 +### Moves and Deprecations
 +
 +* Moved [`from_iter_instead_of_collect`] to `pedantic`
 +  [#7375](https://github.com/rust-lang/rust-clippy/pull/7375)
 +* Added `suspicious` as a new lint group for *code that is most likely wrong or useless*
 +  [#7350](https://github.com/rust-lang/rust-clippy/pull/7350)
 +  * Moved [`blanket_clippy_restriction_lints`] to `suspicious`
 +  * Moved [`empty_loop`] to `suspicious`
 +  * Moved [`eval_order_dependence`] to `suspicious`
 +  * Moved [`float_equality_without_abs`] to `suspicious`
 +  * Moved [`for_loops_over_fallibles`] to `suspicious`
 +  * Moved [`misrefactored_assign_op`] to `suspicious`
 +  * Moved [`mut_range_bound`] to `suspicious`
 +  * Moved [`mutable_key_type`] to `suspicious`
 +  * Moved [`suspicious_arithmetic_impl`] to `suspicious`
 +  * Moved [`suspicious_assignment_formatting`] to `suspicious`
 +  * Moved [`suspicious_else_formatting`] to `suspicious`
 +  * Moved [`suspicious_map`] to `suspicious`
 +  * Moved [`suspicious_op_assign_impl`] to `suspicious`
 +  * Moved [`suspicious_unary_op_formatting`] to `suspicious`
 +
 +### Enhancements
 +
 +* [`while_let_on_iterator`]: Now suggests `&mut iter` inside closures
 +  [#7262](https://github.com/rust-lang/rust-clippy/pull/7262)
 +* [`doc_markdown`]:
 +  * Now detects unbalanced ticks
 +    [#7357](https://github.com/rust-lang/rust-clippy/pull/7357)
 +  * Add `FreeBSD` to the default configuration as an allowed identifier
 +    [#7334](https://github.com/rust-lang/rust-clippy/pull/7334)
 +* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]: Now allows wildcards for enums with unstable
 +  or hidden variants
 +  [#7407](https://github.com/rust-lang/rust-clippy/pull/7407)
 +* [`redundant_allocation`]: Now additionally supports the `Arc<>` type
 +  [#7308](https://github.com/rust-lang/rust-clippy/pull/7308)
 +* [`blacklisted_name`]: Now allows blacklisted names in test code
 +  [#7379](https://github.com/rust-lang/rust-clippy/pull/7379)
 +* [`redundant_closure`]: Suggests `&mut` for `FnMut`
 +  [#7437](https://github.com/rust-lang/rust-clippy/pull/7437)
 +* [`disallowed_method`], [`disallowed_type`]: The configuration values `disallowed-method` and `disallowed-type`
 +  no longer require fully qualified paths
 +  [#7345](https://github.com/rust-lang/rust-clippy/pull/7345)
 +* [`zst_offset`]: Fixed lint invocation after it was accidentally suppressed
 +  [#7396](https://github.com/rust-lang/rust-clippy/pull/7396)
 +
 +### False Positive Fixes
 +
 +* [`default_numeric_fallback`]: No longer lints on float literals as function arguments
 +  [#7446](https://github.com/rust-lang/rust-clippy/pull/7446)
 +* [`use_self`]: No longer lints on type parameters
 +  [#7288](https://github.com/rust-lang/rust-clippy/pull/7288)
 +* [`unimplemented`]: Now ignores the `assert` and `debug_assert` macros
 +  [#7439](https://github.com/rust-lang/rust-clippy/pull/7439)
 +* [`branches_sharing_code`]: Now always checks for block expressions
 +  [#7462](https://github.com/rust-lang/rust-clippy/pull/7462)
 +* [`field_reassign_with_default`]: No longer triggers in macros
 +  [#7160](https://github.com/rust-lang/rust-clippy/pull/7160)
 +* [`redundant_clone`]: No longer lints on required clones for borrowed data
 +  [#7346](https://github.com/rust-lang/rust-clippy/pull/7346)
 +* [`default_numeric_fallback`]: No longer triggers in external macros
 +  [#7325](https://github.com/rust-lang/rust-clippy/pull/7325)
 +* [`needless_bool`]: No longer lints in macros
 +  [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
 +* [`useless_format`]: No longer triggers when additional text is being appended
 +  [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
 +* [`assertions_on_constants`]: `cfg!(...)` is no longer considered to be a constant
 +  [#7319](https://github.com/rust-lang/rust-clippy/pull/7319)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`needless_collect`]: Now show correct lint messages for shadowed values
 +  [#7289](https://github.com/rust-lang/rust-clippy/pull/7289)
 +* [`wrong_pub_self_convention`]: The deprecated message now suggest the correct configuration value
 +  [#7382](https://github.com/rust-lang/rust-clippy/pull/7382)
 +* [`semicolon_if_nothing_returned`]: Allow missing semicolon in blocks with only one expression
 +  [#7326](https://github.com/rust-lang/rust-clippy/pull/7326)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#7470](https://github.com/rust-lang/rust-clippy/pull/7470)
 +* [`redundant_pattern_matching`]
 +  [#7471](https://github.com/rust-lang/rust-clippy/pull/7471)
 +* [`modulo_one`]
 +  [#7473](https://github.com/rust-lang/rust-clippy/pull/7473)
 +* [`use_self`]
 +  [#7428](https://github.com/rust-lang/rust-clippy/pull/7428)
 +
 +### Documentation Improvements
 +
 +* Reworked Clippy's website:
 +  [#7279](https://github.com/rust-lang/rust-clippy/pull/7279)
 +  [#7172](https://github.com/rust-lang/rust-clippy/issues/7172)
 +  * Added applicability information about lints
 +  * Added a link to jump into the implementation
 +  * Improved loading times
 +  * Adapted some styling
 +* Clippy now uses a lint to generate its documentation
 +  [#7298](https://github.com/rust-lang/rust-clippy/pull/7298)
 +
 +## Rust 1.54
 +
 +Current stable, released 2021-07-29
 +
 +[7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
 +
 +### New Lints
 +
 +- [`ref_binding_to_reference`]
 +  [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
 +- [`needless_bitwise_bool`]
 +  [#7133](https://github.com/rust-lang/rust-clippy/pull/7133)
 +- [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225)
 +- [`manual_str_repeat`]
 +  [#7265](https://github.com/rust-lang/rust-clippy/pull/7265)
 +- [`suspicious_splitn`]
 +  [#7292](https://github.com/rust-lang/rust-clippy/pull/7292)
 +
 +### Moves and Deprecations
 +
 +- Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of
 +  the new `avoid-breaking-exported-api` config option (see
 +  [Enhancements](#1-54-enhancements))
 +  [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
 +- Move [`inconsistent_struct_constructor`] to `pedantic`
 +  [#7193](https://github.com/rust-lang/rust-clippy/pull/7193)
 +- Move [`needless_borrow`] to `style` (now warn-by-default)
 +  [#7254](https://github.com/rust-lang/rust-clippy/pull/7254)
 +- Move [`suspicious_operation_groupings`] to `nursery`
 +  [#7266](https://github.com/rust-lang/rust-clippy/pull/7266)
 +- Move [`semicolon_if_nothing_returned`] to `pedantic`
 +  [#7268](https://github.com/rust-lang/rust-clippy/pull/7268)
 +
 +### Enhancements <a name="1-54-enhancements"></a>
 +
 +- [`while_let_on_iterator`]: Now also lints in nested loops
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix`
 +  [#7156](https://github.com/rust-lang/rust-clippy/pull/7156)
 +- [`needless_collect`]: Now also lints on assignments with type annotations
 +  [#7163](https://github.com/rust-lang/rust-clippy/pull/7163)
 +- [`if_then_some_else_none`]: Now works with the MSRV config
 +  [#7177](https://github.com/rust-lang/rust-clippy/pull/7177)
 +- Add `avoid-breaking-exported-api` config option for the lints
 +  [`enum_variant_names`], [`large_types_passed_by_value`],
 +  [`trivially_copy_pass_by_ref`], [`unnecessary_wraps`],
 +  [`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set
 +  this configuration option to `false` before a major release (1.0/2.0/...) to
 +  clean up the API [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
 +- [`needless_collect`]: Now lints on even more data structures
 +  [#7188](https://github.com/rust-lang/rust-clippy/pull/7188)
 +- [`missing_docs_in_private_items`]: No longer sees `#[<name> = "<value>"]` like
 +  attributes as sufficient documentation
 +  [#7281](https://github.com/rust-lang/rust-clippy/pull/7281)
 +- [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]:
 +  Now work as expected when used with `allow`
 +  [#7282](https://github.com/rust-lang/rust-clippy/pull/7282)
 +
 +### False Positive Fixes
 +
 +- [`implicit_return`]: Now takes all diverging functions in account to avoid
 +  false positives [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
 +- [`while_let_on_iterator`]: No longer lints when the iterator is a struct field
 +  and the struct is used in the loop
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`multiple_inherent_impl`]: No longer lints with generic arguments
 +  [#7089](https://github.com/rust-lang/rust-clippy/pull/7089)
 +- [`comparison_chain`]: No longer lints in a `const` context
 +  [#7118](https://github.com/rust-lang/rust-clippy/pull/7118)
 +- [`while_immutable_condition`]: Fix false positive where mutation in the loop
 +  variable wasn't picked up
 +  [#7144](https://github.com/rust-lang/rust-clippy/pull/7144)
 +- [`default_trait_access`]: No longer lints in macros
 +  [#7150](https://github.com/rust-lang/rust-clippy/pull/7150)
 +- [`needless_question_mark`]: No longer lints when the inner value is implicitly
 +  dereferenced [#7165](https://github.com/rust-lang/rust-clippy/pull/7165)
 +- [`unused_unit`]: No longer lints when multiple macro contexts are involved
 +  [#7167](https://github.com/rust-lang/rust-clippy/pull/7167)
 +- [`eval_order_dependence`]: Fix false positive in async context
 +  [#7174](https://github.com/rust-lang/rust-clippy/pull/7174)
 +- [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the
 +  type [#7175](https://github.com/rust-lang/rust-clippy/pull/7175)
 +- [`wrong_self_convention`]: No longer lints in trait implementations of
 +  non-`Copy` types [#7182](https://github.com/rust-lang/rust-clippy/pull/7182)
 +- [`suboptimal_flops`]: No longer lints on `powi(2)`
 +  [#7201](https://github.com/rust-lang/rust-clippy/pull/7201)
 +- [`wrong_self_convention`]: No longer lints if there is no implicit `self`
 +  [#7215](https://github.com/rust-lang/rust-clippy/pull/7215)
 +- [`option_if_let_else`]: No longer lints on `else if let` pattern
 +  [#7216](https://github.com/rust-lang/rust-clippy/pull/7216)
 +- [`use_self`], [`useless_conversion`]: Fix false positives when generic
 +  arguments are involved
 +  [#7223](https://github.com/rust-lang/rust-clippy/pull/7223)
 +- [`manual_unwrap_or`]: Fix false positive with deref coercion
 +  [#7233](https://github.com/rust-lang/rust-clippy/pull/7233)
 +- [`similar_names`]: No longer lints on `wparam`/`lparam`
 +  [#7255](https://github.com/rust-lang/rust-clippy/pull/7255)
 +- [`redundant_closure`]: No longer lints on using the `vec![]` macro in a
 +  closure [#7263](https://github.com/rust-lang/rust-clippy/pull/7263)
 +
 +### Suggestion Fixes/Improvements
 +
 +- [`implicit_return`]
 +  [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
 +    - Fix suggestion for async functions
 +    - Improve suggestion with macros
 +    - Suggest to change `break` to `return` when appropriate
 +- [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`match_single_binding`]: Improve suggestion when match scrutinee has side
 +  effects [#7095](https://github.com/rust-lang/rust-clippy/pull/7095)
 +- [`needless_borrow`]: Now suggests to also change usage sites as needed
 +  [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
 +- [`write_with_newline`]: Improve suggestion when only `\n` is written to the
 +  buffer [#7183](https://github.com/rust-lang/rust-clippy/pull/7183)
 +- [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also
 +  when a `<_ as Trait>::_` is involved
 +  [#7264](https://github.com/rust-lang/rust-clippy/pull/7264)
 +- [`not_unsafe_ptr_arg_deref`]: Improved error message
 +  [#7294](https://github.com/rust-lang/rust-clippy/pull/7294)
 +
 +### ICE Fixes
 +
 +- Fix ICE when running Clippy on `libstd`
 +  [#7140](https://github.com/rust-lang/rust-clippy/pull/7140)
 +- [`implicit_return`]
 +  [#7242](https://github.com/rust-lang/rust-clippy/pull/7242)
 +
 +## Rust 1.53
 +
 +Released 2021-06-17
 +
 +[6ed6f1e...7c7683c](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...7c7683c)
 +
 +### New Lints
 +
 +* [`option_filter_map`]
 +  [#6342](https://github.com/rust-lang/rust-clippy/pull/6342)
 +* [`branches_sharing_code`]
 +  [#6463](https://github.com/rust-lang/rust-clippy/pull/6463)
 +* [`needless_for_each`]
 +  [#6706](https://github.com/rust-lang/rust-clippy/pull/6706)
 +* [`if_then_some_else_none`]
 +  [#6859](https://github.com/rust-lang/rust-clippy/pull/6859)
 +* [`non_octal_unix_permissions`]
 +  [#7001](https://github.com/rust-lang/rust-clippy/pull/7001)
 +* [`unnecessary_self_imports`]
 +  [#7072](https://github.com/rust-lang/rust-clippy/pull/7072)
 +* [`bool_assert_comparison`]
 +  [#7083](https://github.com/rust-lang/rust-clippy/pull/7083)
 +* [`cloned_instead_of_copied`]
 +  [#7098](https://github.com/rust-lang/rust-clippy/pull/7098)
 +* [`flat_map_option`]
 +  [#7101](https://github.com/rust-lang/rust-clippy/pull/7101)
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`filter_map`] lint
 +  [#7059](https://github.com/rust-lang/rust-clippy/pull/7059)
 +* Move [`transmute_ptr_to_ptr`] to `pedantic`
 +  [#7102](https://github.com/rust-lang/rust-clippy/pull/7102)
 +
 +### Enhancements
 +
 +* [`mem_replace_with_default`]: Also lint on common std constructors
 +  [#6820](https://github.com/rust-lang/rust-clippy/pull/6820)
 +* [`wrong_self_convention`]: Also lint on `to_*_mut` methods
 +  [#6828](https://github.com/rust-lang/rust-clippy/pull/6828)
 +* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]:
 +  [#6863](https://github.com/rust-lang/rust-clippy/pull/6863)
 +    * Attempt to find a common path prefix in suggestion
 +    * Don't lint on `Option` and `Result`
 +    * Consider `Self` prefix
 +* [`explicit_deref_methods`]: Also lint on chained `deref` calls
 +  [#6865](https://github.com/rust-lang/rust-clippy/pull/6865)
 +* [`or_fun_call`]: Also lint on `unsafe` blocks
 +  [#6928](https://github.com/rust-lang/rust-clippy/pull/6928)
 +* [`vec_box`], [`linkedlist`], [`option_option`]: Also lint in `const` and
 +  `static` items [#6938](https://github.com/rust-lang/rust-clippy/pull/6938)
 +* [`search_is_some`]: Also check for `is_none`
 +  [#6942](https://github.com/rust-lang/rust-clippy/pull/6942)
 +* [`string_lit_as_bytes`]: Also lint on `into_bytes`
 +  [#6959](https://github.com/rust-lang/rust-clippy/pull/6959)
 +* [`len_without_is_empty`]: Also lint if function signatures of `len` and
 +  `is_empty` don't match
 +  [#6980](https://github.com/rust-lang/rust-clippy/pull/6980)
 +* [`redundant_pattern_matching`]: Also lint if the pattern is a `&` pattern
 +  [#6991](https://github.com/rust-lang/rust-clippy/pull/6991)
 +* [`clone_on_copy`]: Also lint on chained method calls taking `self` by value
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`missing_panics_doc`]: Also lint on `assert_eq!` and `assert_ne!`
 +  [#7029](https://github.com/rust-lang/rust-clippy/pull/7029)
 +* [`needless_return`]: Also lint in `async` functions
 +  [#7067](https://github.com/rust-lang/rust-clippy/pull/7067)
 +* [`unused_io_amount`]: Also lint on expressions like `_.read().ok()?`
 +  [#7100](https://github.com/rust-lang/rust-clippy/pull/7100)
 +* [`iter_cloned_collect`]: Also lint on large arrays, since const-generics are
 +  now stable [#7138](https://github.com/rust-lang/rust-clippy/pull/7138)
 +
 +### False Positive Fixes
 +
 +* [`upper_case_acronyms`]: No longer lints on public items
 +  [#6805](https://github.com/rust-lang/rust-clippy/pull/6805)
 +* [`suspicious_map`]: No longer lints when side effects may occur inside the
 +  `map` call [#6831](https://github.com/rust-lang/rust-clippy/pull/6831)
 +* [`manual_map`], [`manual_unwrap_or`]: No longer lints in `const` functions
 +  [#6917](https://github.com/rust-lang/rust-clippy/pull/6917)
 +* [`wrong_self_convention`]: Now respects `Copy` types
 +  [#6924](https://github.com/rust-lang/rust-clippy/pull/6924)
 +* [`needless_question_mark`]: No longer lints if the `?` and the `Some(..)` come
 +  from different macro contexts [#6935](https://github.com/rust-lang/rust-clippy/pull/6935)
 +* [`map_entry`]: Better detect if the entry API can be used
 +  [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
 +* [`or_fun_call`]: No longer lints on some `len` function calls
 +  [#6950](https://github.com/rust-lang/rust-clippy/pull/6950)
 +* [`new_ret_no_self`]: No longer lints when `Self` is returned with different
 +  generic arguments [#6952](https://github.com/rust-lang/rust-clippy/pull/6952)
 +* [`upper_case_acronyms`]: No longer lints on public items
 +  [#6981](https://github.com/rust-lang/rust-clippy/pull/6981)
 +* [`explicit_into_iter_loop`]: Only lint when `into_iter` is an implementation
 +  of `IntoIterator` [#6982](https://github.com/rust-lang/rust-clippy/pull/6982)
 +* [`expl_impl_clone_on_copy`]: Take generic constraints into account before
 +  suggesting to use `derive` instead
 +  [#6993](https://github.com/rust-lang/rust-clippy/pull/6993)
 +* [`missing_panics_doc`]: No longer lints when only debug-assertions are used
 +  [#6996](https://github.com/rust-lang/rust-clippy/pull/6996)
 +* [`clone_on_copy`]: Only lint when using the `Clone` trait
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`wrong_self_convention`]: No longer lints inside a trait implementation
 +  [#7002](https://github.com/rust-lang/rust-clippy/pull/7002)
 +* [`redundant_clone`]: No longer lints when the cloned value is modified while
 +  the clone is in use
 +  [#7011](https://github.com/rust-lang/rust-clippy/pull/7011)
 +* [`same_item_push`]: No longer lints if the `Vec` is used in the loop body
 +  [#7018](https://github.com/rust-lang/rust-clippy/pull/7018)
 +* [`cargo_common_metadata`]: Remove author requirement
 +  [#7026](https://github.com/rust-lang/rust-clippy/pull/7026)
 +* [`panic_in_result_fn`]: No longer lints on `debug_assert` family
 +  [#7060](https://github.com/rust-lang/rust-clippy/pull/7060)
 +* [`panic`]: No longer wrongfully lints on `debug_assert` with message
 +  [#7063](https://github.com/rust-lang/rust-clippy/pull/7063)
 +* [`wrong_self_convention`]: No longer lints in trait implementations where no
 +  `self` is involved [#7064](https://github.com/rust-lang/rust-clippy/pull/7064)
 +* [`missing_const_for_fn`]: No longer lints when unstable `const` function is
 +  involved [#7076](https://github.com/rust-lang/rust-clippy/pull/7076)
 +* [`suspicious_else_formatting`]: Allow Allman style braces
 +  [#7087](https://github.com/rust-lang/rust-clippy/pull/7087)
 +* [`inconsistent_struct_constructor`]: No longer lints in macros
 +  [#7097](https://github.com/rust-lang/rust-clippy/pull/7097)
 +* [`single_component_path_imports`]: No longer lints on macro re-exports
 +  [#7120](https://github.com/rust-lang/rust-clippy/pull/7120)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`redundant_pattern_matching`]: Add a note when applying this lint would
 +  change the drop order
 +  [#6568](https://github.com/rust-lang/rust-clippy/pull/6568)
 +* [`write_literal`], [`print_literal`]: Add auto-applicable suggestion
 +  [#6821](https://github.com/rust-lang/rust-clippy/pull/6821)
 +* [`manual_map`]: Fix suggestion for complex `if let ... else` chains
 +  [#6856](https://github.com/rust-lang/rust-clippy/pull/6856)
 +* [`inconsistent_struct_constructor`]: Make lint description and message clearer
 +  [#6892](https://github.com/rust-lang/rust-clippy/pull/6892)
 +* [`map_entry`]: Now suggests `or_insert`, `insert_with` or `match _.entry(_)`
 +  as appropriate [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
 +* [`manual_flatten`]: Suggest to insert `copied` if necessary
 +  [#6962](https://github.com/rust-lang/rust-clippy/pull/6962)
 +* [`redundant_slicing`]: Fix suggestion when a re-borrow might be required or
 +  when the value is from a macro call
 +  [#6975](https://github.com/rust-lang/rust-clippy/pull/6975)
 +* [`match_wildcard_for_single_variants`]: Fix suggestion for hidden variant
 +  [#6988](https://github.com/rust-lang/rust-clippy/pull/6988)
 +* [`clone_on_copy`]: Correct suggestion when the cloned value is a macro call
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`manual_map`]: Fix suggestion at the end of an if chain
 +  [#7004](https://github.com/rust-lang/rust-clippy/pull/7004)
 +* Fix needless parenthesis output in multiple lint suggestions
 +  [#7013](https://github.com/rust-lang/rust-clippy/pull/7013)
 +* [`needless_collect`]: Better explanation in the lint message
 +  [#7020](https://github.com/rust-lang/rust-clippy/pull/7020)
 +* [`useless_vec`]: Now considers mutability
 +  [#7036](https://github.com/rust-lang/rust-clippy/pull/7036)
 +* [`useless_format`]: Wrap the content in braces if necessary
 +  [#7092](https://github.com/rust-lang/rust-clippy/pull/7092)
 +* [`single_match`]: Don't suggest an equality check for types which don't
 +  implement `PartialEq`
 +  [#7093](https://github.com/rust-lang/rust-clippy/pull/7093)
 +* [`from_over_into`]: Mention type in help message
 +  [#7099](https://github.com/rust-lang/rust-clippy/pull/7099)
 +* [`manual_unwrap_or`]: Fix invalid code suggestion due to a macro call
 +  [#7136](https://github.com/rust-lang/rust-clippy/pull/7136)
 +
 +### ICE Fixes
 +
 +* [`macro_use_imports`]
 +  [#7022](https://github.com/rust-lang/rust-clippy/pull/7022)
 +* [`missing_panics_doc`]
 +  [#7034](https://github.com/rust-lang/rust-clippy/pull/7034)
 +* [`tabs_in_doc_comments`]
 +  [#7039](https://github.com/rust-lang/rust-clippy/pull/7039)
 +* [`missing_const_for_fn`]
 +  [#7128](https://github.com/rust-lang/rust-clippy/pull/7128)
 +
 +### Others
 +
 +* [Clippy's lint
 +  list](https://rust-lang.github.io/rust-clippy/master/index.html) now supports
 +  themes [#7030](https://github.com/rust-lang/rust-clippy/pull/7030)
 +* Lints that were uplifted to `rustc` now mention the new `rustc` name in the
 +  deprecation warning
 +  [#7056](https://github.com/rust-lang/rust-clippy/pull/7056)
 +
 +## Rust 1.52
 +
 +Released 2021-05-06
 +
 +[3e41797...6ed6f1e](https://github.com/rust-lang/rust-clippy/compare/3e41797...6ed6f1e)
 +
 +### New Lints
 +
 +* [`from_str_radix_10`]
 +  [#6717](https://github.com/rust-lang/rust-clippy/pull/6717)
 +* [`implicit_clone`]
 +  [#6730](https://github.com/rust-lang/rust-clippy/pull/6730)
 +* [`semicolon_if_nothing_returned`]
 +  [#6681](https://github.com/rust-lang/rust-clippy/pull/6681)
 +* [`manual_flatten`]
 +  [#6646](https://github.com/rust-lang/rust-clippy/pull/6646)
 +* [`inconsistent_struct_constructor`]
 +  [#6769](https://github.com/rust-lang/rust-clippy/pull/6769)
 +* [`iter_count`]
 +  [#6791](https://github.com/rust-lang/rust-clippy/pull/6791)
 +* [`default_numeric_fallback`]
 +  [#6662](https://github.com/rust-lang/rust-clippy/pull/6662)
 +* [`bytes_nth`]
 +  [#6695](https://github.com/rust-lang/rust-clippy/pull/6695)
 +* [`filter_map_identity`]
 +  [#6685](https://github.com/rust-lang/rust-clippy/pull/6685)
 +* [`manual_map`]
 +  [#6573](https://github.com/rust-lang/rust-clippy/pull/6573)
 +
 +### Moves and Deprecations
 +
 +* Moved [`upper_case_acronyms`] to `pedantic`
 +  [#6775](https://github.com/rust-lang/rust-clippy/pull/6775)
 +* Moved [`manual_map`] to `nursery`
 +  [#6796](https://github.com/rust-lang/rust-clippy/pull/6796)
 +* Moved [`unnecessary_wraps`] to `pedantic`
 +  [#6765](https://github.com/rust-lang/rust-clippy/pull/6765)
 +* Moved [`trivial_regex`] to `nursery`
 +  [#6696](https://github.com/rust-lang/rust-clippy/pull/6696)
 +* Moved [`naive_bytecount`] to `pedantic`
 +  [#6825](https://github.com/rust-lang/rust-clippy/pull/6825)
 +* Moved [`upper_case_acronyms`] to `style`
 +  [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
 +* Moved [`manual_map`] to `style`
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +
 +### Enhancements
 +
 +* [`disallowed_method`]: Now supports functions in addition to methods
 +  [#6674](https://github.com/rust-lang/rust-clippy/pull/6674)
 +* [`upper_case_acronyms`]: Added a new configuration `upper-case-acronyms-aggressive` to
 +  trigger the lint if there is more than one uppercase character next to each other
 +  [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
 +* [`collapsible_match`]: Now supports block comparison with different value names
 +  [#6754](https://github.com/rust-lang/rust-clippy/pull/6754)
 +* [`unnecessary_wraps`]: Will now suggest removing unnecessary wrapped return unit type, like `Option<()>`
 +  [#6665](https://github.com/rust-lang/rust-clippy/pull/6665)
 +* Improved value usage detection in closures
 +  [#6698](https://github.com/rust-lang/rust-clippy/pull/6698)
 +
 +### False Positive Fixes
 +
 +* [`use_self`]: No longer lints in macros
 +  [#6833](https://github.com/rust-lang/rust-clippy/pull/6833)
 +* [`use_self`]: Fixed multiple false positives for: generics, associated types and derive implementations
 +  [#6179](https://github.com/rust-lang/rust-clippy/pull/6179)
 +* [`missing_inline_in_public_items`]: No longer lints for procedural macros
 +  [#6814](https://github.com/rust-lang/rust-clippy/pull/6814)
 +* [`inherent_to_string`]: No longer lints on functions with function generics
 +  [#6771](https://github.com/rust-lang/rust-clippy/pull/6771)
 +* [`doc_markdown`]: Add `OpenDNS` to the default configuration as an allowed identifier
 +  [#6783](https://github.com/rust-lang/rust-clippy/pull/6783)
 +* [`missing_panics_doc`]: No longer lints on [`unreachable!`](https://doc.rust-lang.org/std/macro.unreachable.html)
 +  [#6700](https://github.com/rust-lang/rust-clippy/pull/6700)
 +* [`collapsible_if`]: No longer lints on if statements with attributes
 +  [#6701](https://github.com/rust-lang/rust-clippy/pull/6701)
 +* [`match_same_arms`]: Only considers empty blocks as equal if the tokens contained are the same
 +  [#6843](https://github.com/rust-lang/rust-clippy/pull/6843)
 +* [`redundant_closure`]: Now ignores macros
 +  [#6871](https://github.com/rust-lang/rust-clippy/pull/6871)
 +* [`manual_map`]: Fixed false positives when control flow statements like `return`, `break` etc. are used
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +* [`vec_init_then_push`]: Fixed false positives for loops and if statements
 +  [#6697](https://github.com/rust-lang/rust-clippy/pull/6697)
 +* [`len_without_is_empty`]: Will now consider multiple impl blocks and `#[allow]` on
 +  the `len` method as well as the type definition.
 +  [#6853](https://github.com/rust-lang/rust-clippy/pull/6853)
 +* [`let_underscore_drop`]: Only lints on types which implement `Drop`
 +  [#6682](https://github.com/rust-lang/rust-clippy/pull/6682)
 +* [`unit_arg`]: No longer lints on unit arguments when they come from a path expression.
 +  [#6601](https://github.com/rust-lang/rust-clippy/pull/6601)
 +* [`cargo_common_metadata`]: No longer lints if
 +  [`publish = false`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field)
 +  is defined in the manifest
 +  [#6650](https://github.com/rust-lang/rust-clippy/pull/6650)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`collapsible_match`]: Fixed lint message capitalization
 +  [#6766](https://github.com/rust-lang/rust-clippy/pull/6766)
 +* [`or_fun_call`]: Improved suggestions for `or_insert(vec![])`
 +  [#6790](https://github.com/rust-lang/rust-clippy/pull/6790)
 +* [`manual_map`]: No longer expands macros in the suggestions
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +* Aligned Clippy's lint messages with the rustc dev guide
 +  [#6787](https://github.com/rust-lang/rust-clippy/pull/6787)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#6866](https://github.com/rust-lang/rust-clippy/pull/6866)
 +
 +### Documentation Improvements
 +
 +* [`useless_format`]: Improved the documentation example
 +  [#6854](https://github.com/rust-lang/rust-clippy/pull/6854)
 +* Clippy's [`README.md`]: Includes a new subsection on running Clippy as a rustc wrapper
 +  [#6782](https://github.com/rust-lang/rust-clippy/pull/6782)
 +
 +### Others
 +* Running `cargo clippy` after `cargo check` now works as expected
 +  (`cargo clippy` and `cargo check` no longer shares the same build cache)
 +  [#6687](https://github.com/rust-lang/rust-clippy/pull/6687)
 +* Cargo now re-runs Clippy if arguments after `--` provided to `cargo clippy` are changed.
 +  [#6834](https://github.com/rust-lang/rust-clippy/pull/6834)
 +* Extracted Clippy's `utils` module into the new `clippy_utils` crate
 +  [#6756](https://github.com/rust-lang/rust-clippy/pull/6756)
 +* Clippy lintcheck tool improvements
 +  [#6800](https://github.com/rust-lang/rust-clippy/pull/6800)
 +  [#6735](https://github.com/rust-lang/rust-clippy/pull/6735)
 +  [#6764](https://github.com/rust-lang/rust-clippy/pull/6764)
 +  [#6708](https://github.com/rust-lang/rust-clippy/pull/6708)
 +  [#6780](https://github.com/rust-lang/rust-clippy/pull/6780)
 +  [#6686](https://github.com/rust-lang/rust-clippy/pull/6686)
 +
 +## Rust 1.51
 +
 +Released 2021-03-25
 +
 +[4911ab1...3e41797](https://github.com/rust-lang/rust-clippy/compare/4911ab1...3e41797)
 +
 +### New Lints
 +
 +* [`upper_case_acronyms`]
 +  [#6475](https://github.com/rust-lang/rust-clippy/pull/6475)
 +* [`from_over_into`] [#6476](https://github.com/rust-lang/rust-clippy/pull/6476)
 +* [`case_sensitive_file_extension_comparisons`]
 +  [#6500](https://github.com/rust-lang/rust-clippy/pull/6500)
 +* [`needless_question_mark`]
 +  [#6507](https://github.com/rust-lang/rust-clippy/pull/6507)
 +* [`missing_panics_doc`]
 +  [#6523](https://github.com/rust-lang/rust-clippy/pull/6523)
 +* [`redundant_slicing`]
 +  [#6528](https://github.com/rust-lang/rust-clippy/pull/6528)
 +* [`vec_init_then_push`]
 +  [#6538](https://github.com/rust-lang/rust-clippy/pull/6538)
 +* [`ptr_as_ptr`] [#6542](https://github.com/rust-lang/rust-clippy/pull/6542)
 +* [`collapsible_else_if`] (split out from `collapsible_if`)
 +  [#6544](https://github.com/rust-lang/rust-clippy/pull/6544)
 +* [`inspect_for_each`] [#6577](https://github.com/rust-lang/rust-clippy/pull/6577)
 +* [`manual_filter_map`]
 +  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
 +* [`exhaustive_enums`]
 +  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
 +* [`exhaustive_structs`]
 +  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
 +
 +### Moves and Deprecations
 +
 +* Replace [`find_map`] with [`manual_find_map`]
 +  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
 +* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint
 +  [#6653](https://github.com/rust-lang/rust-clippy/pull/6653)
 +
 +### Enhancements
 +
 +* [`ptr_arg`] Now also suggests to use `&Path` instead of `&PathBuf`
 +  [#6506](https://github.com/rust-lang/rust-clippy/pull/6506)
 +* [`cast_ptr_alignment`] Also lint when the `pointer::cast` method is used
 +  [#6557](https://github.com/rust-lang/rust-clippy/pull/6557)
 +* [`collapsible_match`] Now also deals with `&` and `*` operators in the `match`
 +  scrutinee [#6619](https://github.com/rust-lang/rust-clippy/pull/6619)
 +
 +### False Positive Fixes
 +
 +* [`similar_names`] Ignore underscore prefixed names
 +  [#6403](https://github.com/rust-lang/rust-clippy/pull/6403)
 +* [`print_literal`] and [`write_literal`] No longer lint numeric literals
 +  [#6408](https://github.com/rust-lang/rust-clippy/pull/6408)
 +* [`large_enum_variant`] No longer lints in external macros
 +  [#6485](https://github.com/rust-lang/rust-clippy/pull/6485)
 +* [`empty_enum`] Only lint if `never_type` feature is enabled
 +  [#6513](https://github.com/rust-lang/rust-clippy/pull/6513)
 +* [`field_reassign_with_default`] No longer lints in macros
 +  [#6553](https://github.com/rust-lang/rust-clippy/pull/6553)
 +* [`size_of_in_element_count`] No longer lints when dividing by element size
 +  [#6578](https://github.com/rust-lang/rust-clippy/pull/6578)
 +* [`needless_return`] No longer lints in macros
 +  [#6586](https://github.com/rust-lang/rust-clippy/pull/6586)
 +* [`match_overlapping_arm`] No longer lint when first arm is completely included
 +  in second arm [#6603](https://github.com/rust-lang/rust-clippy/pull/6603)
 +* [`doc_markdown`] Add `WebGL` to the default configuration as an allowed
 +  identifier [#6605](https://github.com/rust-lang/rust-clippy/pull/6605)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`field_reassign_with_default`] Don't expand macro in lint suggestion
 +  [#6531](https://github.com/rust-lang/rust-clippy/pull/6531)
 +* [`match_like_matches_macro`] Strip references in suggestion
 +  [#6532](https://github.com/rust-lang/rust-clippy/pull/6532)
 +* [`single_match`] Suggest `if` over `if let` when possible
 +  [#6574](https://github.com/rust-lang/rust-clippy/pull/6574)
 +* [`ref_in_deref`] Use parentheses correctly in suggestion
 +  [#6609](https://github.com/rust-lang/rust-clippy/pull/6609)
 +* [`stable_sort_primitive`] Clarify error message
 +  [#6611](https://github.com/rust-lang/rust-clippy/pull/6611)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#6582](https://github.com/rust-lang/rust-clippy/pull/6582)
 +
 +### Documentation Improvements
 +
 +* Improve search performance on the Clippy website and make it possible to
 +  directly search for lints on the GitHub issue tracker
 +  [#6483](https://github.com/rust-lang/rust-clippy/pull/6483)
 +* Clean up `README.md` by removing outdated paragraph
 +  [#6488](https://github.com/rust-lang/rust-clippy/pull/6488)
 +* [`await_holding_refcell_ref`] and [`await_holding_lock`]
 +  [#6585](https://github.com/rust-lang/rust-clippy/pull/6585)
 +* [`as_conversions`] [#6608](https://github.com/rust-lang/rust-clippy/pull/6608)
 +
 +### Others
 +
 +* Clippy now has a [Roadmap] for 2021. If you like to get involved in a bigger
 +  project, take a look at the [Roadmap project page]. All issues listed there
 +  are actively mentored
 +  [#6462](https://github.com/rust-lang/rust-clippy/pull/6462)
 +* The Clippy version number now corresponds to the Rust version number
 +  [#6526](https://github.com/rust-lang/rust-clippy/pull/6526)
 +* Fix oversight which caused Clippy to lint deps in some environments, where
 +  `CLIPPY_TESTS=true` was set somewhere
 +  [#6575](https://github.com/rust-lang/rust-clippy/pull/6575)
 +* Add `cargo dev-lintcheck` tool to the Clippy Dev Tool
 +  [#6469](https://github.com/rust-lang/rust-clippy/pull/6469)
 +
 +[Roadmap]: https://github.com/rust-lang/rust-clippy/blob/master/doc/roadmap-2021.md
 +[Roadmap project page]: https://github.com/rust-lang/rust-clippy/projects/3
 +
 +## Rust 1.50
 +
 +Released 2021-02-11
 +
 +[b20d4c1...4bd77a1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4bd77a1)
 +
 +### New Lints
 +
 +* [`suspicious_operation_groupings`] [#6086](https://github.com/rust-lang/rust-clippy/pull/6086)
 +* [`size_of_in_element_count`] [#6394](https://github.com/rust-lang/rust-clippy/pull/6394)
 +* [`unnecessary_wraps`] [#6070](https://github.com/rust-lang/rust-clippy/pull/6070)
 +* [`let_underscore_drop`] [#6305](https://github.com/rust-lang/rust-clippy/pull/6305)
 +* [`collapsible_match`] [#6402](https://github.com/rust-lang/rust-clippy/pull/6402)
 +* [`redundant_else`] [#6330](https://github.com/rust-lang/rust-clippy/pull/6330)
 +* [`zero_sized_map_values`] [#6218](https://github.com/rust-lang/rust-clippy/pull/6218)
 +* [`print_stderr`] [#6367](https://github.com/rust-lang/rust-clippy/pull/6367)
 +* [`string_from_utf8_as_bytes`] [#6134](https://github.com/rust-lang/rust-clippy/pull/6134)
 +
 +### Moves and Deprecations
 +
 +* Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated
 +  as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333)
 +* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panics`
 +  [#6351](https://github.com/rust-lang/rust-clippy/pull/6351)
 +* Move [`map_err_ignore`] to `restriction`
 +  [#6416](https://github.com/rust-lang/rust-clippy/pull/6416)
 +* Move [`await_holding_refcell_ref`] to `pedantic`
 +  [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
 +* Move [`await_holding_lock`] to `pedantic`
 +  [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
 +
 +### Enhancements
 +
 +* Add the `unreadable-literal-lint-fractions` configuration to disable
 +  the `unreadable_literal` lint for fractions
 +  [#6421](https://github.com/rust-lang/rust-clippy/pull/6421)
 +* [`clone_on_copy`]: Now shows the type in the lint message
 +  [#6443](https://github.com/rust-lang/rust-clippy/pull/6443)
 +* [`redundant_pattern_matching`]: Now also lints on `std::task::Poll`
 +  [#6339](https://github.com/rust-lang/rust-clippy/pull/6339)
 +* [`redundant_pattern_matching`]: Additionally also lints on `std::net::IpAddr`
 +  [#6377](https://github.com/rust-lang/rust-clippy/pull/6377)
 +* [`search_is_some`]: Now suggests `contains` instead of `find(foo).is_some()`
 +  [#6119](https://github.com/rust-lang/rust-clippy/pull/6119)
 +* [`clone_double_ref`]: Now prints the reference type in the lint message
 +  [#6442](https://github.com/rust-lang/rust-clippy/pull/6442)
 +* [`modulo_one`]: Now also lints on -1.
 +  [#6360](https://github.com/rust-lang/rust-clippy/pull/6360)
 +* [`empty_loop`]: Now lints no_std crates, too
 +  [#6205](https://github.com/rust-lang/rust-clippy/pull/6205)
 +* [`or_fun_call`]: Now also lints when indexing `HashMap` or `BTreeMap`
 +  [#6267](https://github.com/rust-lang/rust-clippy/pull/6267)
 +* [`wrong_self_convention`]: Now also lints in trait definitions
 +  [#6316](https://github.com/rust-lang/rust-clippy/pull/6316)
 +* [`needless_borrow`]: Print the type in the lint message
 +  [#6449](https://github.com/rust-lang/rust-clippy/pull/6449)
 +
 +[msrv_readme]: https://github.com/rust-lang/rust-clippy#specifying-the-minimum-supported-rust-version
 +
 +### False Positive Fixes
 +
 +* [`manual_range_contains`]: No longer lints in `const fn`
 +  [#6382](https://github.com/rust-lang/rust-clippy/pull/6382)
 +* [`unnecessary_lazy_evaluations`]: No longer lints if closure argument is used
 +  [#6370](https://github.com/rust-lang/rust-clippy/pull/6370)
 +* [`match_single_binding`]: Now ignores cases with `#[cfg()]` macros
 +  [#6435](https://github.com/rust-lang/rust-clippy/pull/6435)
 +* [`match_like_matches_macro`]: No longer lints on arms with attributes
 +  [#6290](https://github.com/rust-lang/rust-clippy/pull/6290)
 +* [`map_clone`]: No longer lints with deref and clone
 +  [#6269](https://github.com/rust-lang/rust-clippy/pull/6269)
 +* [`map_clone`]: No longer lints in the case of &mut
 +  [#6301](https://github.com/rust-lang/rust-clippy/pull/6301)
 +* [`needless_update`]: Now ignores `non_exhaustive` structs
 +  [#6464](https://github.com/rust-lang/rust-clippy/pull/6464)
 +* [`needless_collect`]: No longer lints when a collect is needed multiple times
 +  [#6313](https://github.com/rust-lang/rust-clippy/pull/6313)
 +* [`unnecessary_cast`] No longer lints cfg-dependent types
 +  [#6369](https://github.com/rust-lang/rust-clippy/pull/6369)
 +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]:
 +  Both now ignore enums with frozen variants
 +  [#6110](https://github.com/rust-lang/rust-clippy/pull/6110)
 +* [`field_reassign_with_default`] No longer lint for private fields
 +  [#6537](https://github.com/rust-lang/rust-clippy/pull/6537)
 +
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`vec_box`]: Provide correct type scope suggestion
 +  [#6271](https://github.com/rust-lang/rust-clippy/pull/6271)
 +* [`manual_range_contains`]: Give correct suggestion when using floats
 +  [#6320](https://github.com/rust-lang/rust-clippy/pull/6320)
 +* [`unnecessary_lazy_evaluations`]: Don't always mark suggestion as MachineApplicable
 +  [#6272](https://github.com/rust-lang/rust-clippy/pull/6272)
 +* [`manual_async_fn`]: Improve suggestion formatting
 +  [#6294](https://github.com/rust-lang/rust-clippy/pull/6294)
 +* [`unnecessary_cast`]: Fix incorrectly formatted float literal suggestion
 +  [#6362](https://github.com/rust-lang/rust-clippy/pull/6362)
 +
 +### ICE Fixes
 +
 +* Fix a crash in [`from_iter_instead_of_collect`]
 +  [#6304](https://github.com/rust-lang/rust-clippy/pull/6304)
 +* Fix a silent crash when parsing doc comments in [`needless_doctest_main`]
 +  [#6458](https://github.com/rust-lang/rust-clippy/pull/6458)
 +
 +### Documentation Improvements
 +
 +* The lint website search has been improved ([#6477](https://github.com/rust-lang/rust-clippy/pull/6477)):
 +  * Searching for lints with dashes and spaces is possible now. For example
 +    `missing-errors-doc` and `missing errors doc` are now valid aliases for lint names
 +  * Improved fuzzy search in lint descriptions
 +* Various README improvements
 +  [#6287](https://github.com/rust-lang/rust-clippy/pull/6287)
 +* Add known problems to [`comparison_chain`] documentation
 +  [#6390](https://github.com/rust-lang/rust-clippy/pull/6390)
 +* Fix example used in [`cargo_common_metadata`]
 +  [#6293](https://github.com/rust-lang/rust-clippy/pull/6293)
 +* Improve [`map_clone`] documentation
 +  [#6340](https://github.com/rust-lang/rust-clippy/pull/6340)
 +
 +### Others
 +
 +* You can now tell Clippy about the MSRV your project supports. Please refer to
 +  the specific README section to learn more about MSRV support [here][msrv_readme]
 +  [#6201](https://github.com/rust-lang/rust-clippy/pull/6201)
 +* Add `--no-deps` option to avoid running on path dependencies in workspaces
 +  [#6188](https://github.com/rust-lang/rust-clippy/pull/6188)
 +
 +## Rust 1.49
 +
 +Released 2020-12-31
 +
 +[e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1)
 +
 +### New Lints
 +
 +* [`field_reassign_with_default`] [#5911](https://github.com/rust-lang/rust-clippy/pull/5911)
 +* [`await_holding_refcell_ref`] [#6029](https://github.com/rust-lang/rust-clippy/pull/6029)
 +* [`disallowed_method`] [#6081](https://github.com/rust-lang/rust-clippy/pull/6081)
 +* [`inline_asm_x86_att_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 +* [`inline_asm_x86_intel_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 +* [`from_iter_instead_of_collect`] [#6101](https://github.com/rust-lang/rust-clippy/pull/6101)
 +* [`mut_mutex_lock`] [#6103](https://github.com/rust-lang/rust-clippy/pull/6103)
 +* [`single_element_loop`] [#6109](https://github.com/rust-lang/rust-clippy/pull/6109)
 +* [`manual_unwrap_or`] [#6123](https://github.com/rust-lang/rust-clippy/pull/6123)
 +* [`large_types_passed_by_value`] [#6135](https://github.com/rust-lang/rust-clippy/pull/6135)
 +* [`result_unit_err`] [#6157](https://github.com/rust-lang/rust-clippy/pull/6157)
 +* [`ref_option_ref`] [#6165](https://github.com/rust-lang/rust-clippy/pull/6165)
 +* [`manual_range_contains`] [#6177](https://github.com/rust-lang/rust-clippy/pull/6177)
 +* [`unusual_byte_groupings`] [#6183](https://github.com/rust-lang/rust-clippy/pull/6183)
 +* [`comparison_to_empty`] [#6226](https://github.com/rust-lang/rust-clippy/pull/6226)
 +* [`map_collect_result_unit`] [#6227](https://github.com/rust-lang/rust-clippy/pull/6227)
 +* [`manual_ok_or`] [#6233](https://github.com/rust-lang/rust-clippy/pull/6233)
 +
 +### Moves and Deprecations
 +
 +* Rename `single_char_push_str` to [`single_char_add_str`]
 +  [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 +* Rename `zero_width_space` to [`invisible_characters`]
 +  [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
 +* Deprecate `drop_bounds` (uplifted)
 +  [#6111](https://github.com/rust-lang/rust-clippy/pull/6111)
 +* Move [`string_lit_as_bytes`] to `nursery`
 +  [#6117](https://github.com/rust-lang/rust-clippy/pull/6117)
 +* Move [`rc_buffer`] to `restriction`
 +  [#6128](https://github.com/rust-lang/rust-clippy/pull/6128)
 +
 +### Enhancements
 +
 +* [`manual_memcpy`]: Also lint when there are loop counters (and produce a
 +  reliable suggestion)
 +  [#5727](https://github.com/rust-lang/rust-clippy/pull/5727)
 +* [`single_char_add_str`]: Also lint on `String::insert_str`
 +  [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 +* [`invisible_characters`]: Also lint the characters `\u{AD}` and `\u{2060}`
 +  [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
 +* [`eq_op`]: Also lint on the `assert_*!` macro family
 +  [#6167](https://github.com/rust-lang/rust-clippy/pull/6167)
 +* [`items_after_statements`]: Also lint in local macro expansions
 +  [#6176](https://github.com/rust-lang/rust-clippy/pull/6176)
 +* [`unnecessary_cast`]: Also lint casts on integer and float literals
 +  [#6187](https://github.com/rust-lang/rust-clippy/pull/6187)
 +* [`manual_unwrap_or`]: Also lint `Result::unwrap_or`
 +  [#6190](https://github.com/rust-lang/rust-clippy/pull/6190)
 +* [`match_like_matches_macro`]: Also lint when `match` has more than two arms
 +  [#6216](https://github.com/rust-lang/rust-clippy/pull/6216)
 +* [`integer_arithmetic`]: Better handle `/` an `%` operators
 +  [#6229](https://github.com/rust-lang/rust-clippy/pull/6229)
 +
 +### False Positive Fixes
 +
 +* [`needless_lifetimes`]: Bail out if the function has a `where` clause with the
 +  lifetime [#5978](https://github.com/rust-lang/rust-clippy/pull/5978)
 +* [`explicit_counter_loop`]: No longer lints, when loop counter is used after it
 +  is incremented [#6076](https://github.com/rust-lang/rust-clippy/pull/6076)
 +* [`or_fun_call`]: Revert changes addressing the handling of `const fn`
 +  [#6077](https://github.com/rust-lang/rust-clippy/pull/6077)
 +* [`needless_range_loop`]: No longer lints, when the iterable is used in the
 +  range [#6102](https://github.com/rust-lang/rust-clippy/pull/6102)
 +* [`inconsistent_digit_grouping`]: Fix bug when using floating point exponent
 +  [#6104](https://github.com/rust-lang/rust-clippy/pull/6104)
 +* [`mistyped_literal_suffixes`]: No longer lints on the fractional part of a
 +  float (e.g. `713.32_64`)
 +  [#6114](https://github.com/rust-lang/rust-clippy/pull/6114)
 +* [`invalid_regex`]: No longer lint on unicode characters within `bytes::Regex`
 +  [#6132](https://github.com/rust-lang/rust-clippy/pull/6132)
 +* [`boxed_local`]: No longer lints on `extern fn` arguments
 +  [#6133](https://github.com/rust-lang/rust-clippy/pull/6133)
 +* [`needless_lifetimes`]: Fix regression, where lifetime is used in `where`
 +  clause [#6198](https://github.com/rust-lang/rust-clippy/pull/6198)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`unnecessary_sort_by`]: Avoid dereferencing the suggested closure parameter
 +  [#6078](https://github.com/rust-lang/rust-clippy/pull/6078)
 +* [`needless_arbitrary_self_type`]: Correctly handle expanded code
 +  [#6093](https://github.com/rust-lang/rust-clippy/pull/6093)
 +* [`useless_format`]: Preserve raw strings in suggestion
 +  [#6151](https://github.com/rust-lang/rust-clippy/pull/6151)
 +* [`empty_loop`]: Suggest alternatives
 +  [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
 +* [`borrowed_box`]: Correctly add parentheses in suggestion
 +  [#6200](https://github.com/rust-lang/rust-clippy/pull/6200)
 +* [`unused_unit`]: Improve suggestion formatting
 +  [#6247](https://github.com/rust-lang/rust-clippy/pull/6247)
 +
 +### Documentation Improvements
 +
 +* Some doc improvements:
 +    * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090)
 +    * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
 +* [`doc_markdown`]: Document problematic link text style
 +  [#6107](https://github.com/rust-lang/rust-clippy/pull/6107)
 +
 +## Rust 1.48
 +
 +Released 2020-11-19
 +
 +[09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88)
 +
 +### New lints
 +
 +* [`self_assignment`] [#5894](https://github.com/rust-lang/rust-clippy/pull/5894)
 +* [`unnecessary_lazy_evaluations`] [#5720](https://github.com/rust-lang/rust-clippy/pull/5720)
 +* [`manual_strip`] [#6038](https://github.com/rust-lang/rust-clippy/pull/6038)
 +* [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998)
 +* [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044)
 +* [`to_string_in_display`] [#5831](https://github.com/rust-lang/rust-clippy/pull/5831)
 +* `single_char_push_str` [#5881](https://github.com/rust-lang/rust-clippy/pull/5881)
 +
 +### Moves and Deprecations
 +
 +* Downgrade [`verbose_bit_mask`] to pedantic
 +  [#6036](https://github.com/rust-lang/rust-clippy/pull/6036)
 +
 +### Enhancements
 +
 +* Extend [`precedence`] to handle chains of methods combined with unary negation
 +  [#5928](https://github.com/rust-lang/rust-clippy/pull/5928)
 +* [`useless_vec`]: add a configuration value for the maximum allowed size on the stack
 +  [#5907](https://github.com/rust-lang/rust-clippy/pull/5907)
 +* [`suspicious_arithmetic_impl`]: extend to implementations of `BitAnd`, `BitOr`, `BitXor`, `Rem`, `Shl`, and `Shr`
 +  [#5884](https://github.com/rust-lang/rust-clippy/pull/5884)
- * [`invalid_atomic_ordering`] [#4999](https://github.com/rust-lang/rust-clippy/pull/4999)
++* `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`], [`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`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
++* `invalid_atomic_ordering` [#4999](https://github.com/rust-lang/rust-clippy/pull/4999)
 +* [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067)
 +
 +### Moves and Deprecations
 +
 +* Move [`transmute_float_to_int`] from nursery to complexity group
 +  [#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
 +* Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
 +* Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
 +
 +### Enhancements
 +
 +* Lint vectored IO in [`unused_io_amount`] [#5027](https://github.com/rust-lang/rust-clippy/pull/5027)
 +* Make [`vec_box`] configurable by adding a size threshold [#5081](https://github.com/rust-lang/rust-clippy/pull/5081)
 +* Also lint constants in [`cmp_nan`] [#4910](https://github.com/rust-lang/rust-clippy/pull/4910)
 +* Fix false negative in [`expect_fun_call`] [#4915](https://github.com/rust-lang/rust-clippy/pull/4915)
 +* Fix false negative in [`redundant_clone`] [#5017](https://github.com/rust-lang/rust-clippy/pull/5017)
 +
 +### False Positive Fixes
 +
 +* [`map_clone`] [#4937](https://github.com/rust-lang/rust-clippy/pull/4937)
 +* [`replace_consts`] [#4977](https://github.com/rust-lang/rust-clippy/pull/4977)
 +* [`let_and_return`] [#5008](https://github.com/rust-lang/rust-clippy/pull/5008)
 +* [`eq_op`] [#5079](https://github.com/rust-lang/rust-clippy/pull/5079)
 +* [`possible_missing_comma`] [#5083](https://github.com/rust-lang/rust-clippy/pull/5083)
 +* [`debug_assert_with_mut_call`] [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Don't trigger [`let_underscore_must_use`] in external macros
 +  [#5082](https://github.com/rust-lang/rust-clippy/pull/5082)
 +* Don't trigger [`empty_loop`] in `no_std` crates [#5086](https://github.com/rust-lang/rust-clippy/pull/5086)
 +
 +### Suggestion Improvements
 +
 +* `option_map_unwrap_or` [#4634](https://github.com/rust-lang/rust-clippy/pull/4634)
 +* [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
 +* [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
 +* [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
 +* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
 +* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
 +* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
 +* [`if_let_some_result`] [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
 +
 +### ICE fixes
 +
 +* [`unsound_collection_transmute`] [#4975](https://github.com/rust-lang/rust-clippy/pull/4975)
 +
 +### Documentation
 +
 +* Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`]
 +
 +
 +## Rust 1.41
 +
 +Released 2020-01-30
 +
 +[c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7)
 +
 +* New Lints:
 +  * [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697)
 +  * [`to_digit_is_some`] [#4801](https://github.com/rust-lang/rust-clippy/pull/4801)
 +  * [`tabs_in_doc_comments`] [#4806](https://github.com/rust-lang/rust-clippy/pull/4806)
 +  * [`large_stack_arrays`] [#4807](https://github.com/rust-lang/rust-clippy/pull/4807)
 +  * [`same_functions_in_if_condition`] [#4814](https://github.com/rust-lang/rust-clippy/pull/4814)
 +  * [`zst_offset`] [#4816](https://github.com/rust-lang/rust-clippy/pull/4816)
 +  * [`as_conversions`] [#4821](https://github.com/rust-lang/rust-clippy/pull/4821)
 +  * [`missing_errors_doc`] [#4884](https://github.com/rust-lang/rust-clippy/pull/4884)
 +  * [`transmute_float_to_int`] [#4889](https://github.com/rust-lang/rust-clippy/pull/4889)
 +* Remove plugin interface, see
 +  [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
 +  details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
 +* Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
 +* Deprecate `into_iter_on_array` [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
 +* Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
 +  [#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
 +* Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
 +* Fix false positive in `while_immutable_condition` [#4730](https://github.com/rust-lang/rust-clippy/pull/4730)
 +* Fix false positive in `explicit_counter_loop` [#4803](https://github.com/rust-lang/rust-clippy/pull/4803)
 +* Fix false positive in `must_use_candidate` [#4794](https://github.com/rust-lang/rust-clippy/pull/4794)
 +* Fix false positive in `print_with_newline` and `write_with_newline`
 +  [#4769](https://github.com/rust-lang/rust-clippy/pull/4769)
 +* Fix false positive in `derive_hash_xor_eq` [#4766](https://github.com/rust-lang/rust-clippy/pull/4766)
 +* Fix false positive in `missing_inline_in_public_items` [#4870](https://github.com/rust-lang/rust-clippy/pull/4870)
 +* Fix false positive in `string_add` [#4880](https://github.com/rust-lang/rust-clippy/pull/4880)
 +* Fix false positive in `float_arithmetic` [#4851](https://github.com/rust-lang/rust-clippy/pull/4851)
 +* Fix false positive in `cast_sign_loss` [#4883](https://github.com/rust-lang/rust-clippy/pull/4883)
 +* Fix false positive in `manual_swap` [#4877](https://github.com/rust-lang/rust-clippy/pull/4877)
 +* Fix ICEs occurring while checking some block expressions [#4772](https://github.com/rust-lang/rust-clippy/pull/4772)
 +* Fix ICE in `use_self` [#4776](https://github.com/rust-lang/rust-clippy/pull/4776)
 +* Fix ICEs related to `const_generics` [#4780](https://github.com/rust-lang/rust-clippy/pull/4780)
 +* Display help when running `clippy-driver` without arguments, instead of ICEing
 +  [#4810](https://github.com/rust-lang/rust-clippy/pull/4810)
 +* Clippy has its own ICE message now [#4588](https://github.com/rust-lang/rust-clippy/pull/4588)
 +* Show deprecated lints in the documentation again [#4757](https://github.com/rust-lang/rust-clippy/pull/4757)
 +* Improve Documentation by adding positive examples to some lints
 +  [#4832](https://github.com/rust-lang/rust-clippy/pull/4832)
 +
 +## Rust 1.40
 +
 +Released 2019-12-19
 +
 +[4e7e71b...c8e3cfb](https://github.com/rust-lang/rust-clippy/compare/4e7e71b...c8e3cfb)
 +
 +* New Lints:
 +  * [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537)
 +  * [`needless_doctest_main`] [#4603](https://github.com/rust-lang/rust-clippy/pull/4603)
 +  * [`suspicious_unary_op_formatting`] [#4615](https://github.com/rust-lang/rust-clippy/pull/4615)
 +  * [`debug_assert_with_mut_call`] [#4680](https://github.com/rust-lang/rust-clippy/pull/4680)
 +  * [`unused_self`] [#4619](https://github.com/rust-lang/rust-clippy/pull/4619)
 +  * [`inefficient_to_string`] [#4683](https://github.com/rust-lang/rust-clippy/pull/4683)
 +  * [`must_use_unit`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`must_use_candidate`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`double_must_use`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`comparison_chain`] [#4569](https://github.com/rust-lang/rust-clippy/pull/4569)
 +  * [`unsound_collection_transmute`] [#4592](https://github.com/rust-lang/rust-clippy/pull/4592)
 +  * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `option_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `result_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +* Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736)
 +* Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613)
 +* Expand `integer_arithmetic` to also detect mutating arithmetic like `+=` [#4585](https://github.com/rust-lang/rust-clippy/pull/4585)
 +* Fix false positive in `nonminimal_bool` [#4568](https://github.com/rust-lang/rust-clippy/pull/4568)
 +* Fix false positive in `missing_safety_doc` [#4611](https://github.com/rust-lang/rust-clippy/pull/4611)
 +* Fix false positive in `cast_sign_loss` [#4614](https://github.com/rust-lang/rust-clippy/pull/4614)
 +* Fix false positive in `redundant_clone` [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Fix false positive in `try_err` [#4721](https://github.com/rust-lang/rust-clippy/pull/4721)
 +* Fix false positive in `toplevel_ref_arg` [#4570](https://github.com/rust-lang/rust-clippy/pull/4570)
 +* Fix false positive in `multiple_inherent_impl` [#4593](https://github.com/rust-lang/rust-clippy/pull/4593)
 +* Improve more suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4575](https://github.com/rust-lang/rust-clippy/pull/4575)
 +* Improve suggestion for `zero_ptr` [#4599](https://github.com/rust-lang/rust-clippy/pull/4599)
 +* Improve suggestion for `explicit_counter_loop` [#4691](https://github.com/rust-lang/rust-clippy/pull/4691)
 +* Improve suggestion for `mul_add` [#4602](https://github.com/rust-lang/rust-clippy/pull/4602)
 +* Improve suggestion for `assertions_on_constants` [#4635](https://github.com/rust-lang/rust-clippy/pull/4635)
 +* Fix ICE in `use_self` [#4671](https://github.com/rust-lang/rust-clippy/pull/4671)
 +* Fix ICE when encountering const casts [#4590](https://github.com/rust-lang/rust-clippy/pull/4590)
 +
 +## Rust 1.39
 +
 +Released 2019-11-07
 +
 +[3aea860...4e7e71b](https://github.com/rust-lang/rust-clippy/compare/3aea860...4e7e71b)
 +
 +* New Lints:
 +  * [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479)
 +  * [`flat_map_identity`] [#4231](https://github.com/rust-lang/rust-clippy/pull/4231)
 +  * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535)
 +  * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511)
 +  * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394)
 +  * `option_and_then_some` [#4386](https://github.com/rust-lang/rust-clippy/pull/4386)
 +  * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498)
 +* Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348)
 +* Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403)
 +* Move `cast_lossless` to pedantic group [#4539](https://github.com/rust-lang/rust-clippy/pull/4539)
 +* `temporary_cstring_as_ptr` now catches more cases [#4425](https://github.com/rust-lang/rust-clippy/pull/4425)
 +* `use_self` now works in constructors, too [#4525](https://github.com/rust-lang/rust-clippy/pull/4525)
 +* `cargo_common_metadata` now checks for license files [#4518](https://github.com/rust-lang/rust-clippy/pull/4518)
 +* `cognitive_complexity` now includes the measured complexity in the warning message [#4469](https://github.com/rust-lang/rust-clippy/pull/4469)
 +* Fix false positives in `block_in_if_*` lints [#4458](https://github.com/rust-lang/rust-clippy/pull/4458)
 +* Fix false positive in `cast_lossless` [#4473](https://github.com/rust-lang/rust-clippy/pull/4473)
 +* Fix false positive in `clone_on_copy` [#4411](https://github.com/rust-lang/rust-clippy/pull/4411)
 +* Fix false positive in `deref_addrof` [#4487](https://github.com/rust-lang/rust-clippy/pull/4487)
 +* Fix false positive in `too_many_lines` [#4490](https://github.com/rust-lang/rust-clippy/pull/4490)
 +* Fix false positive in `new_ret_no_self` [#4365](https://github.com/rust-lang/rust-clippy/pull/4365)
 +* Fix false positive in `manual_swap` [#4478](https://github.com/rust-lang/rust-clippy/pull/4478)
 +* Fix false positive in `missing_const_for_fn` [#4450](https://github.com/rust-lang/rust-clippy/pull/4450)
 +* Fix false positive in `extra_unused_lifetimes` [#4477](https://github.com/rust-lang/rust-clippy/pull/4477)
 +* Fix false positive in `inherent_to_string` [#4460](https://github.com/rust-lang/rust-clippy/pull/4460)
 +* Fix false positive in `map_entry` [#4495](https://github.com/rust-lang/rust-clippy/pull/4495)
 +* Fix false positive in `unused_unit` [#4445](https://github.com/rust-lang/rust-clippy/pull/4445)
 +* Fix false positive in `redundant_pattern` [#4489](https://github.com/rust-lang/rust-clippy/pull/4489)
 +* Fix false positive in `wrong_self_convention` [#4369](https://github.com/rust-lang/rust-clippy/pull/4369)
 +* Improve various suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4558](https://github.com/rust-lang/rust-clippy/pull/4558)
 +* Improve suggestions for `redundant_pattern_matching` [#4352](https://github.com/rust-lang/rust-clippy/pull/4352)
 +* Improve suggestions for `explicit_write` [#4544](https://github.com/rust-lang/rust-clippy/pull/4544)
 +* Improve suggestion for `or_fun_call` [#4522](https://github.com/rust-lang/rust-clippy/pull/4522)
 +* Improve suggestion for `match_as_ref` [#4446](https://github.com/rust-lang/rust-clippy/pull/4446)
 +* Improve suggestion for `unnecessary_fold_span` [#4382](https://github.com/rust-lang/rust-clippy/pull/4382)
 +* Add suggestions for `unseparated_literal_suffix` [#4401](https://github.com/rust-lang/rust-clippy/pull/4401)
 +* Add suggestions for `char_lit_as_u8` [#4418](https://github.com/rust-lang/rust-clippy/pull/4418)
 +
 +## Rust 1.38
 +
 +Released 2019-09-26
 +
 +[e3cb40e...3aea860](https://github.com/rust-lang/rust-clippy/compare/e3cb40e...3aea860)
 +
 +* New Lints:
 +  * [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203)
 +  * [`inherent_to_string`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`inherent_to_string_shadow_display`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`type_repetition_in_bounds`] [#3766](https://github.com/rust-lang/rust-clippy/pull/3766)
 +  * [`try_err`] [#4222](https://github.com/rust-lang/rust-clippy/pull/4222)
 +* Move `{unnnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
 +* Extend the `use_self` lint to suggest uses of `Self::Variant` [#4308](https://github.com/rust-lang/rust-clippy/pull/4308)
 +* Improve suggestion for needless return [#4262](https://github.com/rust-lang/rust-clippy/pull/4262)
 +* Add auto-fixable suggestion for `let_unit` [#4337](https://github.com/rust-lang/rust-clippy/pull/4337)
 +* Fix false positive in `pub_enum_variant_names` and `enum_variant_names` [#4345](https://github.com/rust-lang/rust-clippy/pull/4345)
 +* Fix false positive in `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
 +* Fix false positive in `string_lit_as_bytes` [#4233](https://github.com/rust-lang/rust-clippy/pull/4233)
 +* Fix false positive in `needless_lifetimes` [#4266](https://github.com/rust-lang/rust-clippy/pull/4266)
 +* Fix false positive in `float_cmp` [#4275](https://github.com/rust-lang/rust-clippy/pull/4275)
 +* Fix false positives in `needless_return` [#4274](https://github.com/rust-lang/rust-clippy/pull/4274)
 +* Fix false negative in `match_same_arms` [#4246](https://github.com/rust-lang/rust-clippy/pull/4246)
 +* Fix incorrect suggestion for `needless_bool` [#4335](https://github.com/rust-lang/rust-clippy/pull/4335)
 +* Improve suggestion for `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
 +* Improve suggestion for `single_char_literal` [#4361](https://github.com/rust-lang/rust-clippy/pull/4361)
 +* Improve suggestion for `len_zero` [#4314](https://github.com/rust-lang/rust-clippy/pull/4314)
 +* Fix ICE in `implicit_hasher` [#4268](https://github.com/rust-lang/rust-clippy/pull/4268)
 +* Fix allow bug in `trivially_copy_pass_by_ref` [#4250](https://github.com/rust-lang/rust-clippy/pull/4250)
 +
 +## Rust 1.37
 +
 +Released 2019-08-15
 +
 +[082cfa7...e3cb40e](https://github.com/rust-lang/rust-clippy/compare/082cfa7...e3cb40e)
 +
 +* New Lints:
 +  * [`checked_conversions`] [#4088](https://github.com/rust-lang/rust-clippy/pull/4088)
 +  * [`get_last_with_len`] [#3832](https://github.com/rust-lang/rust-clippy/pull/3832)
 +  * [`integer_division`] [#4195](https://github.com/rust-lang/rust-clippy/pull/4195)
 +* Renamed Lint: `const_static_lifetime` is now called [`redundant_static_lifetimes`].
 +  The lint now covers statics in addition to consts [#4162](https://github.com/rust-lang/rust-clippy/pull/4162)
 +* [`match_same_arms`] now warns for all identical arms, instead of only the first one [#4102](https://github.com/rust-lang/rust-clippy/pull/4102)
 +* [`needless_return`] now works with void functions [#4220](https://github.com/rust-lang/rust-clippy/pull/4220)
 +* Fix false positive in [`redundant_closure`] [#4190](https://github.com/rust-lang/rust-clippy/pull/4190)
 +* Fix false positive in [`useless_attribute`] [#4107](https://github.com/rust-lang/rust-clippy/pull/4107)
 +* Fix incorrect suggestion for [`float_cmp`] [#4214](https://github.com/rust-lang/rust-clippy/pull/4214)
 +* Add suggestions for [`print_with_newline`] and [`write_with_newline`] [#4136](https://github.com/rust-lang/rust-clippy/pull/4136)
 +* Improve suggestions for `option_map_unwrap_or_else` and `result_map_unwrap_or_else` [#4164](https://github.com/rust-lang/rust-clippy/pull/4164)
 +* Improve suggestions for [`non_ascii_literal`] [#4119](https://github.com/rust-lang/rust-clippy/pull/4119)
 +* Improve diagnostics for [`let_and_return`] [#4137](https://github.com/rust-lang/rust-clippy/pull/4137)
 +* Improve diagnostics for [`trivially_copy_pass_by_ref`] [#4071](https://github.com/rust-lang/rust-clippy/pull/4071)
 +* Add macro check for [`unreadable_literal`] [#4099](https://github.com/rust-lang/rust-clippy/pull/4099)
 +
 +## Rust 1.36
 +
 +Released 2019-07-04
 +
 +[eb9f9b1...082cfa7](https://github.com/rust-lang/rust-clippy/compare/eb9f9b1...082cfa7)
 +
 +* New lints: [`find_map`], [`filter_map_next`] [#4039](https://github.com/rust-lang/rust-clippy/pull/4039)
 +* New lint: [`path_buf_push_overwrite`] [#3954](https://github.com/rust-lang/rust-clippy/pull/3954)
 +* Move `path_buf_push_overwrite` to the nursery [#4013](https://github.com/rust-lang/rust-clippy/pull/4013)
 +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 +* Allow allowing of [`toplevel_ref_arg`] lint [#4007](https://github.com/rust-lang/rust-clippy/pull/4007)
 +* Fix false negative in [`or_fun_call`] pertaining to nested constructors [#4084](https://github.com/rust-lang/rust-clippy/pull/4084)
 +* Fix false positive in [`or_fun_call`] pertaining to enum variant constructors [#4018](https://github.com/rust-lang/rust-clippy/pull/4018)
 +* Fix false positive in [`useless_let_if_seq`] pertaining to interior mutability [#4035](https://github.com/rust-lang/rust-clippy/pull/4035)
 +* Fix false positive in [`redundant_closure`] pertaining to non-function types [#4008](https://github.com/rust-lang/rust-clippy/pull/4008)
 +* Fix false positive in [`let_and_return`] pertaining to attributes on `let`s [#4024](https://github.com/rust-lang/rust-clippy/pull/4024)
 +* Fix false positive in [`module_name_repetitions`] lint pertaining to attributes [#4006](https://github.com/rust-lang/rust-clippy/pull/4006)
 +* Fix false positive on [`assertions_on_constants`] pertaining to `debug_assert!` [#3989](https://github.com/rust-lang/rust-clippy/pull/3989)
 +* Improve suggestion in [`map_clone`] to suggest `.copied()` where applicable  [#3970](https://github.com/rust-lang/rust-clippy/pull/3970) [#4043](https://github.com/rust-lang/rust-clippy/pull/4043)
 +* Improve suggestion for [`search_is_some`] [#4049](https://github.com/rust-lang/rust-clippy/pull/4049)
 +* Improve suggestion applicability for [`naive_bytecount`] [#3984](https://github.com/rust-lang/rust-clippy/pull/3984)
 +* Improve suggestion applicability for [`while_let_loop`] [#3975](https://github.com/rust-lang/rust-clippy/pull/3975)
 +* Improve diagnostics for [`too_many_arguments`] [#4053](https://github.com/rust-lang/rust-clippy/pull/4053)
 +* Improve diagnostics for [`cast_lossless`] [#4021](https://github.com/rust-lang/rust-clippy/pull/4021)
 +* Deal with macro checks in desugarings better [#4082](https://github.com/rust-lang/rust-clippy/pull/4082)
 +* Add macro check for [`unnecessary_cast`]  [#4026](https://github.com/rust-lang/rust-clippy/pull/4026)
 +* Remove [`approx_constant`]'s documentation's "Known problems" section. [#4027](https://github.com/rust-lang/rust-clippy/pull/4027)
 +* Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960)
 +* Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931)
 +
 +
 +## Rust 1.35
 +
 +Released 2019-05-20
 +
 +[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
 +
 +* New lint: `drop_bounds` to detect `T: Drop` bounds
 +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 +* Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code
 +* Move [`get_unwrap`] to the restriction category
 +* Improve suggestions for [`iter_cloned_collect`]
 +* Improve suggestions for [`cast_lossless`] to suggest suffixed literals
 +* Fix false positives in [`print_with_newline`] and [`write_with_newline`] pertaining to raw strings
 +* Fix false positive in [`needless_range_loop`] pertaining to structs without a `.iter()`
 +* Fix false positive in [`bool_comparison`] pertaining to non-bool types
 +* Fix false positive in [`redundant_closure`] pertaining to differences in borrows
 +* Fix false positive in `option_map_unwrap_or` on non-copy types
 +* Fix false positives in [`missing_const_for_fn`] pertaining to macros and trait method impls
 +* Fix false positive in [`needless_pass_by_value`] pertaining to procedural macros
 +* Fix false positive in [`needless_continue`] pertaining to loop labels
 +* Fix false positive for [`boxed_local`] pertaining to arguments moved into closures
 +* Fix false positive for [`use_self`] in nested functions
 +* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846)
 +* Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables
 +* Fix suggestion for [`single_char_pattern`] to correctly escape single quotes
 +* Avoid triggering [`redundant_closure`] in macros
 +* ICE fixes: [#3805](https://github.com/rust-lang/rust-clippy/pull/3805), [#3772](https://github.com/rust-lang/rust-clippy/pull/3772), [#3741](https://github.com/rust-lang/rust-clippy/pull/3741)
 +
 +## Rust 1.34
 +
 +Released 2019-04-10
 +
 +[1b89724...1fac380](https://github.com/rust-lang/rust-clippy/compare/1b89724...1fac380)
 +
 +* New lint: [`assertions_on_constants`] to detect for example `assert!(true)`
 +* New lint: [`dbg_macro`] to detect uses of the `dbg!` macro
 +* New lint: [`missing_const_for_fn`] that can suggest functions to be made `const`
 +* New lint: [`too_many_lines`] to detect functions with excessive LOC. It can be
 +  configured using the `too-many-lines-threshold` configuration.
 +* New lint: [`wildcard_enum_match_arm`] to check for wildcard enum matches using `_`
 +* Expand `redundant_closure` to also work for methods (not only functions)
 +* Fix ICEs in `vec_box`, `needless_pass_by_value` and `implicit_hasher`
 +* Fix false positive in `cast_sign_loss`
 +* Fix false positive in `integer_arithmetic`
 +* Fix false positive in `unit_arg`
 +* Fix false positives in `implicit_return`
 +* Add suggestion to `explicit_write`
 +* Improve suggestions for `question_mark` lint
 +* Fix incorrect suggestion for `cast_lossless`
 +* Fix incorrect suggestion for `expect_fun_call`
 +* Fix incorrect suggestion for `needless_bool`
 +* Fix incorrect suggestion for `needless_range_loop`
 +* Fix incorrect suggestion for `use_self`
 +* Fix incorrect suggestion for `while_let_on_iterator`
 +* Clippy is now slightly easier to invoke in non-cargo contexts. See
 +  [#3665][pull3665] for more details.
 +* We now have [improved documentation][adding_lints] on how to add new lints
 +
 +## Rust 1.33
 +
 +Released 2019-02-26
 +
 +[b2601be...1b89724](https://github.com/rust-lang/rust-clippy/compare/b2601be...1b89724)
 +
 +* New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`]
 +* The `rust-clippy` repository is now part of the `rust-lang` org.
 +* Rename `stutter` to `module_name_repetitions`
 +* Merge `new_without_default_derive` into `new_without_default` lint
 +* Move `large_digit_groups` from `style` group to `pedantic`
 +* Expand `bool_comparison` to check for `<`, `<=`, `>`, `>=`, and `!=`
 +  comparisons against booleans
 +* Expand `no_effect` to detect writes to constants such as `A_CONST.field = 2`
 +* Expand `redundant_clone` to work on struct fields
 +* Expand `suspicious_else_formatting` to detect `if .. {..} {..}`
 +* Expand `use_self` to work on tuple structs and also in local macros
 +* Fix ICE in `result_map_unit_fn` and `option_map_unit_fn`
 +* Fix false positives in `implicit_return`
 +* Fix false positives in `use_self`
 +* Fix false negative in `clone_on_copy`
 +* Fix false positive in `doc_markdown`
 +* Fix false positive in `empty_loop`
 +* Fix false positive in `if_same_then_else`
 +* Fix false positive in `infinite_iter`
 +* Fix false positive in `question_mark`
 +* Fix false positive in `useless_asref`
 +* Fix false positive in `wildcard_dependencies`
 +* Fix false positive in `write_with_newline`
 +* Add suggestion to `explicit_write`
 +* Improve suggestions for `question_mark` lint
 +* Fix incorrect suggestion for `get_unwrap`
 +
 +## Rust 1.32
 +
 +Released 2019-01-17
 +
 +[2e26fdc2...b2601be](https://github.com/rust-lang/rust-clippy/compare/2e26fdc2...b2601be)
 +
 +* New lints: [`slow_vector_initialization`], [`mem_discriminant_non_enum`],
 +  [`redundant_clone`], [`wildcard_dependencies`],
 +  [`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`],
 +  [`mem_discriminant_non_enum`], [`cargo_common_metadata`]
 +* Add support for `u128` and `i128` to integer related lints
 +* Add float support to `mistyped_literal_suffixes`
 +* Fix false positives in `use_self`
 +* Fix false positives in `missing_comma`
 +* Fix false positives in `new_ret_no_self`
 +* Fix false positives in `possible_missing_comma`
 +* Fix false positive in `integer_arithmetic` in constant items
 +* Fix false positive in `needless_borrow`
 +* Fix false positive in `out_of_bounds_indexing`
 +* Fix false positive in `new_without_default_derive`
 +* Fix false positive in `string_lit_as_bytes`
 +* Fix false negative in `out_of_bounds_indexing`
 +* Fix false negative in `use_self`. It will now also check existential types
 +* Fix incorrect suggestion for `redundant_closure_call`
 +* Fix various suggestions that contained expanded macros
 +* Fix `bool_comparison` triggering 3 times on on on the same code
 +* Expand `trivially_copy_pass_by_ref` to work on trait methods
 +* Improve suggestion for `needless_range_loop`
 +* Move `needless_pass_by_value` from `pedantic` group to `style`
 +
 +## Rust 1.31
 +
 +Released 2018-12-06
 +
 +[125907ad..2e26fdc2](https://github.com/rust-lang/rust-clippy/compare/125907ad..2e26fdc2)
 +
 +* Clippy has been relicensed under a dual MIT / Apache license.
 +  See [#3093](https://github.com/rust-lang/rust-clippy/issues/3093) for more
 +  information.
 +* With Rust 1.31, Clippy is no longer available via crates.io. The recommended
 +  installation method is via `rustup component add clippy`.
 +* New lints: [`redundant_pattern_matching`], [`unnecessary_filter_map`],
 +  [`unused_unit`], [`map_flatten`], [`mem_replace_option_with_none`]
 +* Fix ICE in `if_let_redundant_pattern_matching`
 +* Fix ICE in `needless_pass_by_value` when encountering a generic function
 +  argument with a lifetime parameter
 +* Fix ICE in `needless_range_loop`
 +* Fix ICE in `single_char_pattern` when encountering a constant value
 +* Fix false positive in `assign_op_pattern`
 +* Fix false positive in `boxed_local` on trait implementations
 +* Fix false positive in `cmp_owned`
 +* Fix false positive in `collapsible_if` when conditionals have comments
 +* Fix false positive in `double_parens`
 +* Fix false positive in `excessive_precision`
 +* Fix false positive in `explicit_counter_loop`
 +* Fix false positive in `fn_to_numeric_cast_with_truncation`
 +* Fix false positive in `map_clone`
 +* Fix false positive in `new_ret_no_self`
 +* Fix false positive in `new_without_default` when `new` is unsafe
 +* Fix false positive in `type_complexity` when using extern types
 +* Fix false positive in `useless_format`
 +* Fix false positive in `wrong_self_convention`
 +* Fix incorrect suggestion for `excessive_precision`
 +* Fix incorrect suggestion for `expect_fun_call`
 +* Fix incorrect suggestion for `get_unwrap`
 +* Fix incorrect suggestion for `useless_format`
 +* `fn_to_numeric_cast_with_truncation` lint can be disabled again
 +* Improve suggestions for `manual_memcpy`
 +* Improve help message for `needless_lifetimes`
 +
 +## Rust 1.30
 +
 +Released 2018-10-25
 +
 +[14207503...125907ad](https://github.com/rust-lang/rust-clippy/compare/14207503...125907ad)
 +
 +* Deprecate `assign_ops` lint
 +* New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`],
 +  [`needless_collect`], [`copy_iterator`]
 +* `cargo clippy -V` now includes the Clippy commit hash of the Rust
 +  Clippy component
 +* Fix ICE in `implicit_hasher`
 +* Fix ICE when encountering `println!("{}" a);`
 +* Fix ICE when encountering a macro call in match statements
 +* Fix false positive in `default_trait_access`
 +* Fix false positive in `trivially_copy_pass_by_ref`
 +* Fix false positive in `similar_names`
 +* Fix false positive in `redundant_field_name`
 +* Fix false positive in `expect_fun_call`
 +* Fix false negative in `identity_conversion`
 +* Fix false negative in `explicit_counter_loop`
 +* Fix `range_plus_one` suggestion and false negative
 +* `print_with_newline` / `write_with_newline`: don't warn about string with several `\n`s in them
 +* Fix `useless_attribute` to also whitelist `unused_extern_crates`
 +* Fix incorrect suggestion for `single_char_pattern`
 +* Improve suggestion for `identity_conversion` lint
 +* Move `explicit_iter_loop` and `explicit_into_iter_loop` from `style` group to `pedantic`
 +* Move `range_plus_one` and `range_minus_one` from `nursery` group to `complexity`
 +* Move `shadow_unrelated` from `restriction` group to `pedantic`
 +* Move `indexing_slicing` from `pedantic` group to `restriction`
 +
 +## Rust 1.29
 +
 +Released 2018-09-13
 +
 +[v0.0.212...14207503](https://github.com/rust-lang/rust-clippy/compare/v0.0.212...14207503)
 +
 +* :tada: :tada: **Rust 1.29 is the first stable Rust that includes a bundled Clippy** :tada:
 +  :tada:
 +  You can now run `rustup component add clippy-preview` and then `cargo
 +  clippy` to run Clippy. This should put an end to the continuous nightly
 +  upgrades for Clippy users.
 +* Clippy now follows the Rust versioning scheme instead of its own
 +* Fix ICE when encountering a `while let (..) = x.iter()` construct
 +* Fix false positives in `use_self`
 +* Fix false positive in `trivially_copy_pass_by_ref`
 +* Fix false positive in `useless_attribute` lint
 +* Fix false positive in `print_literal`
 +* Fix `use_self` regressions
 +* Improve lint message for `neg_cmp_op_on_partial_ord`
 +* Improve suggestion highlight for `single_char_pattern`
 +* Improve suggestions for various print/write macro lints
 +* Improve website header
 +
 +## 0.0.212 (2018-07-10)
 +* Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)*
 +
 +## 0.0.211
 +* Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)*
 +
 +## 0.0.210
 +* Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)*
 +
 +## 0.0.209
 +* Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)*
 +
 +## 0.0.208
 +* Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)*
 +
 +## 0.0.207
 +* Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)*
 +
 +## 0.0.206
 +* Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)*
 +
 +## 0.0.205
 +* Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)*
 +* Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint
 +
 +## 0.0.204
 +* Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)*
 +
 +## 0.0.203
 +* Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)*
 +* Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)`
 +
 +## 0.0.202
 +* Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)*
 +
 +## 0.0.201
 +* Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)*
 +
 +## 0.0.200
 +* Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)*
 +
 +## 0.0.199
 +* Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)*
 +
 +## 0.0.198
 +* Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)*
 +
 +## 0.0.197
 +* Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)*
 +
 +## 0.0.196
 +* Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)*
 +
 +## 0.0.195
 +* Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)*
 +
 +## 0.0.194
 +* Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)*
 +* New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`]
 +
 +## 0.0.193
 +* Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)*
 +
 +## 0.0.192
 +* Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)*
 +* New lint: [`print_literal`]
 +
 +## 0.0.191
 +* Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)*
 +* Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction.
 +
 +## 0.0.190
 +* Fix a bunch of intermittent cargo bugs
 +
 +## 0.0.189
 +* Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)*
 +
 +## 0.0.188
 +* Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)*
 +* New lint: [`while_immutable_condition`]
 +
 +## 0.0.187
 +* Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)*
 +* New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`]
 +
 +## 0.0.186
 +* Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)*
 +* Various false positive fixes
 +
 +## 0.0.185
 +* Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)*
 +* New lint: [`question_mark`]
 +
 +## 0.0.184
 +* Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)*
 +* New lints: [`double_comparisons`], [`empty_line_after_outer_attr`]
 +
 +## 0.0.183
 +* Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)*
 +* New lint: [`misaligned_transmute`]
 +
 +## 0.0.182
 +* Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)*
 +* New lint: [`decimal_literal_representation`]
 +
 +## 0.0.181
 +* Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)*
 +* New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`]
 +* Removed `unit_expr`
 +* Various false positive fixes for [`needless_pass_by_value`]
 +
 +## 0.0.180
 +* Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)*
 +
 +## 0.0.179
 +* Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)*
 +
 +## 0.0.178
 +* Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)*
 +
 +## 0.0.177
 +* Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)*
 +* New lint: [`match_as_ref`]
 +
 +## 0.0.176
 +* Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)*
 +
 +## 0.0.175
 +* Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)*
 +
 +## 0.0.174
 +* Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)*
 +
 +## 0.0.173
 +* Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)*
 +
 +## 0.0.172
 +* Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)*
 +
 +## 0.0.171
 +* Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)*
 +
 +## 0.0.170
 +* Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)*
 +
 +## 0.0.169
 +* Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)*
 +* New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`]
 +
 +## 0.0.168
 +* Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)*
 +
 +## 0.0.167
 +* Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)*
 +* New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`]
 +
 +## 0.0.166
 +* Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)*
 +* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`],
 +  [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`],
 +  [`transmute_int_to_float`]
 +
 +## 0.0.165
 +* Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27)
 +* New lint: [`mut_range_bound`]
 +
 +## 0.0.164
 +* Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)*
 +* New lint: [`int_plus_one`]
 +
 +## 0.0.163
 +* Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)*
 +
 +## 0.0.162
 +* Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)*
 +* New lint: [`chars_last_cmp`]
 +* Improved suggestions for [`needless_borrow`], [`ptr_arg`],
 +
 +## 0.0.161
 +* Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)*
 +
 +## 0.0.160
 +* Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)*
 +
 +## 0.0.159
 +* Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)*
 +* New lint: [`clone_on_ref_ptr`]
 +
 +## 0.0.158
 +* New lint: [`manual_memcpy`]
 +* [`cast_lossless`] no longer has redundant parentheses in its suggestions
 +* Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)*
 +
 +## 0.0.157 - 2017-09-04
 +* Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)*
 +* New lint: `unit_expr`
 +
 +## 0.0.156 - 2017-09-03
 +* Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)*
 +
 +## 0.0.155
 +* Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)*
 +* New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`]
 +
 +## 0.0.154
 +* Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)*
 +* Fix [`use_self`] triggering inside derives
 +* Add support for linting an entire workspace with `cargo clippy --all`
 +* New lint: [`naive_bytecount`]
 +
 +## 0.0.153
 +* Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)*
 +* New lint: [`use_self`]
 +
 +## 0.0.152
 +* Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)*
 +
 +## 0.0.151
 +* Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)*
 +
 +## 0.0.150
 +* Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)*
 +
 +## 0.0.148
 +* Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)*
 +* New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`]
 +
 +## 0.0.147
 +* Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)*
 +
 +## 0.0.146
 +* Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)*
 +* Fixes false positives in `inline_always`
 +* Fixes false negatives in `panic_params`
 +
 +## 0.0.145
 +* Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)*
 +
 +## 0.0.144
 +* Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)*
 +
 +## 0.0.143
 +* Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)*
 +* Fix `cargo clippy` crashing on `dylib` projects
 +* Fix false positives around `nested_while_let` and `never_loop`
 +
 +## 0.0.142
 +* Update to *rustc 1.20.0-nightly (067971139 2017-07-02)*
 +
 +## 0.0.141
 +* Rewrite of the `doc_markdown` lint.
 +* Deprecated [`range_step_by_zero`]
 +* New lint: [`iterator_step_by_zero`]
 +* New lint: [`needless_borrowed_reference`]
 +* Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)*
 +
 +## 0.0.140 - 2017-06-16
 +* Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)*
 +
 +## 0.0.139 — 2017-06-10
 +* Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)*
 +* Fix bugs with for loop desugaring
 +* Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`]
 +
 +## 0.0.138 — 2017-06-05
 +* Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)*
 +
 +## 0.0.137 — 2017-06-05
 +* Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)*
 +
 +## 0.0.136 — 2017—05—26
 +* Update to *rustc 1.19.0-nightly (557967766 2017-05-26)*
 +
 +## 0.0.135 — 2017—05—24
 +* Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)*
 +
 +## 0.0.134 — 2017—05—19
 +* Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)*
 +
 +## 0.0.133 — 2017—05—14
 +* Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)*
 +
 +## 0.0.132 — 2017—05—05
 +* Fix various bugs and some ices
 +
 +## 0.0.131 — 2017—05—04
 +* Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)*
 +
 +## 0.0.130 — 2017—05—03
 +* Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)*
 +
 +## 0.0.129 — 2017-05-01
 +* Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)*
 +
 +## 0.0.128 — 2017-04-28
 +* Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)*
 +
 +## 0.0.127 — 2017-04-27
 +* Update to *rustc 1.18.0-nightly (036983201 2017-04-26)*
 +* New lint: [`needless_continue`]
 +
 +## 0.0.126 — 2017-04-24
 +* Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)*
 +
 +## 0.0.125 — 2017-04-19
 +* Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)*
 +
 +## 0.0.124 — 2017-04-16
 +* Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)*
 +
 +## 0.0.123 — 2017-04-07
 +* Fix various false positives
 +
 +## 0.0.122 — 2017-04-07
 +* Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)*
 +* New lint: [`op_ref`]
 +
 +## 0.0.121 — 2017-03-21
 +* Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)*
 +
 +## 0.0.120 — 2017-03-17
 +* Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)*
 +
 +## 0.0.119 — 2017-03-13
 +* Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)*
 +
 +## 0.0.118 — 2017-03-05
 +* Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)*
 +
 +## 0.0.117 — 2017-03-01
 +* Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)*
 +
 +## 0.0.116 — 2017-02-28
 +* Fix `cargo clippy` on 64 bit windows systems
 +
 +## 0.0.115 — 2017-02-27
 +* Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)*
 +* New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`]
 +
 +## 0.0.114 — 2017-02-08
 +* Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)*
 +* Tests are now ui tests (testing the exact output of rustc)
 +
 +## 0.0.113 — 2017-02-04
 +* Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)*
 +* New lint: [`large_enum_variant`]
 +* `explicit_into_iter_loop` provides suggestions
 +
 +## 0.0.112 — 2017-01-27
 +* Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)*
 +
 +## 0.0.111 — 2017-01-21
 +* Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)*
 +
 +## 0.0.110 — 2017-01-20
 +* Add badges and categories to `Cargo.toml`
 +
 +## 0.0.109 — 2017-01-19
 +* Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)*
 +
 +## 0.0.108 — 2017-01-12
 +* Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)*
 +
 +## 0.0.107 — 2017-01-11
 +* Update regex dependency
 +* Fix FP when matching `&&mut` by `&ref`
 +* Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()`
 +* New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`]
 +
 +## 0.0.106 — 2017-01-04
 +* Fix FP introduced by rustup in [`wrong_self_convention`]
 +
 +## 0.0.105 — 2017-01-04
 +* Update to *rustc 1.16.0-nightly (468227129 2017-01-03)*
 +* New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`]
 +* Fix suggestion in [`new_without_default`]
 +* FP fix in [`absurd_extreme_comparisons`]
 +
 +## 0.0.104 — 2016-12-15
 +* Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)*
 +
 +## 0.0.103 — 2016-11-25
 +* Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)*
 +
 +## 0.0.102 — 2016-11-24
 +* Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)*
 +
 +## 0.0.101 — 2016-11-23
 +* Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)*
 +* New lint: [`string_extend_chars`]
 +
 +## 0.0.100 — 2016-11-20
 +* Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)*
 +
 +## 0.0.99 — 2016-11-18
 +* Update to rustc 1.15.0-nightly (0ed951993 2016-11-14)
 +* New lint: [`get_unwrap`]
 +
 +## 0.0.98 — 2016-11-08
 +* Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy`
 +
 +## 0.0.97 — 2016-11-03
 +* For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was
 +  previously added for a short time under the name `clippy` but removed for
 +  compatibility.
 +* `cargo clippy --help` is more helping (and less helpful :smile:)
 +* Rustup to *rustc 1.14.0-nightly (5665bdf3e 2016-11-02)*
 +* New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`]
 +
 +## 0.0.96 — 2016-10-22
 +* Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)*
 +* New lint: [`iter_skip_next`]
 +
 +## 0.0.95 — 2016-10-06
 +* Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)*
 +
 +## 0.0.94 — 2016-10-04
 +* Fixes bustage on Windows due to forbidden directory name
 +
 +## 0.0.93 — 2016-10-03
 +* Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)*
 +* `option_map_unwrap_or` and `option_map_unwrap_or_else` are now
 +  allowed by default.
 +* New lint: [`explicit_into_iter_loop`]
 +
 +## 0.0.92 — 2016-09-30
 +* Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)*
 +
 +## 0.0.91 — 2016-09-28
 +* Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)*
 +
 +## 0.0.90 — 2016-09-09
 +* Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)*
 +
 +## 0.0.89 — 2016-09-06
 +* Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)*
 +
 +## 0.0.88 — 2016-09-04
 +* Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)*
 +* The following lints are not new but were only usable through the `clippy`
 +  lint groups: [`filter_next`], `for_loop_over_option`,
 +  `for_loop_over_result` and [`match_overlapping_arm`]. You should now be
 +  able to `#[allow/deny]` them individually and they are available directly
 +  through `cargo clippy`.
 +
 +## 0.0.87 — 2016-08-31
 +* Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)*
 +* New lints: [`builtin_type_shadow`]
 +* Fix FP in [`zero_prefixed_literal`] and `0b`/`0o`
 +
 +## 0.0.86 — 2016-08-28
 +* Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)*
 +* New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`]
 +
 +## 0.0.85 — 2016-08-19
 +* Fix ICE with [`useless_attribute`]
 +* [`useless_attribute`] ignores `unused_imports` on `use` statements
 +
 +## 0.0.84 — 2016-08-18
 +* Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)*
 +
 +## 0.0.83 — 2016-08-17
 +* Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)*
 +* New lints: [`print_with_newline`], [`useless_attribute`]
 +
 +## 0.0.82 — 2016-08-17
 +* Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)*
 +* New lint: [`module_inception`]
 +
 +## 0.0.81 — 2016-08-14
 +* Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)*
 +* New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`]
 +* False positive fix in [`too_many_arguments`]
 +* Addition of functionality to [`needless_borrow`]
 +* Suggestions for [`clone_on_copy`]
 +* Bug fix in [`wrong_self_convention`]
 +* Doc improvements
 +
 +## 0.0.80 — 2016-07-31
 +* Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)*
 +* New lints: [`misrefactored_assign_op`], [`serde_api_misuse`]
 +
 +## 0.0.79 — 2016-07-10
 +* Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)*
 +* Major suggestions refactoring
 +
 +## 0.0.78 — 2016-07-02
 +* Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)*
 +* New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`]
 +* For compatibility, `cargo clippy` does not defines the `clippy` feature
 +  introduced in 0.0.76 anymore
 +* [`collapsible_if`] now considers `if let`
 +
 +## 0.0.77 — 2016-06-21
 +* Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)*
 +* New lints: `stutter` and [`iter_nth`]
 +
 +## 0.0.76 — 2016-06-10
 +* Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)*
 +* `cargo clippy` now automatically defines the `clippy` feature
 +* New lint: [`not_unsafe_ptr_arg_deref`]
 +
 +## 0.0.75 — 2016-06-08
 +* Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)*
 +
 +## 0.0.74 — 2016-06-07
 +* Fix bug with `cargo-clippy` JSON parsing
 +* Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the
 +  “for further information visit *lint-link*” message.
 +
 +## 0.0.73 — 2016-06-05
 +* Fix false positives in [`useless_let_if_seq`]
 +
 +## 0.0.72 — 2016-06-04
 +* Fix false positives in [`useless_let_if_seq`]
 +
 +## 0.0.71 — 2016-05-31
 +* Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)*
 +* New lint: [`useless_let_if_seq`]
 +
 +## 0.0.70 — 2016-05-28
 +* Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)*
 +* [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`,
 +  `RegexBuilder::new` and byte regexes
 +
 +## 0.0.69 — 2016-05-20
 +* Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)*
 +* [`used_underscore_binding`] has been made `Allow` temporarily
 +
 +## 0.0.68 — 2016-05-17
 +* Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)*
 +* New lint: [`unnecessary_operation`]
 +
 +## 0.0.67 — 2016-05-12
 +* Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)*
 +
 +## 0.0.66 — 2016-05-11
 +* New `cargo clippy` subcommand
 +* New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`]
 +
 +## 0.0.65 — 2016-05-08
 +* Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)*
 +* New lints: [`float_arithmetic`], [`integer_arithmetic`]
 +
 +## 0.0.64 — 2016-04-26
 +* Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)*
 +* New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`]
 +
 +## 0.0.63 — 2016-04-08
 +* Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)*
 +
 +## 0.0.62 — 2016-04-07
 +* Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)*
 +
 +## 0.0.61 — 2016-04-03
 +* Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)*
 +* New lint: [`invalid_upcast_comparisons`]
 +
 +## 0.0.60 — 2016-04-01
 +* Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)*
 +
 +## 0.0.59 — 2016-03-31
 +* Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)*
 +* New lints: [`logic_bug`], [`nonminimal_bool`]
 +* Fixed: [`match_same_arms`] now ignores arms with guards
 +* Improved: [`useless_vec`] now warns on `for … in vec![…]`
 +
 +## 0.0.58 — 2016-03-27
 +* Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)*
 +* New lint: [`doc_markdown`]
 +
 +## 0.0.57 — 2016-03-27
 +* Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)*
 +* Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`]
 +* New lint: [`crosspointer_transmute`]
 +
 +## 0.0.56 — 2016-03-23
 +* Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)*
 +* New lints: [`many_single_char_names`] and [`similar_names`]
 +
 +## 0.0.55 — 2016-03-21
 +* Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)*
 +
 +## 0.0.54 — 2016-03-16
 +* Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)*
 +
 +## 0.0.53 — 2016-03-15
 +* Add a [configuration file]
 +
 +## ~~0.0.52~~
 +
 +## 0.0.51 — 2016-03-13
 +* Add `str` to types considered by [`len_zero`]
 +* New lints: [`indexing_slicing`]
 +
 +## 0.0.50 — 2016-03-11
 +* Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)*
 +
 +## 0.0.49 — 2016-03-09
 +* Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)*
 +* New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`]
 +
 +## 0.0.48 — 2016-03-07
 +* Fixed: ICE in [`needless_range_loop`] with globals
 +
 +## 0.0.47 — 2016-03-07
 +* Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)*
 +* New lint: [`redundant_closure_call`]
 +
 +[`AsMut`]: https://doc.rust-lang.org/std/convert/trait.AsMut.html
 +[`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
 +[configuration file]: ./rust-clippy#configuration
 +[pull3665]: https://github.com/rust-lang/rust-clippy/pull/3665
 +[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
 +[`README.md`]: https://github.com/rust-lang/rust-clippy/blob/master/README.md
 +
 +<!-- lint disable no-unused-definitions -->
 +<!-- begin autogenerated links to lint list -->
 +[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
 +[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
 +[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
 +[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
 +[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
 +[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
 +[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
 +[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
 +[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
 +[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
 +[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
 +[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
 +[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
 +[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
 +[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
 +[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
 +[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
 +[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
 +[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
 +[`box_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_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
 +[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
 +[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
 +[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
 +[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
 +[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
 +[`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss
 +[`cast_ptr_alignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ptr_alignment
 +[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
 +[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
 +[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
 +[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
 +[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
 +[`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions
 +[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
 +[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
 +[`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
 +[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied
 +[`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
 +[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
 +[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
 +[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
 +[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
 +[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
 +[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
 +[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
 +[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
 +[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
 +[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
 +[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
 +[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
 +[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
 +[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
 +[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
 +[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
 +[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
 +[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
 +[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
 +[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
++[`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls
 +[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
 +[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
 +[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
 +[`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents
 +[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
 +[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
 +[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
 +[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
 +[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
 +[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
 +[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
 +[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
 +[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
 +[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
 +[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
 +[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
 +[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
 +[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
 +[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
 +[`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant
 +[`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use
 +[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
 +[`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op
 +[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
 +[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
 +[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
 +[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
 +[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs
 +[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
 +[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
 +[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used
 +[`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy
 +[`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop
 +[`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods
 +[`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop
 +[`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop
 +[`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write
 +[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
 +[`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain
 +[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
 +[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
 +[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
 +[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
 +[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
 +[`filter_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_identity
 +[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
 +[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
 +[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
 +[`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
 +[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option
 +[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
 +[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
 +[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
 +[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs
 +[`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons
 +[`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools
 +[`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast
 +[`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation
 +[`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map
 +[`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles
 +[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
 +[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
 +[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
 +[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
 +[`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10
 +[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
 +[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
 +[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
 +[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
 +[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
 +[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
 +[`if_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
 +[`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
 +[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
 +[`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
 +[`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
 +[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
 +[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
 +[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
 +[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
 +[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax
 +[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax
 +[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
 +[`inspect_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#inspect_for_each
 +[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
 +[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
 +[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
 +[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
 +[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
 +[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
 +[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
 +[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
 +[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
 +[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
 +[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
 +[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
 +[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice
 +[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
 +[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
 +[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
 +[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
 +[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
 +[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
 +[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
 +[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
 +[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
 +[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
 +[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
 +[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
 +[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
 +[`let_underscore_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop
 +[`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock
 +[`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
 +[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
 +[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
 +[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
 +[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
 +[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
 +[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
 +[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
 +[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
 +[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
 +[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
 +[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
 +[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
 +[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
 +[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
 +[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
 +[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
++[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
 +[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
 +[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
 +[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
 +[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
 +[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
 +[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
 +[`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit
 +[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
 +[`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore
 +[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
 +[`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity
 +[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or
 +[`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
 +[`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
 +[`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
 +[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
 +[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
 +[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
 +[`match_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_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
 +[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
 +[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
 +[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
 +[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
 +[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
 +[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
 +[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
 +[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
 +[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
 +[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
++[`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files
 +[`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
 +[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
 +[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
 +[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
 +[`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
 +[`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl
 +[`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate
 +[`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit
 +[`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref
 +[`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut
 +[`mut_mutex_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mutex_lock
 +[`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound
 +[`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type
 +[`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic
 +[`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
 +[`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
 +[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
 +[`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool
 +[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
 +[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
 +[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
 +[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
 +[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
 +[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
 +[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
 +[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
++[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
 +[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
 +[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
 +[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
 +[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
 +[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
 +[`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
 +[`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
++[`negative_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#negative_feature_names
 +[`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop
 +[`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self
 +[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
 +[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
 +[`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
 +[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
 +[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
 +[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
 +[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
 +[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
 +[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
 +[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
 +[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
 +[`option_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_filter_map
 +[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
 +[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
 +[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
 +[`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
 +[`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call
 +[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
 +[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
 +[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
 +[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
 +[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
 +[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
 +[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
 +[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
 +[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
 +[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
 +[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
 +[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr
 +[`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
 +[`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
 +[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
 +[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
 +[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr
 +[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
 +[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
 +[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
 +[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
 +[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
 +[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
 +[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
 +[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
 +[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
 +[`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex
 +[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
 +[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
 +[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
 +[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
 +[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
 +[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
++[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
 +[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
 +[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
 +[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
 +[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
 +[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
 +[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
 +[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
 +[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
 +[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
 +[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
 +[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
 +[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
 +[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
 +[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
 +[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
 +[`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err
 +[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
 +[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
 +[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
 +[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
 +[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
 +[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
++[`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files
 +[`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
 +[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
 +[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
 +[`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same
 +[`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
 +[`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement
 +[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq
 +[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
 +[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
 +[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
 +[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
 +[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
 +[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
 +[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
 +[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
 +[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
 +[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
 +[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
 +[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
 +[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
 +[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
 +[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
 +[`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars
 +[`string_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes
 +[`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
 +[`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
 +[`strlen_on_c_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#strlen_on_c_strings
 +[`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
 +[`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops
 +[`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
 +[`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting
 +[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
 +[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
 +[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
 +[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
 +[`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn
 +[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
 +[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
 +[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
 +[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
 +[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
 +[`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
 +[`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_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
 +[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
 +[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
 +[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
 +[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
 +[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
 +[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
 +[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
 +[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
 +[`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
 +[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
 +[`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp
 +[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
 +[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
 +[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
 +[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
 +[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
 +[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
 +[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
 +[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
 +[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
 +[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
 +[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
 +[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
 +[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
 +[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
 +[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable
 +[`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal
 +[`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize
 +[`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name
 +[`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization
 +[`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix
 +[`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute
 +[`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice
 +[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
 +[`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async
 +[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
 +[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
 +[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
 +[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
 +[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
 +[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result
 +[`unwrap_or_else_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_or_else_default
 +[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used
 +[`upper_case_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms
 +[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
 +[`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
 +[`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding
 +[`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref
 +[`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute
 +[`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
 +[`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format
 +[`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq
 +[`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute
 +[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
 +[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box
 +[`vec_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push
 +[`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero
 +[`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask
 +[`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads
 +[`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons
 +[`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition
 +[`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop
 +[`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator
 +[`wildcard_dependencies`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_dependencies
 +[`wildcard_enum_match_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_enum_match_arm
 +[`wildcard_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports
 +[`wildcard_in_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_in_or_patterns
 +[`write_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_literal
 +[`write_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_with_newline
 +[`writeln_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#writeln_empty_string
 +[`wrong_pub_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_pub_self_convention
 +[`wrong_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention
 +[`wrong_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_transmute
 +[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
 +[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
 +[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
 +[`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values
 +[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
 +<!-- end autogenerated links to lint list -->
index 16b6c207a5f19bc1c77297f8dc8e2cfdee2b9c81,0000000000000000000000000000000000000000..2310370fb9fbe7eaed80f888f40633a2f8151fd3
mode 100644,000000..100644
--- /dev/null
@@@ -1,59 -1,0 +1,64 @@@
- version = "0.1.56"
 +[package]
 +name = "clippy"
- serde = { version = "1.0", features = ["derive"] }
- derive-new = "0.5"
++version = "0.1.57"
 +description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 +repository = "https://github.com/rust-lang/rust-clippy"
 +readme = "README.md"
 +license = "MIT OR Apache-2.0"
 +keywords = ["clippy", "lint", "plugin"]
 +categories = ["development-tools", "development-tools::cargo-plugins"]
 +build = "build.rs"
 +edition = "2018"
 +publish = false
 +
 +[[bin]]
 +name = "cargo-clippy"
 +test = false
 +path = "src/main.rs"
 +
 +[[bin]]
 +name = "clippy-driver"
 +path = "src/driver.rs"
 +
 +[dependencies]
 +# begin automatic update
 +clippy_lints = { version = "0.1.50", path = "clippy_lints" }
 +# end automatic update
 +semver = "0.11"
 +rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" }
 +tempfile = { version = "3.1.0", optional = true }
 +
 +[dev-dependencies]
 +cargo_metadata = "0.12"
 +compiletest_rs = { version = "0.6.0", features = ["tmp"] }
 +tester = "0.9"
- quote = "1"
- syn = { version = "1", features = ["full"] }
 +regex = "1.4"
 +# This is used by the `collect-metadata` alias.
 +filetime = "0.2"
 +
 +# A noop dependency that changes in the Rust repository, it's a bit of a hack.
 +# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
 +# for more information.
 +rustc-workspace-hack = "1.0.0"
 +
++# UI test dependencies
++clippy_utils = { path = "clippy_utils" }
++derive-new = "0.5"
++if_chain = "1.0"
++itertools = "0.10.1"
++quote = "1"
++serde = { version = "1.0", features = ["derive"] }
++syn = { version = "1", features = ["full"] }
++
 +[build-dependencies]
 +rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" }
 +
 +[features]
 +deny-warnings = ["clippy_lints/deny-warnings"]
 +integration = ["tempfile"]
 +internal-lints = ["clippy_lints/internal-lints"]
 +metadata-collector-lint = ["internal-lints", "clippy_lints/metadata-collector-lint"]
 +
 +[package.metadata.rust-analyzer]
 +# This package uses #[feature(rustc_private)]
 +rustc_private = true
index e1c968273cdf63ef95eae0633929566df1304e7e,0000000000000000000000000000000000000000..aaf404eadea11e9ac52730c83a8331fa1ded4e4e
mode 100644,000000..100644
--- /dev/null
@@@ -1,246 -1,0 +1,246 @@@
- #### Step 1: Install rustup
 +# Clippy
 +
 +[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test%22+event%3Apush+branch%3Aauto)
 +[![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](#license)
 +
 +A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
 +
 +[There are over 450 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
 +
 +Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
 +You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
 +
 +| Category              | Description                                                                         | Default level |
 +| --------------------- | ----------------------------------------------------------------------------------- | ------------- |
 +| `clippy::all`         | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** |
 +| `clippy::correctness` | code that is outright wrong or useless                                              | **deny**      |
 +| `clippy::suspicious`  | code that is most likely wrong or useless                                           | **warn**      |
 +| `clippy::style`       | code that should be written in a more idiomatic way                                 | **warn**      |
 +| `clippy::complexity`  | code that does something simple but in a complex way                                | **warn**      |
 +| `clippy::perf`        | code that can be written to run faster                                              | **warn**      |
 +| `clippy::pedantic`    | lints which are rather strict or might have false positives                         | allow         |
 +| `clippy::nursery`     | new lints that are still under development                                          | allow         |
 +| `clippy::cargo`       | lints for the cargo manifest                                                        | allow         |
 +
 +More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
 +
 +The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also contains "restriction lints", which are
 +for things which are usually not considered "bad", but may be useful to turn on in specific cases. These should be used
 +very selectively, if at all.
 +
 +Table of contents:
 +
 +*   [Usage instructions](#usage)
 +*   [Configuration](#configuration)
 +*   [Contributing](#contributing)
 +*   [License](#license)
 +
 +## Usage
 +
 +Below are instructions on how to use Clippy as a subcommand, compiled from source
 +or in Travis CI.
 +
 +### As a cargo subcommand (`cargo clippy`)
 +
 +One way to use Clippy is by installing Clippy through rustup as a cargo
 +subcommand.
 +
- You can install [rustup](https://rustup.rs/) on supported platforms. This will help
++#### Step 1: Install Rustup
 +
- If you already have rustup installed, update to ensure you have the latest
- rustup and compiler:
++You can install [Rustup](https://rustup.rs/) on supported platforms. This will help
 +us install Clippy and its dependencies.
 +
++If you already have Rustup installed, update to ensure you have the latest
++Rustup and compiler:
 +
 +```terminal
 +rustup update
 +```
 +
 +#### Step 2: Install Clippy
 +
 +Once you have rustup and the latest stable release (at least Rust 1.29) installed, run the following command:
 +
 +```terminal
 +rustup component add clippy
 +```
 +If it says that it can't find the `clippy` component, please run `rustup self update`.
 +
 +#### Step 3: Run Clippy
 +
 +Now you can run Clippy by invoking the following command:
 +
 +```terminal
 +cargo clippy
 +```
 +
 +#### 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:
 +
 +```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
 +```
 +
 +### As a rustc replacement (`clippy-driver`)
 +
 +Clippy can also be used in projects that do not use cargo. To do so, you will need to replace
 +your `rustc` compilation commands with `clippy-driver`. For example, if your project runs:
 +
 +```terminal
 +rustc --edition 2018 -Cpanic=abort foo.rs
 +```
 +
 +Then, to enable Clippy, you will need to call:
 +
 +```terminal
 +clippy-driver --edition 2018 -Cpanic=abort foo.rs
 +```
 +
 +Note that `rustc` will still run, i.e. it will still emit the output files it normally does.
 +
 +### Travis CI
 +
 +You can add Clippy to Travis CI in the same way you use it locally:
 +
 +```yml
 +language: rust
 +rust:
 +  - stable
 +  - beta
 +before_script:
 +  - rustup component add clippy
 +script:
 +  - cargo clippy
 +  # if you want the build job to fail when encountering warnings, use
 +  - cargo clippy -- -D warnings
 +  # in order to also check tests and non-default crate features, use
 +  - cargo clippy --all-targets --all-features -- -D warnings
 +  - cargo test
 +  # etc.
 +```
 +
 +Note that 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.). If you want to avoid this and only cause
 +an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command
 +line. (You can swap `clippy::all` with the specific lint category you are targeting.)
 +
 +## Configuration
 +
 +Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable =
 +value` mapping eg.
 +
 +```toml
 +avoid-breaking-exported-api = false
 +blacklisted-names = ["toto", "tata", "titi"]
 +cognitive-complexity-threshold = 30
 +```
 +
 +See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
 +lints can be configured and the meaning of the variables.
 +
 +To deactivate the “for further information visit *lint-link*” message you can
 +define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable.
 +
 +### Allowing/denying lints
 +
 +You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
 +
 +*   the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`)
 +
 +*   all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`,
 +    `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive
 +    lints prone to false positives.
 +
 +*   only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.)
 +
 +*   `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc.
 +
 +Note: `allow` means to suppress the lint for your code. With `warn` the lint
 +will only emit a warning, while with `deny` the lint will emit an error, when
 +triggering for your code. An error causes clippy to exit with an error code, so
 +is useful in scripts like CI/CD.
 +
 +If you do not want to include your lint levels in your code, you can globally
 +enable/disable lints by passing extra flags to Clippy during the run:
 +
 +To allow `lint_name`, run
 +
 +```terminal
 +cargo clippy -- -A clippy::lint_name
 +```
 +
 +And to warn on `lint_name`, run
 +
 +```terminal
 +cargo clippy -- -W clippy::lint_name
 +```
 +
 +This also works with lint groups. For example you
 +can run Clippy with warnings for all lints enabled:
 +```terminal
 +cargo clippy -- -W clippy::pedantic
 +```
 +
 +If you care only about a single lint, you can allow all others and then explicitly warn on
 +the lint(s) you are interested in:
 +```terminal
 +cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...
 +```
 +
 +### Specifying the minimum supported Rust version
 +
 +Projects that intend to support old versions of Rust can disable lints pertaining to newer features by
 +specifying the minimum supported Rust version (MSRV) in the clippy configuration file.
 +
 +```toml
 +msrv = "1.30.0"
 +```
 +
 +The MSRV can also be specified as an inner attribute, like below.
 +
 +```rust
 +#![feature(custom_inner_attributes)]
 +#![clippy::msrv = "1.30.0"]
 +
 +fn main() {
 +  ...
 +}
 +```
 +
 +You can also omit the patch version when specifying the MSRV, so `msrv = 1.30`
 +is equivalent to `msrv = 1.30.0`.
 +
 +Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly.
 +
 +Lints that recognize this configuration option can be found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv)
 +
 +## Contributing
 +
 +If you want to contribute to Clippy, you can find more information in [CONTRIBUTING.md](https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md).
 +
 +## License
 +
 +Copyright 2014-2021 The Rust Project Developers
 +
 +Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 +[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license
 +<LICENSE-MIT or [https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT)>, at your
 +option. Files in the project may not be
 +copied, modified, or distributed except according to those terms.
index 72bdaf8d59282f89f39f63142a9191325f4c224f,0000000000000000000000000000000000000000..e05db7af58677cd0f30c84fabf39fd7c40780df0
mode 100644,000000..100644
--- /dev/null
@@@ -1,560 -1,0 +1,559 @@@
- pub mod stderr_length_check;
 +#![feature(once_cell)]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use itertools::Itertools;
 +use regex::Regex;
 +use std::collections::HashMap;
 +use std::ffi::OsStr;
 +use std::fs;
 +use std::lazy::SyncLazy;
 +use std::path::{Path, PathBuf};
 +use walkdir::WalkDir;
 +
 +pub mod bless;
 +pub mod fmt;
 +pub mod new_lint;
 +pub mod serve;
 +pub mod setup;
 +pub mod update_lints;
 +
 +static DEC_CLIPPY_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
 +    Regex::new(
 +        r#"(?x)
 +    declare_clippy_lint!\s*[\{(]
 +    (?:\s+///.*)*
 +    \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
 +    (?P<cat>[a-z_]+)\s*,\s*
 +    "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
 +"#,
 +    )
 +    .unwrap()
 +});
 +
 +static DEC_DEPRECATED_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
 +    Regex::new(
 +        r#"(?x)
 +    declare_deprecated_lint!\s*[{(]\s*
 +    (?:\s+///.*)*
 +    \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
 +    "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
 +"#,
 +    )
 +    .unwrap()
 +});
 +static NL_ESCAPE_RE: SyncLazy<Regex> = SyncLazy::new(|| Regex::new(r#"\\\n\s*"#).unwrap());
 +
 +pub static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
 +
 +/// Lint data parsed from the Clippy source code.
 +#[derive(Clone, PartialEq, Debug)]
 +pub struct Lint {
 +    pub name: String,
 +    pub group: String,
 +    pub desc: String,
 +    pub deprecation: Option<String>,
 +    pub module: String,
 +}
 +
 +impl Lint {
 +    #[must_use]
 +    pub fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self {
 +        Self {
 +            name: name.to_lowercase(),
 +            group: group.to_string(),
 +            desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(),
 +            deprecation: deprecation.map(ToString::to_string),
 +            module: module.to_string(),
 +        }
 +    }
 +
 +    /// Returns all non-deprecated lints and non-internal lints
 +    #[must_use]
 +    pub fn usable_lints(lints: &[Self]) -> Vec<Self> {
 +        lints
 +            .iter()
 +            .filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal"))
 +            .cloned()
 +            .collect()
 +    }
 +
 +    /// Returns all internal lints (not `internal_warn` lints)
 +    #[must_use]
 +    pub fn internal_lints(lints: &[Self]) -> Vec<Self> {
 +        lints.iter().filter(|l| l.group == "internal").cloned().collect()
 +    }
 +
 +    /// Returns all deprecated lints
 +    #[must_use]
 +    pub fn deprecated_lints(lints: &[Self]) -> Vec<Self> {
 +        lints.iter().filter(|l| l.deprecation.is_some()).cloned().collect()
 +    }
 +
 +    /// Returns the lints in a `HashMap`, grouped by the different lint groups
 +    #[must_use]
 +    pub fn by_lint_group(lints: impl Iterator<Item = Self>) -> HashMap<String, Vec<Self>> {
 +        lints.map(|lint| (lint.group.to_string(), lint)).into_group_map()
 +    }
 +}
 +
 +/// Generates the Vec items for `register_lint_group` calls in `clippy_lints/src/lib.rs`.
 +#[must_use]
 +pub fn gen_lint_group_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
 +    lints
 +        .map(|l| format!("        LintId::of({}::{}),", l.module, l.name.to_uppercase()))
 +        .sorted()
 +        .collect::<Vec<String>>()
 +}
 +
 +/// Generates the `pub mod module_name` list in `clippy_lints/src/lib.rs`.
 +#[must_use]
 +pub fn gen_modules_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
 +    lints
 +        .map(|l| &l.module)
 +        .unique()
 +        .map(|module| format!("mod {};", module))
 +        .sorted()
 +        .collect::<Vec<String>>()
 +}
 +
 +/// Generates the list of lint links at the bottom of the README
 +#[must_use]
 +pub fn gen_changelog_lint_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
 +    lints
 +        .sorted_by_key(|l| &l.name)
 +        .map(|l| format!("[`{}`]: {}#{}", l.name, DOCS_LINK, l.name))
 +        .collect()
 +}
 +
 +/// Generates the `register_removed` code in `./clippy_lints/src/lib.rs`.
 +#[must_use]
 +pub fn gen_deprecated<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
 +    lints
 +        .flat_map(|l| {
 +            l.deprecation
 +                .clone()
 +                .map(|depr_text| {
 +                    vec![
 +                        "    store.register_removed(".to_string(),
 +                        format!("        \"clippy::{}\",", l.name),
 +                        format!("        \"{}\",", depr_text),
 +                        "    );".to_string(),
 +                    ]
 +                })
 +                .expect("only deprecated lints should be passed")
 +        })
 +        .collect::<Vec<String>>()
 +}
 +
 +#[must_use]
 +pub fn gen_register_lint_list<'a>(
 +    internal_lints: impl Iterator<Item = &'a Lint>,
 +    usable_lints: impl Iterator<Item = &'a Lint>,
 +) -> Vec<String> {
 +    let header = "    store.register_lints(&[".to_string();
 +    let footer = "    ]);".to_string();
 +    let internal_lints = internal_lints
 +        .sorted_by_key(|l| format!("        {}::{},", l.module, l.name.to_uppercase()))
 +        .map(|l| {
 +            format!(
 +                "        #[cfg(feature = \"internal-lints\")]\n        {}::{},",
 +                l.module,
 +                l.name.to_uppercase()
 +            )
 +        });
 +    let other_lints = usable_lints
 +        .sorted_by_key(|l| format!("        {}::{},", l.module, l.name.to_uppercase()))
 +        .map(|l| format!("        {}::{},", l.module, l.name.to_uppercase()))
 +        .sorted();
 +    let mut lint_list = vec![header];
 +    lint_list.extend(internal_lints);
 +    lint_list.extend(other_lints);
 +    lint_list.push(footer);
 +    lint_list
 +}
 +
 +/// Gathers all files in `src/clippy_lints` and gathers all lints inside
 +pub fn gather_all() -> impl Iterator<Item = Lint> {
 +    lint_files().flat_map(|f| gather_from_file(&f))
 +}
 +
 +fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator<Item = Lint> {
 +    let content = fs::read_to_string(dir_entry.path()).unwrap();
 +    let path = dir_entry.path();
 +    let filename = path.file_stem().unwrap();
 +    let path_buf = path.with_file_name(filename);
 +    let mut rel_path = path_buf
 +        .strip_prefix(clippy_project_root().join("clippy_lints/src"))
 +        .expect("only files in `clippy_lints/src` should be looked at");
 +    // If the lints are stored in mod.rs, we get the module name from
 +    // the containing directory:
 +    if filename == "mod" {
 +        rel_path = rel_path.parent().unwrap();
 +    }
 +
 +    let module = rel_path
 +        .components()
 +        .map(|c| c.as_os_str().to_str().unwrap())
 +        .collect::<Vec<_>>()
 +        .join("::");
 +
 +    parse_contents(&content, &module)
 +}
 +
 +fn parse_contents(content: &str, module: &str) -> impl Iterator<Item = Lint> {
 +    let lints = DEC_CLIPPY_LINT_RE
 +        .captures_iter(content)
 +        .map(|m| Lint::new(&m["name"], &m["cat"], &m["desc"], None, module));
 +    let deprecated = DEC_DEPRECATED_LINT_RE
 +        .captures_iter(content)
 +        .map(|m| Lint::new(&m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), module));
 +    // Removing the `.collect::<Vec<Lint>>().into_iter()` causes some lifetime issues due to the map
 +    lints.chain(deprecated).collect::<Vec<Lint>>().into_iter()
 +}
 +
 +/// Collects all .rs files in the `clippy_lints/src` directory
 +fn lint_files() -> impl Iterator<Item = walkdir::DirEntry> {
 +    // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
 +    // Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`.
 +    let path = clippy_project_root().join("clippy_lints/src");
 +    WalkDir::new(path)
 +        .into_iter()
 +        .filter_map(Result::ok)
 +        .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
 +}
 +
 +/// Whether a file has had its text changed or not
 +#[derive(PartialEq, Debug)]
 +pub struct FileChange {
 +    pub changed: bool,
 +    pub new_lines: String,
 +}
 +
 +/// Replaces a region in a file delimited by two lines matching regexes.
 +///
 +/// `path` is the relative path to the file on which you want to perform the replacement.
 +///
 +/// See `replace_region_in_text` for documentation of the other options.
 +///
 +/// # Panics
 +///
 +/// Panics if the path could not read or then written
 +pub fn replace_region_in_file<F>(
 +    path: &Path,
 +    start: &str,
 +    end: &str,
 +    replace_start: bool,
 +    write_back: bool,
 +    replacements: F,
 +) -> FileChange
 +where
 +    F: FnOnce() -> Vec<String>,
 +{
 +    let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.display(), e));
 +    let file_change = replace_region_in_text(&contents, start, end, replace_start, replacements);
 +
 +    if write_back {
 +        if let Err(e) = fs::write(path, file_change.new_lines.as_bytes()) {
 +            panic!("Cannot write to {}: {}", path.display(), e);
 +        }
 +    }
 +    file_change
 +}
 +
 +/// Replaces a region in a text delimited by two lines matching regexes.
 +///
 +/// * `text` is the input text on which you want to perform the replacement
 +/// * `start` is a `&str` that describes the delimiter line before the region you want to replace.
 +///   As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
 +/// * `end` is a `&str` that describes the delimiter line until where the replacement should happen.
 +///   As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
 +/// * If `replace_start` is true, the `start` delimiter line is replaced as well. The `end`
 +///   delimiter line is never replaced.
 +/// * `replacements` is a closure that has to return a `Vec<String>` which contains the new text.
 +///
 +/// If you want to perform the replacement on files instead of already parsed text,
 +/// use `replace_region_in_file`.
 +///
 +/// # Example
 +///
 +/// ```
 +/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end";
 +/// let result =
 +///     clippy_dev::replace_region_in_text(the_text, "replace_start", "replace_end", false, || {
 +///         vec!["a different".to_string(), "text".to_string()]
 +///     })
 +///     .new_lines;
 +/// assert_eq!("replace_start\na different\ntext\nreplace_end", result);
 +/// ```
 +///
 +/// # Panics
 +///
 +/// Panics if start or end is not valid regex
 +pub fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange
 +where
 +    F: FnOnce() -> Vec<String>,
 +{
 +    let replace_it = replacements();
 +    let mut in_old_region = false;
 +    let mut found = false;
 +    let mut new_lines = vec![];
 +    let start = Regex::new(start).unwrap();
 +    let end = Regex::new(end).unwrap();
 +
 +    for line in text.lines() {
 +        if in_old_region {
 +            if end.is_match(line) {
 +                in_old_region = false;
 +                new_lines.extend(replace_it.clone());
 +                new_lines.push(line.to_string());
 +            }
 +        } else if start.is_match(line) {
 +            if !replace_start {
 +                new_lines.push(line.to_string());
 +            }
 +            in_old_region = true;
 +            found = true;
 +        } else {
 +            new_lines.push(line.to_string());
 +        }
 +    }
 +
 +    if !found {
 +        // This happens if the provided regex in `clippy_dev/src/main.rs` does not match in the
 +        // given text or file. Most likely this is an error on the programmer's side and the Regex
 +        // is incorrect.
 +        eprintln!("error: regex \n{:?}\ndoesn't match. You may have to update it.", start);
 +        std::process::exit(1);
 +    }
 +
 +    let mut new_lines = new_lines.join("\n");
 +    if text.ends_with('\n') {
 +        new_lines.push('\n');
 +    }
 +    let changed = new_lines != text;
 +    FileChange { changed, new_lines }
 +}
 +
 +/// Returns the path to the Clippy project directory
 +///
 +/// # Panics
 +///
 +/// Panics if the current directory could not be retrieved, there was an error reading any of the
 +/// Cargo.toml files or ancestor directory is the clippy root directory
 +#[must_use]
 +pub fn clippy_project_root() -> PathBuf {
 +    let current_dir = std::env::current_dir().unwrap();
 +    for path in current_dir.ancestors() {
 +        let result = std::fs::read_to_string(path.join("Cargo.toml"));
 +        if let Err(err) = &result {
 +            if err.kind() == std::io::ErrorKind::NotFound {
 +                continue;
 +            }
 +        }
 +
 +        let content = result.unwrap();
 +        if content.contains("[package]\nname = \"clippy\"") {
 +            return path.to_path_buf();
 +        }
 +    }
 +    panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
 +}
 +
 +#[test]
 +fn test_parse_contents() {
 +    let result: Vec<Lint> = parse_contents(
 +        r#"
 +declare_clippy_lint! {
 +    pub PTR_ARG,
 +    style,
 +    "really long \
 +     text"
 +}
 +
 +declare_clippy_lint!{
 +    pub DOC_MARKDOWN,
 +    pedantic,
 +    "single line"
 +}
 +
 +/// some doc comment
 +declare_deprecated_lint! {
 +    pub SHOULD_ASSERT_EQ,
 +    "`assert!()` will be more flexible with RFC 2011"
 +}
 +    "#,
 +        "module_name",
 +    )
 +    .collect();
 +
 +    let expected = vec![
 +        Lint::new("ptr_arg", "style", "really long text", None, "module_name"),
 +        Lint::new("doc_markdown", "pedantic", "single line", None, "module_name"),
 +        Lint::new(
 +            "should_assert_eq",
 +            "Deprecated",
 +            "`assert!()` will be more flexible with RFC 2011",
 +            Some("`assert!()` will be more flexible with RFC 2011"),
 +            "module_name",
 +        ),
 +    ];
 +    assert_eq!(expected, result);
 +}
 +
 +#[test]
 +fn test_replace_region() {
 +    let text = "\nabc\n123\n789\ndef\nghi";
 +    let expected = FileChange {
 +        changed: true,
 +        new_lines: "\nabc\nhello world\ndef\nghi".to_string(),
 +    };
 +    let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || {
 +        vec!["hello world".to_string()]
 +    });
 +    assert_eq!(expected, result);
 +}
 +
 +#[test]
 +fn test_replace_region_with_start() {
 +    let text = "\nabc\n123\n789\ndef\nghi";
 +    let expected = FileChange {
 +        changed: true,
 +        new_lines: "\nhello world\ndef\nghi".to_string(),
 +    };
 +    let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || {
 +        vec!["hello world".to_string()]
 +    });
 +    assert_eq!(expected, result);
 +}
 +
 +#[test]
 +fn test_replace_region_no_changes() {
 +    let text = "123\n456\n789";
 +    let expected = FileChange {
 +        changed: false,
 +        new_lines: "123\n456\n789".to_string(),
 +    };
 +    let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, Vec::new);
 +    assert_eq!(expected, result);
 +}
 +
 +#[test]
 +fn test_usable_lints() {
 +    let lints = vec![
 +        Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"),
 +        Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"),
 +        Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"),
 +        Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name"),
 +    ];
 +    let expected = vec![Lint::new(
 +        "should_assert_eq2",
 +        "Not Deprecated",
 +        "abc",
 +        None,
 +        "module_name",
 +    )];
 +    assert_eq!(expected, Lint::usable_lints(&lints));
 +}
 +
 +#[test]
 +fn test_by_lint_group() {
 +    let lints = vec![
 +        Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
 +        Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
 +        Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
 +    ];
 +    let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
 +    expected.insert(
 +        "group1".to_string(),
 +        vec![
 +            Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
 +            Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
 +        ],
 +    );
 +    expected.insert(
 +        "group2".to_string(),
 +        vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")],
 +    );
 +    assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
 +}
 +
 +#[test]
 +fn test_gen_changelog_lint_list() {
 +    let lints = vec![
 +        Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
 +        Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
 +    ];
 +    let expected = vec![
 +        format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK.to_string()),
 +        format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK.to_string()),
 +    ];
 +    assert_eq!(expected, gen_changelog_lint_list(lints.iter()));
 +}
 +
 +#[test]
 +fn test_gen_deprecated() {
 +    let lints = vec![
 +        Lint::new(
 +            "should_assert_eq",
 +            "group1",
 +            "abc",
 +            Some("has been superseded by should_assert_eq2"),
 +            "module_name",
 +        ),
 +        Lint::new(
 +            "another_deprecated",
 +            "group2",
 +            "abc",
 +            Some("will be removed"),
 +            "module_name",
 +        ),
 +    ];
 +    let expected: Vec<String> = vec![
 +        "    store.register_removed(",
 +        "        \"clippy::should_assert_eq\",",
 +        "        \"has been superseded by should_assert_eq2\",",
 +        "    );",
 +        "    store.register_removed(",
 +        "        \"clippy::another_deprecated\",",
 +        "        \"will be removed\",",
 +        "    );",
 +    ]
 +    .into_iter()
 +    .map(String::from)
 +    .collect();
 +    assert_eq!(expected, gen_deprecated(lints.iter()));
 +}
 +
 +#[test]
 +#[should_panic]
 +fn test_gen_deprecated_fail() {
 +    let lints = vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")];
 +    let _deprecated_lints = gen_deprecated(lints.iter());
 +}
 +
 +#[test]
 +fn test_gen_modules_list() {
 +    let lints = vec![
 +        Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
 +        Lint::new("incorrect_stuff", "group3", "abc", None, "another_module"),
 +    ];
 +    let expected = vec!["mod another_module;".to_string(), "mod module_name;".to_string()];
 +    assert_eq!(expected, gen_modules_list(lints.iter()));
 +}
 +
 +#[test]
 +fn test_gen_lint_group_list() {
 +    let lints = vec![
 +        Lint::new("abc", "group1", "abc", None, "module_name"),
 +        Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
 +        Lint::new("internal", "internal_style", "abc", None, "module_name"),
 +    ];
 +    let expected = vec![
 +        "        LintId::of(module_name::ABC),".to_string(),
 +        "        LintId::of(module_name::INTERNAL),".to_string(),
 +        "        LintId::of(module_name::SHOULD_ASSERT_EQ),".to_string(),
 +    ];
 +    assert_eq!(expected, gen_lint_group_list(lints.iter()));
 +}
index ff324ff6ee6fff5b6a38edc9aaefc6ee09df0f34,0000000000000000000000000000000000000000..8fdeba9842af3e6f80070aa055e2a28dd70ff000
mode 100644,000000..100644
--- /dev/null
@@@ -1,224 -1,0 +1,217 @@@
- use clippy_dev::{bless, fmt, new_lint, serve, setup, stderr_length_check, 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::{App, AppSettings, Arg, ArgMatches, SubCommand};
-         ("limit_stderr_length", _) => {
-             stderr_length_check::check();
-         },
++use clippy_dev::{bless, fmt, new_lint, serve, setup, update_lints};
 +fn main() {
 +    let matches = get_clap_config();
 +
 +    match matches.subcommand() {
 +        ("bless", Some(matches)) => {
 +            bless::bless(matches.is_present("ignore-timestamp"));
 +        },
 +        ("fmt", Some(matches)) => {
 +            fmt::run(matches.is_present("check"), matches.is_present("verbose"));
 +        },
 +        ("update_lints", Some(matches)) => {
 +            if matches.is_present("print-only") {
 +                update_lints::print_lints();
 +            } else if matches.is_present("check") {
 +                update_lints::run(update_lints::UpdateMode::Check);
 +            } else {
 +                update_lints::run(update_lints::UpdateMode::Change);
 +            }
 +        },
 +        ("new_lint", Some(matches)) => {
 +            match new_lint::create(
 +                matches.value_of("pass"),
 +                matches.value_of("name"),
 +                matches.value_of("category"),
 +            ) {
 +                Ok(_) => update_lints::run(update_lints::UpdateMode::Change),
 +                Err(e) => eprintln!("Unable to create lint: {}", e),
 +            }
 +        },
-         .subcommand(
-             SubCommand::with_name("limit_stderr_length")
-                 .about("Ensures that stderr files do not grow longer than a certain amount of lines."),
-         )
 +        ("setup", Some(sub_command)) => match sub_command.subcommand() {
 +            ("intellij", Some(matches)) => setup::intellij::setup_rustc_src(
 +                matches
 +                    .value_of("rustc-repo-path")
 +                    .expect("this field is mandatory and therefore always valid"),
 +            ),
 +            ("git-hook", Some(matches)) => setup::git_hook::install_hook(matches.is_present("force-override")),
 +            ("vscode-tasks", Some(matches)) => setup::vscode::install_tasks(matches.is_present("force-override")),
 +            _ => {},
 +        },
 +        ("remove", Some(sub_command)) => match sub_command.subcommand() {
 +            ("git-hook", Some(_)) => setup::git_hook::remove_hook(),
 +            ("intellij", Some(_)) => setup::intellij::remove_rustc_src(),
 +            ("vscode-tasks", Some(_)) => setup::vscode::remove_tasks(),
 +            _ => {},
 +        },
 +        ("serve", Some(matches)) => {
 +            let port = matches.value_of("port").unwrap().parse().unwrap();
 +            let lint = matches.value_of("lint");
 +            serve::run(port, lint);
 +        },
 +        _ => {},
 +    }
 +}
 +
 +fn get_clap_config<'a>() -> ArgMatches<'a> {
 +    App::new("Clippy developer tooling")
 +        .setting(AppSettings::ArgRequiredElseHelp)
 +        .subcommand(
 +            SubCommand::with_name("bless")
 +                .about("bless the test output changes")
 +                .arg(
 +                    Arg::with_name("ignore-timestamp")
 +                        .long("ignore-timestamp")
 +                        .help("Include files updated before clippy was built"),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("fmt")
 +                .about("Run rustfmt on all projects and tests")
 +                .arg(
 +                    Arg::with_name("check")
 +                        .long("check")
 +                        .help("Use the rustfmt --check option"),
 +                )
 +                .arg(
 +                    Arg::with_name("verbose")
 +                        .short("v")
 +                        .long("verbose")
 +                        .help("Echo commands run"),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("update_lints")
 +                .about("Updates lint registration and information from the source code")
 +                .long_about(
 +                    "Makes sure that:\n \
 +                 * the lint count in README.md is correct\n \
 +                 * the changelog contains markdown link references at the bottom\n \
 +                 * all lint groups include the correct lints\n \
 +                 * lint modules in `clippy_lints/*` are visible in `src/lifb.rs` via `pub mod`\n \
 +                 * all lints are registered in the lint store",
 +                )
 +                .arg(Arg::with_name("print-only").long("print-only").help(
 +                    "Print a table of lints to STDOUT. \
 +                 This does not include deprecated and internal lints. \
 +                 (Does not modify any files)",
 +                ))
 +                .arg(
 +                    Arg::with_name("check")
 +                        .long("check")
 +                        .help("Checks that `cargo dev update_lints` has been run. Used on CI."),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("new_lint")
 +                .about("Create new lint and run `cargo dev update_lints`")
 +                .arg(
 +                    Arg::with_name("pass")
 +                        .short("p")
 +                        .long("pass")
 +                        .help("Specify whether the lint runs during the early or late pass")
 +                        .takes_value(true)
 +                        .possible_values(&["early", "late"])
 +                        .required(true),
 +                )
 +                .arg(
 +                    Arg::with_name("name")
 +                        .short("n")
 +                        .long("name")
 +                        .help("Name of the new lint in snake case, ex: fn_too_long")
 +                        .takes_value(true)
 +                        .required(true),
 +                )
 +                .arg(
 +                    Arg::with_name("category")
 +                        .short("c")
 +                        .long("category")
 +                        .help("What category the lint belongs to")
 +                        .default_value("nursery")
 +                        .possible_values(&[
 +                            "style",
 +                            "correctness",
 +                            "suspicious",
 +                            "complexity",
 +                            "perf",
 +                            "pedantic",
 +                            "restriction",
 +                            "cargo",
 +                            "nursery",
 +                            "internal",
 +                            "internal_warn",
 +                        ])
 +                        .takes_value(true),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("setup")
 +                .about("Support for setting up your personal development environment")
 +                .setting(AppSettings::ArgRequiredElseHelp)
 +                .subcommand(
 +                    SubCommand::with_name("intellij")
 +                        .about("Alter dependencies so Intellij Rust can find rustc internals")
 +                        .arg(
 +                            Arg::with_name("rustc-repo-path")
 +                                .long("repo-path")
 +                                .short("r")
 +                                .help("The path to a rustc repo that will be used for setting the dependencies")
 +                                .takes_value(true)
 +                                .value_name("path")
 +                                .required(true),
 +                        ),
 +                )
 +                .subcommand(
 +                    SubCommand::with_name("git-hook")
 +                        .about("Add a pre-commit git hook that formats your code to make it look pretty")
 +                        .arg(
 +                            Arg::with_name("force-override")
 +                                .long("force-override")
 +                                .short("f")
 +                                .help("Forces the override of an existing git pre-commit hook")
 +                                .required(false),
 +                        ),
 +                )
 +                .subcommand(
 +                    SubCommand::with_name("vscode-tasks")
 +                        .about("Add several tasks to vscode for formatting, validation and testing")
 +                        .arg(
 +                            Arg::with_name("force-override")
 +                                .long("force-override")
 +                                .short("f")
 +                                .help("Forces the override of existing vscode tasks")
 +                                .required(false),
 +                        ),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("remove")
 +                .about("Support for undoing changes done by the setup command")
 +                .setting(AppSettings::ArgRequiredElseHelp)
 +                .subcommand(SubCommand::with_name("git-hook").about("Remove any existing pre-commit git hook"))
 +                .subcommand(SubCommand::with_name("vscode-tasks").about("Remove any existing vscode tasks"))
 +                .subcommand(
 +                    SubCommand::with_name("intellij")
 +                        .about("Removes rustc source paths added via `cargo dev setup intellij`"),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("serve")
 +                .about("Launch a local 'ALL the Clippy Lints' website in a browser")
 +                .arg(
 +                    Arg::with_name("port")
 +                        .long("port")
 +                        .short("p")
 +                        .help("Local port for the http server")
 +                        .default_value("8000")
 +                        .validator_os(serve::validate_port),
 +                )
 +                .arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")),
 +        )
 +        .get_matches()
 +}
index a3b92e1faa1a9d908932a411c21e4f098812f017,0000000000000000000000000000000000000000..3c28024bf926a4e1243ce0d52910223d28da0562
mode 100644,000000..100644
--- /dev/null
@@@ -1,40 -1,0 +1,38 @@@
- # begin automatic update
- version = "0.1.56"
- # end automatic update
 +[package]
 +name = "clippy_lints"
++version = "0.1.57"
 +description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 +repository = "https://github.com/rust-lang/rust-clippy"
 +readme = "README.md"
 +license = "MIT OR Apache-2.0"
 +keywords = ["clippy", "lint", "plugin"]
 +edition = "2018"
 +
 +[dependencies]
 +cargo_metadata = "0.12"
 +clippy_utils = { path = "../clippy_utils" }
 +if_chain = "1.0.0"
 +itertools = "0.9"
 +pulldown-cmark = { version = "0.8", default-features = false }
 +quine-mc_cluskey = "0.2.2"
 +regex-syntax = "0.6"
 +serde = { version = "1.0", features = ["derive"] }
 +serde_json = { version = "1.0", optional = true }
 +toml = "0.5.3"
 +unicode-normalization = "0.1"
 +unicode-script = { version = "0.5.3", default-features = false }
 +semver = "0.11"
 +rustc-semver = "1.1.0"
 +# NOTE: cargo requires serde feat in its url dep
 +# see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
 +url = { version = "2.1.0", features = ["serde"] }
 +
 +[features]
 +deny-warnings = ["clippy_utils/deny-warnings"]
 +# build clippy with internal lints enabled, off by default
 +internal-lints = ["clippy_utils/internal-lints"]
 +metadata-collector-lint = ["serde_json", "clippy_utils/metadata-collector-lint"]
 +
 +[package.metadata.rust-analyzer]
 +# This crate uses #[feature(rustc_private)]
 +rustc_private = true
index 6100f4e435a8a9b08e6f658bcf5014d8a8186d7c,0000000000000000000000000000000000000000..fb54ac1ec511981595402d73dfd3bba329b3e903
mode 100644,000000..100644
--- /dev/null
@@@ -1,117 -1,0 +1,133 @@@
- use clippy_utils::diagnostics::span_lint;
++use clippy_utils::diagnostics::span_lint_and_help;
++use clippy_utils::{meets_msrv, msrvs};
 +use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
 +use rustc_hir::{Expr, ExprKind};
- use rustc_lint::{LateContext, LateLintPass};
- use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_semver::RustcVersion;
++use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol;
 +use std::f64::consts as f64;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for floating point literals that approximate
 +    /// constants which are defined in
 +    /// [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)
 +    /// or
 +    /// [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),
 +    /// respectively, suggesting to use the predefined constant.
 +    ///
 +    /// ### Why is this bad?
 +    /// Usually, the definition in the standard library is more
 +    /// precise than what people come up with. If you find that your definition is
 +    /// actually more precise, please [file a Rust
 +    /// issue](https://github.com/rust-lang/rust/issues).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 3.14;
 +    /// let y = 1_f64 / x;
 +    /// ```
 +    /// Use predefined constants instead:
 +    /// ```rust
 +    /// let x = std::f32::consts::PI;
 +    /// let y = std::f64::consts::FRAC_1_PI;
 +    /// ```
 +    pub APPROX_CONSTANT,
 +    correctness,
 +    "the approximate of a known float constant (in `std::fXX::consts`)"
 +}
 +
- // Tuples are of the form (constant, name, min_digits)
- const KNOWN_CONSTS: [(f64, &str, usize); 18] = [
-     (f64::E, "E", 4),
-     (f64::FRAC_1_PI, "FRAC_1_PI", 4),
-     (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5),
-     (f64::FRAC_2_PI, "FRAC_2_PI", 5),
-     (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5),
-     (f64::FRAC_PI_2, "FRAC_PI_2", 5),
-     (f64::FRAC_PI_3, "FRAC_PI_3", 5),
-     (f64::FRAC_PI_4, "FRAC_PI_4", 5),
-     (f64::FRAC_PI_6, "FRAC_PI_6", 5),
-     (f64::FRAC_PI_8, "FRAC_PI_8", 5),
-     (f64::LN_10, "LN_10", 5),
-     (f64::LN_2, "LN_2", 5),
-     (f64::LOG10_E, "LOG10_E", 5),
-     (f64::LOG2_E, "LOG2_E", 5),
-     (f64::LOG2_10, "LOG2_10", 5),
-     (f64::LOG10_2, "LOG10_2", 5),
-     (f64::PI, "PI", 3),
-     (f64::SQRT_2, "SQRT_2", 5),
++// Tuples are of the form (constant, name, min_digits, msrv)
++const KNOWN_CONSTS: [(f64, &str, usize, Option<RustcVersion>); 19] = [
++    (f64::E, "E", 4, None),
++    (f64::FRAC_1_PI, "FRAC_1_PI", 4, None),
++    (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5, None),
++    (f64::FRAC_2_PI, "FRAC_2_PI", 5, None),
++    (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5, None),
++    (f64::FRAC_PI_2, "FRAC_PI_2", 5, None),
++    (f64::FRAC_PI_3, "FRAC_PI_3", 5, None),
++    (f64::FRAC_PI_4, "FRAC_PI_4", 5, None),
++    (f64::FRAC_PI_6, "FRAC_PI_6", 5, None),
++    (f64::FRAC_PI_8, "FRAC_PI_8", 5, None),
++    (f64::LN_2, "LN_2", 5, None),
++    (f64::LN_10, "LN_10", 5, None),
++    (f64::LOG2_10, "LOG2_10", 5, Some(msrvs::LOG2_10)),
++    (f64::LOG2_E, "LOG2_E", 5, None),
++    (f64::LOG10_2, "LOG10_2", 5, Some(msrvs::LOG10_2)),
++    (f64::LOG10_E, "LOG10_E", 5, None),
++    (f64::PI, "PI", 3, None),
++    (f64::SQRT_2, "SQRT_2", 5, None),
++    (f64::TAU, "TAU", 3, Some(msrvs::TAU)),
 +];
 +
- declare_lint_pass!(ApproxConstant => [APPROX_CONSTANT]);
++pub struct ApproxConstant {
++    msrv: Option<RustcVersion>,
++}
 +
- impl<'tcx> LateLintPass<'tcx> for ApproxConstant {
-     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-         if let ExprKind::Lit(lit) = &e.kind {
-             check_lit(cx, &lit.node, e);
++impl ApproxConstant {
++    #[must_use]
++    pub fn new(msrv: Option<RustcVersion>) -> Self {
++        Self { msrv }
++    }
++
++    fn check_lit(&self, cx: &LateContext<'_>, lit: &LitKind, e: &Expr<'_>) {
++        match *lit {
++            LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty {
++                FloatTy::F32 => self.check_known_consts(cx, e, s, "f32"),
++                FloatTy::F64 => self.check_known_consts(cx, e, s, "f64"),
++            },
++            LitKind::Float(s, LitFloatType::Unsuffixed) => self.check_known_consts(cx, e, s, "f{32, 64}"),
++            _ => (),
 +        }
 +    }
- }
 +
- fn check_lit(cx: &LateContext<'_>, lit: &LitKind, e: &Expr<'_>) {
-     match *lit {
-         LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty {
-             FloatTy::F32 => check_known_consts(cx, e, s, "f32"),
-             FloatTy::F64 => check_known_consts(cx, e, s, "f64"),
-         },
-         LitKind::Float(s, LitFloatType::Unsuffixed) => check_known_consts(cx, e, s, "f{32, 64}"),
-         _ => (),
++    fn check_known_consts(&self, cx: &LateContext<'_>, e: &Expr<'_>, s: symbol::Symbol, module: &str) {
++        let s = s.as_str();
++        if s.parse::<f64>().is_ok() {
++            for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS {
++                if is_approx_const(constant, &s, min_digits)
++                    && msrv.as_ref().map_or(true, |msrv| meets_msrv(self.msrv.as_ref(), msrv))
++                {
++                    span_lint_and_help(
++                        cx,
++                        APPROX_CONSTANT,
++                        e.span,
++                        &format!("approximate value of `{}::consts::{}` found", module, &name),
++                        None,
++                        "consider using the constant directly",
++                    );
++                    return;
++                }
++            }
++        }
 +    }
 +}
 +
- fn check_known_consts(cx: &LateContext<'_>, e: &Expr<'_>, s: symbol::Symbol, module: &str) {
-     let s = s.as_str();
-     if s.parse::<f64>().is_ok() {
-         for &(constant, name, min_digits) in &KNOWN_CONSTS {
-             if is_approx_const(constant, &s, min_digits) {
-                 span_lint(
-                     cx,
-                     APPROX_CONSTANT,
-                     e.span,
-                     &format!(
-                         "approximate value of `{}::consts::{}` found. \
-                          Consider using it directly",
-                         module, &name
-                     ),
-                 );
-                 return;
-             }
++impl_lint_pass!(ApproxConstant => [APPROX_CONSTANT]);
++
++impl<'tcx> LateLintPass<'tcx> for ApproxConstant {
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
++        if let ExprKind::Lit(lit) = &e.kind {
++            self.check_lit(cx, &lit.node, e);
 +        }
 +    }
++
++    extract_msrv_attr!(LateContext);
 +}
 +
 +/// Returns `false` if the number of significant figures in `value` are
 +/// less than `min_digits`; otherwise, returns true if `value` is equal
 +/// to `constant`, rounded to the number of digits present in `value`.
 +#[must_use]
 +fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool {
 +    if value.len() <= min_digits {
 +        false
 +    } else if constant.to_string().starts_with(value) {
 +        // The value is a truncated constant
 +        true
 +    } else {
 +        let round_const = format!("{:.*}", value.len() - 2, constant);
 +        value == round_const
 +    }
 +}
index 891e865b245dda516c07987e4a5ce3827d0a094a,0000000000000000000000000000000000000000..d834a1d317a0f1d7e9d3344fb764cc3a2ab866d4
mode 100644,000000..100644
--- /dev/null
@@@ -1,149 -1,0 +1,149 @@@
-         if let ExprKind::Unary(UnOp::Not, ref expr) = cond.kind;
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::higher;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call};
 +use if_chain::if_chain;
 +use rustc_hir::{Expr, ExprKind, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `assert!(true)` and `assert!(false)` calls.
 +    ///
 +    /// ### Why is this bad?
 +    /// Will be optimized out by the compiler or should probably be replaced by a
 +    /// `panic!()` or `unreachable!()`
 +    ///
 +    /// ### Known problems
 +    /// None
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// assert!(false)
 +    /// assert!(true)
 +    /// const B: bool = false;
 +    /// assert!(B)
 +    /// ```
 +    pub ASSERTIONS_ON_CONSTANTS,
 +    style,
 +    "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`"
 +}
 +
 +declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        let lint_true = |is_debug: bool| {
 +            span_lint_and_help(
 +                cx,
 +                ASSERTIONS_ON_CONSTANTS,
 +                e.span,
 +                if is_debug {
 +                    "`debug_assert!(true)` will be optimized out by the compiler"
 +                } else {
 +                    "`assert!(true)` will be optimized out by the compiler"
 +                },
 +                None,
 +                "remove it",
 +            );
 +        };
 +        let lint_false_without_message = || {
 +            span_lint_and_help(
 +                cx,
 +                ASSERTIONS_ON_CONSTANTS,
 +                e.span,
 +                "`assert!(false)` should probably be replaced",
 +                None,
 +                "use `panic!()` or `unreachable!()`",
 +            );
 +        };
 +        let lint_false_with_message = |panic_message: String| {
 +            span_lint_and_help(
 +                cx,
 +                ASSERTIONS_ON_CONSTANTS,
 +                e.span,
 +                &format!("`assert!(false, {})` should probably be replaced", panic_message),
 +                None,
 +                &format!("use `panic!({})` or `unreachable!({})`", panic_message, panic_message),
 +            );
 +        };
 +
 +        if let Some(debug_assert_span) = is_expn_of(e.span, "debug_assert") {
 +            if debug_assert_span.from_expansion() {
 +                return;
 +            }
 +            if_chain! {
 +                if let ExprKind::Unary(_, lit) = e.kind;
 +                if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), lit);
 +                if is_true;
 +                then {
 +                    lint_true(true);
 +                }
 +            };
 +        } else if let Some(assert_span) = is_direct_expn_of(e.span, "assert") {
 +            if assert_span.from_expansion() {
 +                return;
 +            }
 +            if let Some(assert_match) = match_assert_with_message(cx, e) {
 +                match assert_match {
 +                    // matched assert but not message
 +                    AssertKind::WithoutMessage(false) => lint_false_without_message(),
 +                    AssertKind::WithoutMessage(true) | AssertKind::WithMessage(_, true) => lint_true(false),
 +                    AssertKind::WithMessage(panic_message, false) => lint_false_with_message(panic_message),
 +                };
 +            }
 +        }
 +    }
 +}
 +
 +/// Result of calling `match_assert_with_message`.
 +enum AssertKind {
 +    WithMessage(String, bool),
 +    WithoutMessage(bool),
 +}
 +
 +/// Check if the expression matches
 +///
 +/// ```rust,ignore
 +/// if !c {
 +///   {
 +///     ::std::rt::begin_panic(message, _)
 +///   }
 +/// }
 +/// ```
 +///
 +/// where `message` is any expression and `c` is a constant bool.
 +fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<AssertKind> {
 +    if_chain! {
 +        if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr);
++        if let ExprKind::Unary(UnOp::Not, expr) = cond.kind;
 +        // bind the first argument of the `assert!` macro
 +        if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr);
 +        // block
 +        if let ExprKind::Block(block, _) = then.kind;
 +        if block.stmts.is_empty();
 +        if let Some(block_expr) = &block.expr;
 +        // inner block is optional. unwrap it if it exists, or use the expression as is otherwise.
 +        if let Some(begin_panic_call) = match block_expr.kind {
 +            ExprKind::Block(inner_block, _) => &inner_block.expr,
 +            _ => &block.expr,
 +        };
 +        // function call
 +        if let Some(arg) = match_panic_call(cx, begin_panic_call);
 +        // bind the second argument of the `assert!` macro if it exists
 +        if let panic_message = snippet_opt(cx, arg.span);
 +        // second argument of begin_panic is irrelevant
 +        // as is the second match arm
 +        then {
 +            // an empty message occurs when it was generated by the macro
 +            // (and not passed by the user)
 +            return panic_message
 +                .filter(|msg| !msg.is_empty())
 +                .map(|msg| AssertKind::WithMessage(msg, is_true))
 +                .or(Some(AssertKind::WithoutMessage(is_true)));
 +        }
 +    }
 +    None
 +}
index 51d95cc6f0b10b485c2f94e3fd10b200e9bb8442,0000000000000000000000000000000000000000..47e5b0d583dabcee654d1613e6057a06459f5112
mode 100644,000000..100644
--- /dev/null
@@@ -1,162 -1,0 +1,162 @@@
-                 if let ExprKind::MethodCall(_, _, args, _) = parent.kind;
-                 let caller = self.cx.typeck_results().expr_ty(&args[0]);
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 +use clippy_utils::higher;
 +use clippy_utils::source::snippet_block_with_applicability;
 +use clippy_utils::ty::implements_trait;
 +use clippy_utils::{differing_macro_contexts, get_parent_expr};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 +use rustc_hir::{BlockCheckMode, Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `if` conditions that use blocks containing an
 +    /// expression, statements or conditions that use closures with blocks.
 +    ///
 +    /// ### Why is this bad?
 +    /// Style, using blocks in the condition makes it hard to read.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// // Bad
 +    /// if { true } { /* ... */ }
 +    ///
 +    /// // Good
 +    /// if true { /* ... */ }
 +    /// ```
 +    ///
 +    /// // or
 +    ///
 +    /// ```rust
 +    /// # fn somefunc() -> bool { true };
 +    /// // Bad
 +    /// if { let x = somefunc(); x } { /* ... */ }
 +    ///
 +    /// // Good
 +    /// let res = { let x = somefunc(); x };
 +    /// if res { /* ... */ }
 +    /// ```
 +    pub BLOCKS_IN_IF_CONDITIONS,
 +    style,
 +    "useless or complex blocks that can be eliminated in conditions"
 +}
 +
 +declare_lint_pass!(BlocksInIfConditions => [BLOCKS_IN_IF_CONDITIONS]);
 +
 +struct ExVisitor<'a, 'tcx> {
 +    found_block: Option<&'tcx Expr<'tcx>>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 +        if let ExprKind::Closure(_, _, eid, _, _) = expr.kind {
 +            // do not lint if the closure is called using an iterator (see #1141)
 +            if_chain! {
 +                if let Some(parent) = get_parent_expr(self.cx, expr);
++                if let ExprKind::MethodCall(_, _, [self_arg, ..], _) = &parent.kind;
++                let caller = self.cx.typeck_results().expr_ty(self_arg);
 +                if let Some(iter_id) = self.cx.tcx.get_diagnostic_item(sym::Iterator);
 +                if implements_trait(self.cx, caller, iter_id, &[]);
 +                then {
 +                    return;
 +                }
 +            }
 +
 +            let body = self.cx.tcx.hir().body(eid);
 +            let ex = &body.value;
 +            if matches!(ex.kind, ExprKind::Block(_, _)) && !body.value.span.from_expansion() {
 +                self.found_block = Some(ex);
 +                return;
 +            }
 +        }
 +        walk_expr(self, expr);
 +    }
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
 +const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition";
 +const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \
 +                                    instead, move the block or closure higher and bind it with a `let`";
 +
 +impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
 +        if let Some(higher::If { cond, .. }) = higher::If::hir(expr) {
 +            if let ExprKind::Block(block, _) = &cond.kind {
 +                if block.rules == BlockCheckMode::DefaultBlock {
 +                    if block.stmts.is_empty() {
 +                        if let Some(ex) = &block.expr {
 +                            // don't dig into the expression here, just suggest that they remove
 +                            // the block
 +                            if expr.span.from_expansion() || differing_macro_contexts(expr.span, ex.span) {
 +                                return;
 +                            }
 +                            let mut applicability = Applicability::MachineApplicable;
 +                            span_lint_and_sugg(
 +                                cx,
 +                                BLOCKS_IN_IF_CONDITIONS,
 +                                cond.span,
 +                                BRACED_EXPR_MESSAGE,
 +                                "try",
 +                                format!(
 +                                    "{}",
 +                                    snippet_block_with_applicability(
 +                                        cx,
 +                                        ex.span,
 +                                        "..",
 +                                        Some(expr.span),
 +                                        &mut applicability
 +                                    )
 +                                ),
 +                                applicability,
 +                            );
 +                        }
 +                    } else {
 +                        let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
 +                        if span.from_expansion() || differing_macro_contexts(expr.span, span) {
 +                            return;
 +                        }
 +                        // move block higher
 +                        let mut applicability = Applicability::MachineApplicable;
 +                        span_lint_and_sugg(
 +                            cx,
 +                            BLOCKS_IN_IF_CONDITIONS,
 +                            expr.span.with_hi(cond.span.hi()),
 +                            COMPLEX_BLOCK_MESSAGE,
 +                            "try",
 +                            format!(
 +                                "let res = {}; if res",
 +                                snippet_block_with_applicability(
 +                                    cx,
 +                                    block.span,
 +                                    "..",
 +                                    Some(expr.span),
 +                                    &mut applicability
 +                                ),
 +                            ),
 +                            applicability,
 +                        );
 +                    }
 +                }
 +            } else {
 +                let mut visitor = ExVisitor { found_block: None, cx };
 +                walk_expr(&mut visitor, cond);
 +                if let Some(block) = visitor.found_block {
 +                    span_lint(cx, BLOCKS_IN_IF_CONDITIONS, block.span, COMPLEX_BLOCK_MESSAGE);
 +                }
 +            }
 +        }
 +    }
 +}
index 8d3f68565b2234b4c23ef4955331015055bce0b9,0000000000000000000000000000000000000000..cdc192a47e48abebb325f95437ce48a1ee00dee6
mode 100644,000000..100644
--- /dev/null
@@@ -1,74 -1,0 +1,108 @@@
- use clippy_utils::diagnostics::span_lint_and_sugg;
- use clippy_utils::{ast_utils, is_direct_expn_of};
- use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
++use clippy_utils::{diagnostics::span_lint_and_sugg, higher, is_direct_expn_of, ty::implements_trait};
++use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
- use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_hir::{Expr, ExprKind, Lit};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::symbol::Ident;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns about boolean comparisons in assert-like macros.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is shorter to use the equivalent.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// assert_eq!("a".is_empty(), false);
 +    /// assert_ne!("a".is_empty(), true);
 +    ///
 +    /// // Good
 +    /// assert!(!"a".is_empty());
 +    /// ```
 +    pub BOOL_ASSERT_COMPARISON,
 +    style,
 +    "Using a boolean as comparison value in an assert_* macro when there is no need"
 +}
 +
 +declare_lint_pass!(BoolAssertComparison => [BOOL_ASSERT_COMPARISON]);
 +
- fn is_bool_lit(e: &Expr) -> bool {
++fn is_bool_lit(e: &Expr<'_>) -> bool {
 +    matches!(
 +        e.kind,
 +        ExprKind::Lit(Lit {
-             kind: LitKind::Bool(_),
++            node: LitKind::Bool(_),
 +            ..
 +        })
 +    ) && !e.span.from_expansion()
 +}
 +
- impl EarlyLintPass for BoolAssertComparison {
-     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
++fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
++    let ty = cx.typeck_results().expr_ty(e);
++
++    cx.tcx
++        .lang_items()
++        .not_trait()
++        .filter(|trait_id| implements_trait(cx, ty, *trait_id, &[]))
++        .and_then(|trait_id| {
++            cx.tcx.associated_items(trait_id).find_by_name_and_kind(
++                cx.tcx,
++                Ident::from_str("Output"),
++                ty::AssocKind::Type,
++                trait_id,
++            )
++        })
++        .map_or(false, |assoc_item| {
++            let proj = cx.tcx.mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(ty, &[]));
++            let nty = cx.tcx.normalize_erasing_regions(cx.param_env, proj);
++
++            nty.is_bool()
++        })
++}
++
++impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        let macros = ["assert_eq", "debug_assert_eq"];
 +        let inverted_macros = ["assert_ne", "debug_assert_ne"];
 +
 +        for mac in macros.iter().chain(inverted_macros.iter()) {
-             if let Some(span) = is_direct_expn_of(e.span, mac) {
-                 if let Some([a, b]) = ast_utils::extract_assert_macro_args(e) {
-                     let nb_bool_args = is_bool_lit(a) as usize + is_bool_lit(b) as usize;
++            if let Some(span) = is_direct_expn_of(expr.span, mac) {
++                if let Some(args) = higher::extract_assert_macro_args(expr) {
++                    if let [a, b, ..] = args[..] {
++                        let nb_bool_args = is_bool_lit(a) as usize + is_bool_lit(b) as usize;
++
++                        if nb_bool_args != 1 {
++                            // If there are two boolean arguments, we definitely don't understand
++                            // what's going on, so better leave things as is...
++                            //
++                            // Or there is simply no boolean and then we can leave things as is!
++                            return;
++                        }
 +
-                     if nb_bool_args != 1 {
-                         // If there are two boolean arguments, we definitely don't understand
-                         // what's going on, so better leave things as is...
-                         //
-                         // Or there is simply no boolean and then we can leave things as is!
++                        if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) {
++                            // At this point the expression which is not a boolean
++                            // literal does not implement Not trait with a bool output,
++                            // so we cannot suggest to rewrite our code
++                            return;
++                        }
++
++                        let non_eq_mac = &mac[..mac.len() - 3];
++                        span_lint_and_sugg(
++                            cx,
++                            BOOL_ASSERT_COMPARISON,
++                            span,
++                            &format!("used `{}!` with a literal bool", mac),
++                            "replace it with",
++                            format!("{}!(..)", non_eq_mac),
++                            Applicability::MaybeIncorrect,
++                        );
 +                        return;
 +                    }
-                     let non_eq_mac = &mac[..mac.len() - 3];
-                     span_lint_and_sugg(
-                         cx,
-                         BOOL_ASSERT_COMPARISON,
-                         span,
-                         &format!("used `{}!` with a literal bool", mac),
-                         "replace it with",
-                         format!("{}!(..)", non_eq_mac),
-                         Applicability::MaybeIncorrect,
-                     );
-                     return;
 +                }
 +            }
 +        }
 +    }
 +}
index c444984bc133a47ab0fb5450055bed06fb80d14c,0000000000000000000000000000000000000000..a07cd5e5f4e53940be988f8fe12b8f968174d755
mode 100644,000000..100644
--- /dev/null
@@@ -1,96 -1,0 +1,96 @@@
- use clippy_utils::visitors::LocalUsedVisitor;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::match_type;
-             if !LocalUsedVisitor::new(cx, arg_id).check_expr(needle);
++use clippy_utils::visitors::is_local_used;
 +use clippy_utils::{path_to_local_id, paths, peel_ref_operators, remove_blocks, strip_pat_refs};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::{self, UintTy};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for naive byte counts
 +    ///
 +    /// ### Why is this bad?
 +    /// The [`bytecount`](https://crates.io/crates/bytecount)
 +    /// crate has methods to count your bytes faster, especially for large slices.
 +    ///
 +    /// ### Known problems
 +    /// If you have predominantly small slices, the
 +    /// `bytecount::count(..)` method may actually be slower. However, if you can
 +    /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
 +    /// faster in those cases.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1_u8];
 +    /// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead
 +    /// ```
 +    pub NAIVE_BYTECOUNT,
 +    pedantic,
 +    "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
 +}
 +
 +declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ByteCount {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::MethodCall(count, _, [count_recv], _) = expr.kind;
 +            if count.ident.name == sym!(count);
 +            if let ExprKind::MethodCall(filter, _, [filter_recv, filter_arg], _) = count_recv.kind;
 +            if filter.ident.name == sym!(filter);
 +            if let ExprKind::Closure(_, _, body_id, _, _) = filter_arg.kind;
 +            let body = cx.tcx.hir().body(body_id);
 +            if let [param] = body.params;
 +            if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
 +            if let ExprKind::Binary(ref op, l, r) = body.value.kind;
 +            if op.node == BinOpKind::Eq;
 +            if match_type(cx,
 +                       cx.typeck_results().expr_ty(filter_recv).peel_refs(),
 +                       &paths::SLICE_ITER);
 +            let operand_is_arg = |expr| {
 +                let expr = peel_ref_operators(cx, remove_blocks(expr));
 +                path_to_local_id(expr, arg_id)
 +            };
 +            let needle = if operand_is_arg(l) {
 +                r
 +            } else if operand_is_arg(r) {
 +                l
 +            } else {
 +                return;
 +            };
 +            if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
++            if !is_local_used(cx, needle, arg_id);
 +            then {
 +                let haystack = if let ExprKind::MethodCall(path, _, args, _) =
 +                        filter_recv.kind {
 +                    let p = path.ident.name;
 +                    if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
 +                        &args[0]
 +                    } else {
 +                        &filter_recv
 +                    }
 +                } else {
 +                    &filter_recv
 +                };
 +                let mut applicability = Applicability::MaybeIncorrect;
 +                span_lint_and_sugg(
 +                    cx,
 +                    NAIVE_BYTECOUNT,
 +                    expr.span,
 +                    "you appear to be counting bytes the naive way",
 +                    "consider using the bytecount crate",
 +                    format!("bytecount::count({}, {})",
 +                            snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
 +                            snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
 +                    applicability,
 +                );
 +            }
 +        };
 +    }
 +}
index 5dcf1824ef0b7e3d387d0507f8f987a0ccf011df,0000000000000000000000000000000000000000..248b35b024ee1aa8a7a0b9f520c3ceab1b86f86f
mode 100644,000000..100644
--- /dev/null
@@@ -1,80 -1,0 +1,80 @@@
-     } else if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind {
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::is_hir_ty_cfg_dependant;
 +use if_chain::if_chain;
 +use rustc_hir::{Expr, ExprKind, GenericArg};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::layout::LayoutOf;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_span::symbol::sym;
 +
 +use super::CAST_PTR_ALIGNMENT;
 +
 +pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +    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),
 +        );
 +        lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
-                     (cx.typeck_results().expr_ty(&args[0]), cx.typeck_results().expr_ty(expr));
++    } else if let ExprKind::MethodCall(method_path, _, [self_arg, ..], _) = &expr.kind {
 +        if_chain! {
 +            if method_path.ident.name == sym!(cast);
 +            if let Some(generic_args) = method_path.args;
 +            if let [GenericArg::Type(cast_to)] = generic_args.args;
 +            // There probably is no obvious reason to do this, just to be consistent with `as` cases.
 +            if !is_hir_ty_cfg_dependant(cx, cast_to);
 +            then {
 +                let (cast_from, cast_to) =
++                    (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
 +                lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
 +            }
 +        }
 +    }
 +}
 +
 +fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) {
 +    if_chain! {
 +        if let ty::RawPtr(from_ptr_ty) = &cast_from.kind();
 +        if let ty::RawPtr(to_ptr_ty) = &cast_to.kind();
 +        if let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty);
 +        if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty);
 +        if from_layout.align.abi < to_layout.align.abi;
 +        // with c_void, we inherently need to trust the user
 +        if !is_c_void(cx, from_ptr_ty.ty);
 +        // when casting from a ZST, we don't know enough to properly lint
 +        if !from_layout.is_zst();
 +        then {
 +            span_lint(
 +                cx,
 +                CAST_PTR_ALIGNMENT,
 +                expr.span,
 +                &format!(
 +                    "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)",
 +                    cast_from,
 +                    cast_to,
 +                    from_layout.align.abi.bytes(),
 +                    to_layout.align.abi.bytes(),
 +                ),
 +            );
 +        }
 +    }
 +}
 +
 +/// Check if the given type is either `core::ffi::c_void` or
 +/// one of the platform specific `libc::<platform>::c_void` of libc.
 +fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
 +    if let ty::Adt(adt, _) = ty.kind() {
 +        let names = cx.get_def_path(adt.did);
 +
 +        if names.is_empty() {
 +            return false;
 +        }
 +        if names[0] == sym::libc || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) {
 +            return true;
 +        }
 +    }
 +    false
 +}
index a42eee53459eba504f63638ba2ef8af761e06bca,0000000000000000000000000000000000000000..a4693fa213bc4a110470615ba12b9912754fe297
mode 100644,000000..100644
--- /dev/null
@@@ -1,209 -1,0 +1,192 @@@
- use clippy_utils::visitors::LocalUsedVisitor;
- use clippy_utils::{higher, is_lang_ctor, is_unit_expr, path_to_local, peel_ref_operators, SpanlessEq};
 +use clippy_utils::diagnostics::span_lint_and_then;
- use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, MatchSource, Pat, PatKind, StmtKind};
++use clippy_utils::higher::IfLetOrMatch;
++use clippy_utils::visitors::is_local_used;
++use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_ref_operators, SpanlessEq};
 +use if_chain::if_chain;
 +use rustc_hir::LangItem::OptionNone;
-             }
++use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{MultiSpan, Span};
 +
 +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,
 +    ///     };
 +    /// }
 +    /// ```
 +    pub COLLAPSIBLE_MATCH,
 +    style,
 +    "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together."
 +}
 +
 +declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
 +        match IfLetOrMatch::parse(cx, expr) {
 +            Some(IfLetOrMatch::Match(_, arms, _)) => {
 +                if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
 +                    for arm in arms {
 +                        check_arm(cx, true, arm.pat, arm.body, arm.guard.as_ref(), Some(els_arm.body));
 +                    }
 +                }
-             }
-             None => {}
++            },
 +            Some(IfLetOrMatch::IfLet(_, pat, body, els)) => {
 +                check_arm(cx, false, pat, body, None, els);
-     outer_else_body: Option<&'tcx Expr<'tcx>>
++            },
++            None => {},
 +        }
 +    }
 +}
 +
 +fn check_arm<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    outer_is_match: bool,
 +    outer_pat: &'tcx Pat<'tcx>,
 +    outer_then_body: &'tcx Expr<'tcx>,
 +    outer_guard: Option<&'tcx Guard<'tcx>>,
-         let mut used_visitor = LocalUsedVisitor::new(cx, binding_id);
-         if outer_guard.map_or(true, |(Guard::If(e) | Guard::IfLet(_, e))| !used_visitor.check_expr(e));
++    outer_else_body: Option<&'tcx Expr<'tcx>>,
 +) {
 +    let inner_expr = strip_singleton_blocks(outer_then_body);
 +    if_chain! {
 +        if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr);
 +        if let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner {
 +            IfLetOrMatch::IfLet(scrutinee, pat, _, els) => Some((scrutinee, pat, els)),
 +            IfLetOrMatch::Match(scrutinee, arms, ..) => if_chain! {
 +                // if there are more than two arms, collapsing would be non-trivial
 +                if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none());
 +                // one of the arms must be "wild-like"
 +                if let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a));
 +                then {
 +                    let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]);
 +                    Some((scrutinee, then.pat, Some(els.body)))
 +                } else {
 +                    None
 +                }
 +            },
 +        };
 +        if outer_pat.span.ctxt() == inner_scrutinee.span.ctxt();
 +        // match expression must be a local binding
 +        // match <local> { .. }
 +        if let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee));
 +        if !pat_contains_or(inner_then_pat);
 +        // the binding must come from the pattern of the containing match arm
 +        // ..<local>.. => match <local> { .. }
 +        if let Some(binding_span) = find_pat_binding(outer_pat, binding_id);
 +        // the "else" branches must be equal
 +        if match (outer_else_body, inner_else_body) {
 +            (None, None) => true,
 +            (None, Some(e)) | (Some(e), None) => is_unit_expr(e),
 +            (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b),
 +        };
 +        // the binding must not be used in the if guard
-                 !used_visitor.check_expr(body) && els.map_or(true, |e| !used_visitor.check_expr(e))
++        if outer_guard.map_or(true, |(Guard::If(e) | Guard::IfLet(_, e))| !is_local_used(cx, *e, binding_id));
 +        // ...or anywhere in the inner expression
 +        if match inner {
 +            IfLetOrMatch::IfLet(_, _, body, els) => {
-             IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| used_visitor.check_arm(arm)),
++                !is_local_used(cx, body, binding_id) && els.map_or(true, |e| !is_local_used(cx, e, binding_id))
 +            },
- enum IfLetOrMatch<'hir> {
-     Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource),
-     /// scrutinee, pattern, then block, else block
-     IfLet(&'hir Expr<'hir>, &'hir Pat<'hir>, &'hir Expr<'hir>, Option<&'hir Expr<'hir>>),
- }
- impl<'hir> IfLetOrMatch<'hir> {
-     fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
-         match expr.kind {
-             ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)),
-             _ => higher::IfLet::hir(cx, expr).map(|higher::IfLet { let_expr, let_pat, if_then, if_else }| {
-                 Self::IfLet(let_expr, let_pat, if_then, if_else)
-             })
-         }
-     }
- }
++            IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| is_local_used(cx, arm, binding_id)),
 +        };
 +        then {
 +            let msg = format!(
 +                "this `{}` can be collapsed into the outer `{}`",
 +                if matches!(inner, IfLetOrMatch::Match(..)) { "match" } else { "if let" },
 +                if outer_is_match { "match" } else { "if let" },
 +            );
 +            span_lint_and_then(
 +                cx,
 +                COLLAPSIBLE_MATCH,
 +                inner_expr.span,
 +                &msg,
 +                |diag| {
 +                    let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]);
 +                    help_span.push_span_label(binding_span, "replace this binding".into());
 +                    help_span.push_span_label(inner_then_pat.span, "with this pattern".into());
 +                    diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern");
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
 +    while let ExprKind::Block(block, _) = expr.kind {
 +        match (block.stmts, block.expr) {
 +            ([stmt], None) => match stmt.kind {
 +                StmtKind::Expr(e) | StmtKind::Semi(e) => expr = e,
 +                _ => break,
 +            },
 +            ([], Some(e)) => expr = e,
 +            _ => break,
 +        }
 +    }
 +    expr
 +}
 +
 +/// A "wild-like" arm has a wild (`_`) or `None` pattern and no guard. Such arms can be "collapsed"
 +/// into a single wild arm without any significant loss in semantics or readability.
 +fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +    if arm.guard.is_some() {
 +        return false;
 +    }
 +    match arm.pat.kind {
 +        PatKind::Binding(..) | PatKind::Wild => true,
 +        PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
 +        _ => false,
 +    }
 +}
 +
 +fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option<Span> {
 +    let mut span = None;
 +    pat.walk_short(|p| match &p.kind {
 +        // ignore OR patterns
 +        PatKind::Or(_) => false,
 +        PatKind::Binding(_bm, _, _ident, _) => {
 +            let found = p.hir_id == hir_id;
 +            if found {
 +                span = Some(p.span);
 +            }
 +            !found
 +        },
 +        _ => true,
 +    });
 +    span
 +}
 +
 +fn pat_contains_or(pat: &Pat<'_>) -> bool {
 +    let mut result = false;
 +    pat.walk(|p| {
 +        let is_or = matches!(p.kind, PatKind::Or(_));
 +        result |= is_or;
 +        !is_or
 +    });
 +    result
 +}
index 5eb99cfe24f49dbae264ea7e6d48b02c7febab4f,0000000000000000000000000000000000000000..d58e49491203c1da290327d0af845d72d2018024
mode 100644,000000..100644
--- /dev/null
@@@ -1,646 -1,0 +1,646 @@@
-     complexity,
 +use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
 +use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
 +use clippy_utils::{
 +    both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, is_else_clause,
 +    is_lint_allowed, search_same, ContainsName, SpanlessEq, SpanlessHash,
 +};
 +use if_chain::if_chain;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::{Applicability, DiagnosticBuilder};
 +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 +use rustc_hir::{Block, Expr, ExprKind, HirId};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::map::Map;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{source_map::Span, symbol::Symbol, BytePos};
 +use std::borrow::Cow;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for consecutive `if`s with the same condition.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a copy & paste error.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// if a == b {
 +    ///     …
 +    /// } else if a == b {
 +    ///     …
 +    /// }
 +    /// ```
 +    ///
 +    /// Note that this lint ignores all conditions with a function call as it could
 +    /// have side effects:
 +    ///
 +    /// ```ignore
 +    /// if foo() {
 +    ///     …
 +    /// } else if foo() { // not linted
 +    ///     …
 +    /// }
 +    /// ```
 +    pub IFS_SAME_COND,
 +    correctness,
 +    "consecutive `if`s with the same condition"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for consecutive `if`s with the same function call.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a copy & paste error.
 +    /// Despite the fact that function can have side effects and `if` works as
 +    /// intended, such an approach is implicit and can be considered a "code smell".
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// if foo() == bar {
 +    ///     …
 +    /// } else if foo() == bar {
 +    ///     …
 +    /// }
 +    /// ```
 +    ///
 +    /// This probably should be:
 +    /// ```ignore
 +    /// if foo() == bar {
 +    ///     …
 +    /// } else if foo() == baz {
 +    ///     …
 +    /// }
 +    /// ```
 +    ///
 +    /// or if the original code was not a typo and called function mutates a state,
 +    /// consider move the mutation out of the `if` condition to avoid similarity to
 +    /// a copy & paste error:
 +    ///
 +    /// ```ignore
 +    /// let first = foo();
 +    /// if first == bar {
 +    ///     …
 +    /// } else {
 +    ///     let second = foo();
 +    ///     if second == bar {
 +    ///     …
 +    ///     }
 +    /// }
 +    /// ```
 +    pub SAME_FUNCTIONS_IN_IF_CONDITION,
 +    pedantic,
 +    "consecutive `if`s with the same function call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `if/else` with the same body as the *then* part
 +    /// and the *else* part.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a copy & paste error.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// let foo = if … {
 +    ///     42
 +    /// } else {
 +    ///     42
 +    /// };
 +    /// ```
 +    pub IF_SAME_THEN_ELSE,
 +    correctness,
 +    "`if` with the same `then` and `else` blocks"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks if the `if` and `else` block contain shared code that can be
 +    /// moved out of the blocks.
 +    ///
 +    /// ### Why is this bad?
 +    /// Duplicate code is less maintainable.
 +    ///
 +    /// ### Known problems
 +    /// * The lint doesn't check if the moved expressions modify values that are beeing used in
 +    ///   the if condition. The suggestion can in that case modify the behavior of the program.
 +    ///   See [rust-clippy#7452](https://github.com/rust-lang/rust-clippy/issues/7452)
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// let foo = if … {
 +    ///     println!("Hello World");
 +    ///     13
 +    /// } else {
 +    ///     println!("Hello World");
 +    ///     42
 +    /// };
 +    /// ```
 +    ///
 +    /// Could be written as:
 +    /// ```ignore
 +    /// println!("Hello World");
 +    /// let foo = if … {
 +    ///     13
 +    /// } else {
 +    ///     42
 +    /// };
 +    /// ```
 +    pub BRANCHES_SHARING_CODE,
++    nursery,
 +    "`if` statement with shared code in all blocks"
 +}
 +
 +declare_lint_pass!(CopyAndPaste => [
 +    IFS_SAME_COND,
 +    SAME_FUNCTIONS_IN_IF_CONDITION,
 +    IF_SAME_THEN_ELSE,
 +    BRANCHES_SHARING_CODE
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if !expr.span.from_expansion() {
 +            if let ExprKind::If(_, _, _) = expr.kind {
 +                // skip ifs directly in else, it will be checked in the parent if
 +                if let Some(&Expr {
 +                    kind: ExprKind::If(_, _, Some(else_expr)),
 +                    ..
 +                }) = get_parent_expr(cx, expr)
 +                {
 +                    if else_expr.hir_id == expr.hir_id {
 +                        return;
 +                    }
 +                }
 +
 +                let (conds, blocks) = if_sequence(expr);
 +                // Conditions
 +                lint_same_cond(cx, &conds);
 +                lint_same_fns_in_if_cond(cx, &conds);
 +                // Block duplication
 +                lint_same_then_else(cx, &blocks, conds.len() == blocks.len(), expr);
 +            }
 +        }
 +    }
 +}
 +
 +/// Implementation of `BRANCHES_SHARING_CODE` and `IF_SAME_THEN_ELSE` if the blocks are equal.
 +fn lint_same_then_else<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    blocks: &[&Block<'tcx>],
 +    has_conditional_else: bool,
 +    expr: &'tcx Expr<'_>,
 +) {
 +    // We only lint ifs with multiple blocks
 +    if blocks.len() < 2 || is_else_clause(cx.tcx, expr) {
 +        return;
 +    }
 +
 +    // Check if each block has shared code
 +    let has_expr = blocks[0].expr.is_some();
 +
 +    let (start_eq, mut end_eq, expr_eq) = if let Some(block_eq) = scan_block_for_eq(cx, blocks) {
 +        (block_eq.start_eq, block_eq.end_eq, block_eq.expr_eq)
 +    } else {
 +        return;
 +    };
 +
 +    // BRANCHES_SHARING_CODE prerequisites
 +    if has_conditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) {
 +        return;
 +    }
 +
 +    // Only the start is the same
 +    if start_eq != 0 && end_eq == 0 && (!has_expr || !expr_eq) {
 +        let block = blocks[0];
 +        let start_stmts = block.stmts.split_at(start_eq).0;
 +
 +        let mut start_walker = UsedValueFinderVisitor::new(cx);
 +        for stmt in start_stmts {
 +            intravisit::walk_stmt(&mut start_walker, stmt);
 +        }
 +
 +        emit_branches_sharing_code_lint(
 +            cx,
 +            start_eq,
 +            0,
 +            false,
 +            check_for_warn_of_moved_symbol(cx, &start_walker.def_symbols, expr),
 +            blocks,
 +            expr,
 +        );
 +    } else if end_eq != 0 || (has_expr && expr_eq) {
 +        let block = blocks[blocks.len() - 1];
 +        let (start_stmts, block_stmts) = block.stmts.split_at(start_eq);
 +        let (block_stmts, end_stmts) = block_stmts.split_at(block_stmts.len() - end_eq);
 +
 +        // Scan start
 +        let mut start_walker = UsedValueFinderVisitor::new(cx);
 +        for stmt in start_stmts {
 +            intravisit::walk_stmt(&mut start_walker, stmt);
 +        }
 +        let mut moved_syms = start_walker.def_symbols;
 +
 +        // Scan block
 +        let mut block_walker = UsedValueFinderVisitor::new(cx);
 +        for stmt in block_stmts {
 +            intravisit::walk_stmt(&mut block_walker, stmt);
 +        }
 +        let mut block_defs = block_walker.defs;
 +
 +        // Scan moved stmts
 +        let mut moved_start: Option<usize> = None;
 +        let mut end_walker = UsedValueFinderVisitor::new(cx);
 +        for (index, stmt) in end_stmts.iter().enumerate() {
 +            intravisit::walk_stmt(&mut end_walker, stmt);
 +
 +            for value in &end_walker.uses {
 +                // Well we can't move this and all prev statements. So reset
 +                if block_defs.contains(value) {
 +                    moved_start = Some(index + 1);
 +                    end_walker.defs.drain().for_each(|x| {
 +                        block_defs.insert(x);
 +                    });
 +
 +                    end_walker.def_symbols.clear();
 +                }
 +            }
 +
 +            end_walker.uses.clear();
 +        }
 +
 +        if let Some(moved_start) = moved_start {
 +            end_eq -= moved_start;
 +        }
 +
 +        let end_linable = block.expr.map_or_else(
 +            || end_eq != 0,
 +            |expr| {
 +                intravisit::walk_expr(&mut end_walker, expr);
 +                end_walker.uses.iter().any(|x| !block_defs.contains(x))
 +            },
 +        );
 +
 +        if end_linable {
 +            end_walker.def_symbols.drain().for_each(|x| {
 +                moved_syms.insert(x);
 +            });
 +        }
 +
 +        emit_branches_sharing_code_lint(
 +            cx,
 +            start_eq,
 +            end_eq,
 +            end_linable,
 +            check_for_warn_of_moved_symbol(cx, &moved_syms, expr),
 +            blocks,
 +            expr,
 +        );
 +    }
 +}
 +
 +struct BlockEqual {
 +    /// The amount statements that are equal from the start
 +    start_eq: usize,
 +    /// The amount statements that are equal from the end
 +    end_eq: usize,
 +    ///  An indication if the block expressions are the same. This will also be true if both are
 +    /// `None`
 +    expr_eq: bool,
 +}
 +
 +/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to
 +/// abort any further processing and avoid duplicate lint triggers.
 +fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<BlockEqual> {
 +    let mut start_eq = usize::MAX;
 +    let mut end_eq = usize::MAX;
 +    let mut expr_eq = true;
 +    let mut iter = blocks.windows(2);
 +    while let Some(&[win0, win1]) = iter.next() {
 +        let l_stmts = win0.stmts;
 +        let r_stmts = win1.stmts;
 +
 +        // `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752.
 +        // The comparison therefore needs to be done in a way that builds the correct context.
 +        let mut evaluator = SpanlessEq::new(cx);
 +        let mut evaluator = evaluator.inter_expr();
 +
 +        let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r));
 +
 +        let current_end_eq = {
 +            // We skip the middle statements which can't be equal
 +            let end_comparison_count = l_stmts.len().min(r_stmts.len()) - current_start_eq;
 +            let it1 = l_stmts.iter().skip(l_stmts.len() - end_comparison_count);
 +            let it2 = r_stmts.iter().skip(r_stmts.len() - end_comparison_count);
 +            it1.zip(it2)
 +                .fold(0, |acc, (l, r)| if evaluator.eq_stmt(l, r) { acc + 1 } else { 0 })
 +        };
 +        let block_expr_eq = both(&win0.expr, &win1.expr, |l, r| evaluator.eq_expr(l, r));
 +
 +        // IF_SAME_THEN_ELSE
 +        if_chain! {
 +            if block_expr_eq;
 +            if l_stmts.len() == r_stmts.len();
 +            if l_stmts.len() == current_start_eq;
 +            if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win0.hir_id);
 +            if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win1.hir_id);
 +            then {
 +                span_lint_and_note(
 +                    cx,
 +                    IF_SAME_THEN_ELSE,
 +                    win0.span,
 +                    "this `if` has identical blocks",
 +                    Some(win1.span),
 +                    "same as this",
 +                );
 +
 +                return None;
 +            }
 +        }
 +
 +        start_eq = start_eq.min(current_start_eq);
 +        end_eq = end_eq.min(current_end_eq);
 +        expr_eq &= block_expr_eq;
 +    }
 +
 +    if !expr_eq {
 +        end_eq = 0;
 +    }
 +
 +    // Check if the regions are overlapping. Set `end_eq` to prevent the overlap
 +    let min_block_size = blocks.iter().map(|x| x.stmts.len()).min().unwrap();
 +    if (start_eq + end_eq) > min_block_size {
 +        end_eq = min_block_size - start_eq;
 +    }
 +
 +    Some(BlockEqual {
 +        start_eq,
 +        end_eq,
 +        expr_eq,
 +    })
 +}
 +
 +fn check_for_warn_of_moved_symbol(
 +    cx: &LateContext<'tcx>,
 +    symbols: &FxHashSet<Symbol>,
 +    if_expr: &'tcx Expr<'_>,
 +) -> bool {
 +    get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| {
 +        let ignore_span = block.span.shrink_to_lo().to(if_expr.span);
 +
 +        symbols
 +            .iter()
 +            .filter(|sym| !sym.as_str().starts_with('_'))
 +            .any(move |sym| {
 +                let mut walker = ContainsName {
 +                    name: *sym,
 +                    result: false,
 +                };
 +
 +                // Scan block
 +                block
 +                    .stmts
 +                    .iter()
 +                    .filter(|stmt| !ignore_span.overlaps(stmt.span))
 +                    .for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt));
 +
 +                if let Some(expr) = block.expr {
 +                    intravisit::walk_expr(&mut walker, expr);
 +                }
 +
 +                walker.result
 +            })
 +    })
 +}
 +
 +fn emit_branches_sharing_code_lint(
 +    cx: &LateContext<'tcx>,
 +    start_stmts: usize,
 +    end_stmts: usize,
 +    lint_end: bool,
 +    warn_about_moved_symbol: bool,
 +    blocks: &[&Block<'tcx>],
 +    if_expr: &'tcx Expr<'_>,
 +) {
 +    if start_stmts == 0 && !lint_end {
 +        return;
 +    }
 +
 +    // (help, span, suggestion)
 +    let mut suggestions: Vec<(&str, Span, String)> = vec![];
 +    let mut add_expr_note = false;
 +
 +    // Construct suggestions
 +    if start_stmts > 0 {
 +        let block = blocks[0];
 +        let span_start = first_line_of_span(cx, if_expr.span).shrink_to_lo();
 +        let span_end = block.stmts[start_stmts - 1].span.source_callsite();
 +
 +        let cond_span = first_line_of_span(cx, if_expr.span).until(block.span);
 +        let cond_snippet = reindent_multiline(snippet(cx, cond_span, "_"), false, None);
 +        let cond_indent = indent_of(cx, cond_span);
 +        let moved_span = block.stmts[0].span.source_callsite().to(span_end);
 +        let moved_snippet = reindent_multiline(snippet(cx, moved_span, "_"), true, None);
 +        let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{";
 +        let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent);
 +
 +        let span = span_start.to(span_end);
 +        suggestions.push(("start", span, suggestion.to_string()));
 +    }
 +
 +    if lint_end {
 +        let block = blocks[blocks.len() - 1];
 +        let span_end = block.span.shrink_to_hi();
 +
 +        let moved_start = if end_stmts == 0 && block.expr.is_some() {
 +            block.expr.unwrap().span
 +        } else {
 +            block.stmts[block.stmts.len() - end_stmts].span
 +        }
 +        .source_callsite();
 +        let moved_end = block
 +            .expr
 +            .map_or_else(|| block.stmts[block.stmts.len() - 1].span, |expr| expr.span)
 +            .source_callsite();
 +
 +        let moved_span = moved_start.to(moved_end);
 +        let moved_snipped = reindent_multiline(snippet(cx, moved_span, "_"), true, None);
 +        let indent = indent_of(cx, if_expr.span.shrink_to_hi());
 +        let suggestion = "}\n".to_string() + &moved_snipped;
 +        let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent);
 +
 +        let mut span = moved_start.to(span_end);
 +        // Improve formatting if the inner block has indention (i.e. normal Rust formatting)
 +        let test_span = Span::new(span.lo() - BytePos(4), span.lo(), span.ctxt());
 +        if snippet_opt(cx, test_span)
 +            .map(|snip| snip == "    ")
 +            .unwrap_or_default()
 +        {
 +            span = span.with_lo(test_span.lo());
 +        }
 +
 +        suggestions.push(("end", span, suggestion.to_string()));
 +        add_expr_note = !cx.typeck_results().expr_ty(if_expr).is_unit();
 +    }
 +
 +    let add_optional_msgs = |diag: &mut DiagnosticBuilder<'_>| {
 +        if add_expr_note {
 +            diag.note("The end suggestion probably needs some adjustments to use the expression result correctly");
 +        }
 +
 +        if warn_about_moved_symbol {
 +            diag.warn("Some moved values might need to be renamed to avoid wrong references");
 +        }
 +    };
 +
 +    // Emit lint
 +    if suggestions.len() == 1 {
 +        let (place_str, span, sugg) = suggestions.pop().unwrap();
 +        let msg = format!("all if blocks contain the same code at the {}", place_str);
 +        let help = format!("consider moving the {} statements out like this", place_str);
 +        span_lint_and_then(cx, BRANCHES_SHARING_CODE, span, msg.as_str(), |diag| {
 +            diag.span_suggestion(span, help.as_str(), sugg, Applicability::Unspecified);
 +
 +            add_optional_msgs(diag);
 +        });
 +    } else if suggestions.len() == 2 {
 +        let (_, end_span, end_sugg) = suggestions.pop().unwrap();
 +        let (_, start_span, start_sugg) = suggestions.pop().unwrap();
 +        span_lint_and_then(
 +            cx,
 +            BRANCHES_SHARING_CODE,
 +            start_span,
 +            "all if blocks contain the same code at the start and the end. Here at the start",
 +            move |diag| {
 +                diag.span_note(end_span, "and here at the end");
 +
 +                diag.span_suggestion(
 +                    start_span,
 +                    "consider moving the start statements out like this",
 +                    start_sugg,
 +                    Applicability::Unspecified,
 +                );
 +
 +                diag.span_suggestion(
 +                    end_span,
 +                    "and consider moving the end statements out like this",
 +                    end_sugg,
 +                    Applicability::Unspecified,
 +                );
 +
 +                add_optional_msgs(diag);
 +            },
 +        );
 +    }
 +}
 +
 +/// This visitor collects `HirId`s and Symbols of defined symbols and `HirId`s of used values.
 +struct UsedValueFinderVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +
 +    /// The `HirId`s of defined values in the scanned statements
 +    defs: FxHashSet<HirId>,
 +
 +    /// The Symbols of the defined symbols in the scanned statements
 +    def_symbols: FxHashSet<Symbol>,
 +
 +    /// The `HirId`s of the used values
 +    uses: FxHashSet<HirId>,
 +}
 +
 +impl<'a, 'tcx> UsedValueFinderVisitor<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        UsedValueFinderVisitor {
 +            cx,
 +            defs: FxHashSet::default(),
 +            def_symbols: FxHashSet::default(),
 +            uses: FxHashSet::default(),
 +        }
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for UsedValueFinderVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::All(self.cx.tcx.hir())
 +    }
 +
 +    fn visit_local(&mut self, l: &'tcx rustc_hir::Local<'tcx>) {
 +        let local_id = l.pat.hir_id;
 +        self.defs.insert(local_id);
 +
 +        if let Some(sym) = l.pat.simple_ident() {
 +            self.def_symbols.insert(sym.name);
 +        }
 +
 +        if let Some(expr) = l.init {
 +            intravisit::walk_expr(self, expr);
 +        }
 +    }
 +
 +    fn visit_qpath(&mut self, qpath: &'tcx rustc_hir::QPath<'tcx>, id: HirId, _span: rustc_span::Span) {
 +        if let rustc_hir::QPath::Resolved(_, path) = *qpath {
 +            if path.segments.len() == 1 {
 +                if let rustc_hir::def::Res::Local(var) = self.cx.qpath_res(qpath, id) {
 +                    self.uses.insert(var);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Implementation of `IFS_SAME_COND`.
 +fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
 +    let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 {
 +        let mut h = SpanlessHash::new(cx);
 +        h.hash_expr(expr);
 +        h.finish()
 +    };
 +
 +    let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { eq_expr_value(cx, lhs, rhs) };
 +
 +    for (i, j) in search_same(conds, hash, eq) {
 +        span_lint_and_note(
 +            cx,
 +            IFS_SAME_COND,
 +            j.span,
 +            "this `if` has the same condition as a previous `if`",
 +            Some(i.span),
 +            "same as this",
 +        );
 +    }
 +}
 +
 +/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`.
 +fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
 +    let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 {
 +        let mut h = SpanlessHash::new(cx);
 +        h.hash_expr(expr);
 +        h.finish()
 +    };
 +
 +    let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool {
 +        // Do not lint if any expr originates from a macro
 +        if in_macro(lhs.span) || in_macro(rhs.span) {
 +            return false;
 +        }
 +        // Do not spawn warning if `IFS_SAME_COND` already produced it.
 +        if eq_expr_value(cx, lhs, rhs) {
 +            return false;
 +        }
 +        SpanlessEq::new(cx).eq_expr(lhs, rhs)
 +    };
 +
 +    for (i, j) in search_same(conds, hash, eq) {
 +        span_lint_and_note(
 +            cx,
 +            SAME_FUNCTIONS_IN_IF_CONDITION,
 +            j.span,
 +            "this `if` has the same function call as a previous `if`",
 +            Some(i.span),
 +            "same as this",
 +        );
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b4c4ca016aace000f6e8d9ed438e83f5325b7b51
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,108 @@@
++use clippy_utils::diagnostics::span_lint_and_help;
++use clippy_utils::{in_macro, is_automatically_derived, is_default_equivalent, remove_blocks};
++use rustc_hir::{
++    def::{DefKind, Res},
++    Body, Expr, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node, QPath,
++};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::TypeFoldable;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::sym;
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Detects manual `std::default::Default` implementations that are identical to a derived implementation.
++    ///
++    /// ### Why is this bad?
++    /// It is less concise.
++    ///
++    /// ### Example
++    /// ```rust
++    /// struct Foo {
++    ///     bar: bool
++    /// }
++    ///
++    /// impl std::default::Default for Foo {
++    ///     fn default() -> Self {
++    ///         Self {
++    ///             bar: false
++    ///         }
++    ///     }
++    /// }
++    /// ```
++    ///
++    /// Could be written as:
++    ///
++    /// ```rust
++    /// #[derive(Default)]
++    /// struct Foo {
++    ///     bar: bool
++    /// }
++    /// ```
++    ///
++    /// ### Known problems
++    /// Derive macros [sometimes use incorrect bounds](https://github.com/rust-lang/rust/issues/26925)
++    /// in generic types and the user defined `impl` maybe is more generalized or
++    /// specialized than what derive will produce. This lint can't detect the manual `impl`
++    /// has exactly equal bounds, and therefore this lint is disabled for types with
++    /// generic parameters.
++    ///
++    pub DERIVABLE_IMPLS,
++    complexity,
++    "manual implementation of the `Default` trait which is equal to a derive"
++}
++
++declare_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]);
++
++fn is_path_self(e: &Expr<'_>) -> bool {
++    if let ExprKind::Path(QPath::Resolved(_, p)) = e.kind {
++        matches!(p.res, Res::SelfCtor(..) | Res::Def(DefKind::Ctor(..), _))
++    } else {
++        false
++    }
++}
++
++impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
++    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
++        if_chain! {
++            if let ItemKind::Impl(Impl {
++                of_trait: Some(ref trait_ref),
++                items: [child],
++                ..
++            }) = item.kind;
++            if let attrs = cx.tcx.hir().attrs(item.hir_id());
++            if !is_automatically_derived(attrs);
++            if !in_macro(item.span);
++            if let Some(def_id) = trait_ref.trait_def_id();
++            if cx.tcx.is_diagnostic_item(sym::Default, def_id);
++            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);
++            if let Some(adt_def) = cx.tcx.type_of(item.def_id).ty_adt_def();
++            then {
++                if cx.tcx.type_of(item.def_id).definitely_has_param_types_or_consts(cx.tcx) {
++                    return;
++                }
++                let should_emit = match remove_blocks(func_expr).kind {
++                    ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)),
++                    ExprKind::Call(callee, args)
++                        if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)),
++                    ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)),
++                    _ => false,
++                };
++                if should_emit {
++                    let path_string = cx.tcx.def_path_str(adt_def.did);
++                    span_lint_and_help(
++                        cx,
++                        DERIVABLE_IMPLS,
++                        item.span,
++                        "this `impl` can be derived",
++                        None,
++                        &format!("try annotating `{}` with `#[derive(Default)]`", path_string),
++                    );
++                }
++            }
++        }
++    }
++}
index dcfa5253f83412ee093327fd70fc9e1276829e50,0000000000000000000000000000000000000000..8416b8440dfbe0bf99988c21d09aea8b60799d18
mode 100644,000000..100644
--- /dev/null
@@@ -1,425 -1,0 +1,422 @@@
-     /// ### Known problems
-     /// Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925
-     ///
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then};
 +use clippy_utils::paths;
 +use clippy_utils::ty::{implements_trait, is_copy};
 +use clippy_utils::{get_trait_def_id, is_automatically_derived, is_lint_allowed, match_def_path};
 +use if_chain::if_chain;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor};
 +use rustc_hir::{
 +    BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +
 +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 {
 +    ///     ...
 +    /// }
 +    /// ```
 +    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;
 +    /// ```
 +    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 behaviour, these traits should
 +    /// agree and the behaviour 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 {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    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
 +    ///     }
 +    /// }
 +    /// ```
 +    pub UNSAFE_DERIVE_DESERIALIZE,
 +    pedantic,
 +    "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
 +}
 +
 +declare_lint_pass!(Derive => [
 +    EXPL_IMPL_CLONE_ON_COPY,
 +    DERIVE_HASH_XOR_EQ,
 +    DERIVE_ORD_XOR_PARTIAL_ORD,
 +    UNSAFE_DERIVE_DESERIALIZE
 +]);
 +
 +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 attrs = cx.tcx.hir().attrs(item.hir_id());
 +            let is_automatically_derived = is_automatically_derived(attrs);
 +
 +            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);
 +            } 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: &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 match_def_path(cx, def_id, &paths::HASH);
 +        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 = is_automatically_derived(cx.tcx.get_attrs(impl_id));
 +
 +                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: &TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +    ord_is_automatically_derived: bool,
 +) {
 +    if_chain! {
 +        if let Some(ord_trait_def_id) = get_trait_def_id(cx, &paths::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 = is_automatically_derived(cx.tcx.get_attrs(impl_id));
 +
 +                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: &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: &TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +) {
 +    fn item_from_def_id<'tcx>(cx: &LateContext<'tcx>, def_id: DefId) -> &'tcx Item<'tcx> {
 +        let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
 +        cx.tcx.hir().expect_item(hir_id)
 +    }
 +
 +    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| item_from_def_id(cx, *imp_did))
 +            .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 Map = Map<'tcx>;
 +
 +    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 let Unsafety::Unsafe = header.unsafety;
 +            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 let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules {
 +                self.has_unsafe = true;
 +            }
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::All(self.cx.tcx.hir())
 +    }
 +}
index 627f746ec99716fd6510ba61cbd7b0912c327e53,0000000000000000000000000000000000000000..ac6824672f66cbf90c13b7f7d9e6dfab2d009d4d
mode 100644,000000..100644
--- /dev/null
@@@ -1,645 -1,0 +1,660 @@@
-     Block, Expr, ExprKind, Guard, HirId, Local, Stmt, StmtKind, UnOp,
 +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, ErasedMap, NestedVisitorMap, Visitor},
-             StmtKind::Local(Local { init: Some(e), .. }) => {
-                 self.allow_insert_closure &= !self.in_tail_pos;
-                 self.in_tail_pos = false;
-                 self.is_single_insert = false;
-                 self.visit_expr(e);
++    Block, Expr, ExprKind, Guard, HirId, 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);
 +    /// }
 +    /// ```
 +    /// can both be rewritten as:
 +    /// ```rust
 +    /// # use std::collections::HashMap;
 +    /// # let mut map = HashMap::new();
 +    /// # let k = 1;
 +    /// # let v = 1;
 +    /// map.entry(k).or_insert(v);
 +    /// ```
 +    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 {
 +    #[allow(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(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(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 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.
 +#[allow(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> {
 +    type Map = ErasedMap<'tcx>;
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +
 +    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);
++                }
 +            },
-                     self.allow_insert_closure &= can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops);
++            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(_, 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(_) | ExprKind::LlvmInlineAsm(_) => {
 +                    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 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(
 +    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(|| InsertSearchResults {
 +        edits,
 +        allow_insert_closure,
 +        is_single_insert,
 +    })
 +}
index f72a1e446d55cd11bae944f8467dfd75a98dc6b2,0000000000000000000000000000000000000000..8714ce90164c8e3808a43192d4d979e854cab4ea
mode 100644,000000..100644
--- /dev/null
@@@ -1,357 -1,0 +1,357 @@@
-     /// It is often confusing to read. In addition, the
-     /// sub-expression evaluation order for Rust is not well documented.
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
 +use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id};
 +use if_chain::if_chain;
 +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for a read and a write to the same variable where
 +    /// whether the read occurs before or after the write depends on the evaluation
 +    /// order of sub-expressions.
 +    ///
 +    /// ### Why is this bad?
++    /// It is often confusing to read. As described [here](https://doc.rust-lang.org/reference/expressions.html?highlight=subexpression#evaluation-order-of-operands),
++    /// the operands of these expressions are evaluated before applying the effects of the expression.
 +    ///
 +    /// ### Known problems
 +    /// Code which intentionally depends on the evaluation
 +    /// order, or which is correct for any evaluation order.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut x = 0;
 +    ///
 +    /// // Bad
 +    /// let a = {
 +    ///     x = 1;
 +    ///     1
 +    /// } + x;
 +    /// // Unclear whether a is 1 or 2.
 +    ///
 +    /// // Good
 +    /// let tmp = {
 +    ///     x = 1;
 +    ///     1
 +    /// };
 +    /// let a = tmp + x;
 +    /// ```
 +    pub EVAL_ORDER_DEPENDENCE,
 +    suspicious,
 +    "whether a variable read occurs before a write depends on sub-expression evaluation order"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for diverging calls that are not match arms or
 +    /// statements.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is often confusing to read. In addition, the
 +    /// sub-expression evaluation order for Rust is not well documented.
 +    ///
 +    /// ### Known problems
 +    /// Someone might want to use `some_bool || panic!()` as a
 +    /// shorthand.
 +    ///
 +    /// ### Example
 +    /// ```rust,no_run
 +    /// # fn b() -> bool { true }
 +    /// # fn c() -> bool { true }
 +    /// let a = b() || panic!() || c();
 +    /// // `c()` is dead, `panic!()` is only called if `b()` returns `false`
 +    /// let x = (a, b, c, panic!());
 +    /// // can simply be replaced by `panic!()`
 +    /// ```
 +    pub DIVERGING_SUB_EXPRESSION,
 +    complexity,
 +    "whether an expression contains a diverging sub expression"
 +}
 +
 +declare_lint_pass!(EvalOrderDependence => [EVAL_ORDER_DEPENDENCE, DIVERGING_SUB_EXPRESSION]);
 +
 +impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        // Find a write to a local variable.
 +        let var = if_chain! {
 +            if let ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) = expr.kind;
 +            if let Some(var) = path_to_local(lhs);
 +            if expr.span.desugaring_kind().is_none();
 +            then { var } else { return; }
 +        };
 +        let mut visitor = ReadVisitor {
 +            cx,
 +            var,
 +            write_expr: expr,
 +            last_expr: expr,
 +        };
 +        check_for_unsequenced_reads(&mut visitor);
 +    }
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
 +        match stmt.kind {
 +            StmtKind::Local(local) => {
 +                if let Local { init: Some(e), .. } = local {
 +                    DivergenceVisitor { cx }.visit_expr(e);
 +                }
 +            },
 +            StmtKind::Expr(e) | StmtKind::Semi(e) => DivergenceVisitor { cx }.maybe_walk_expr(e),
 +            StmtKind::Item(..) => {},
 +        }
 +    }
 +}
 +
 +struct DivergenceVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
 +    fn maybe_walk_expr(&mut self, e: &'tcx Expr<'_>) {
 +        match e.kind {
 +            ExprKind::Closure(..) => {},
 +            ExprKind::Match(e, arms, _) => {
 +                self.visit_expr(e);
 +                for arm in arms {
 +                    if let Some(Guard::If(if_expr)) = arm.guard {
 +                        self.visit_expr(if_expr);
 +                    }
 +                    // make sure top level arm expressions aren't linted
 +                    self.maybe_walk_expr(&*arm.body);
 +                }
 +            },
 +            _ => walk_expr(self, e),
 +        }
 +    }
 +    fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) {
 +        span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges");
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +        match e.kind {
 +            ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => self.report_diverging_sub_expr(e),
 +            ExprKind::Call(func, _) => {
 +                let typ = self.cx.typeck_results().expr_ty(func);
 +                match typ.kind() {
 +                    ty::FnDef(..) | ty::FnPtr(_) => {
 +                        let sig = typ.fn_sig(self.cx.tcx);
 +                        if let ty::Never = self.cx.tcx.erase_late_bound_regions(sig).output().kind() {
 +                            self.report_diverging_sub_expr(e);
 +                        }
 +                    },
 +                    _ => {},
 +                }
 +            },
 +            ExprKind::MethodCall(..) => {
 +                let borrowed_table = self.cx.typeck_results();
 +                if borrowed_table.expr_ty(e).is_never() {
 +                    self.report_diverging_sub_expr(e);
 +                }
 +            },
 +            _ => {
 +                // do not lint expressions referencing objects of type `!`, as that required a
 +                // diverging expression
 +                // to begin with
 +            },
 +        }
 +        self.maybe_walk_expr(e);
 +    }
 +    fn visit_block(&mut self, _: &'tcx Block<'_>) {
 +        // don't continue over blocks, LateLintPass already does that
 +    }
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
 +/// Walks up the AST from the given write expression (`vis.write_expr`) looking
 +/// for reads to the same variable that are unsequenced relative to the write.
 +///
 +/// This means reads for which there is a common ancestor between the read and
 +/// the write such that
 +///
 +/// * evaluating the ancestor necessarily evaluates both the read and the write (for example, `&x`
 +///   and `|| x = 1` don't necessarily evaluate `x`), and
 +///
 +/// * which one is evaluated first depends on the order of sub-expression evaluation. Blocks, `if`s,
 +///   loops, `match`es, and the short-circuiting logical operators are considered to have a defined
 +///   evaluation order.
 +///
 +/// When such a read is found, the lint is triggered.
 +fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) {
 +    let map = &vis.cx.tcx.hir();
 +    let mut cur_id = vis.write_expr.hir_id;
 +    loop {
 +        let parent_id = map.get_parent_node(cur_id);
 +        if parent_id == cur_id {
 +            break;
 +        }
 +        let parent_node = match map.find(parent_id) {
 +            Some(parent) => parent,
 +            None => break,
 +        };
 +
 +        let stop_early = match parent_node {
 +            Node::Expr(expr) => check_expr(vis, expr),
 +            Node::Stmt(stmt) => check_stmt(vis, stmt),
 +            Node::Item(_) => {
 +                // We reached the top of the function, stop.
 +                break;
 +            },
 +            _ => StopEarly::KeepGoing,
 +        };
 +        match stop_early {
 +            StopEarly::Stop => break,
 +            StopEarly::KeepGoing => {},
 +        }
 +
 +        cur_id = parent_id;
 +    }
 +}
 +
 +/// Whether to stop early for the loop in `check_for_unsequenced_reads`. (If
 +/// `check_expr` weren't an independent function, this would be unnecessary and
 +/// we could just use `break`).
 +enum StopEarly {
 +    KeepGoing,
 +    Stop,
 +}
 +
 +fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) -> StopEarly {
 +    if expr.hir_id == vis.last_expr.hir_id {
 +        return StopEarly::KeepGoing;
 +    }
 +
 +    match expr.kind {
 +        ExprKind::Array(_)
 +        | ExprKind::Tup(_)
 +        | ExprKind::MethodCall(..)
 +        | ExprKind::Call(_, _)
 +        | ExprKind::Assign(..)
 +        | ExprKind::Index(_, _)
 +        | ExprKind::Repeat(_, _)
 +        | ExprKind::Struct(_, _, _) => {
 +            walk_expr(vis, expr);
 +        },
 +        ExprKind::Binary(op, _, _) | ExprKind::AssignOp(op, _, _) => {
 +            if op.node == BinOpKind::And || op.node == BinOpKind::Or {
 +                // x && y and x || y always evaluate x first, so these are
 +                // strictly sequenced.
 +            } else {
 +                walk_expr(vis, expr);
 +            }
 +        },
 +        ExprKind::Closure(_, _, _, _, _) => {
 +            // Either
 +            //
 +            // * `var` is defined in the closure body, in which case we've reached the top of the enclosing
 +            //   function and can stop, or
 +            //
 +            // * `var` is captured by the closure, in which case, because evaluating a closure does not evaluate
 +            //   its body, we don't necessarily have a write, so we need to stop to avoid generating false
 +            //   positives.
 +            //
 +            // This is also the only place we need to stop early (grrr).
 +            return StopEarly::Stop;
 +        },
 +        // All other expressions either have only one child or strictly
 +        // sequence the evaluation order of their sub-expressions.
 +        _ => {},
 +    }
 +
 +    vis.last_expr = expr;
 +
 +    StopEarly::KeepGoing
 +}
 +
 +fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly {
 +    match stmt.kind {
 +        StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr),
 +        // If the declaration is of a local variable, check its initializer
 +        // expression if it has one. Otherwise, keep going.
 +        StmtKind::Local(local) => local
 +            .init
 +            .as_ref()
 +            .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)),
 +        StmtKind::Item(..) => StopEarly::KeepGoing,
 +    }
 +}
 +
 +/// A visitor that looks for reads from a variable.
 +struct ReadVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    /// The ID of the variable we're looking for.
 +    var: HirId,
 +    /// The expressions where the write to the variable occurred (for reporting
 +    /// in the lint).
 +    write_expr: &'tcx Expr<'tcx>,
 +    /// The last (highest in the AST) expression we've checked, so we know not
 +    /// to recheck it.
 +    last_expr: &'tcx Expr<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if expr.hir_id == self.last_expr.hir_id {
 +            return;
 +        }
 +
 +        if path_to_local_id(expr, self.var) {
 +            // Check that this is a read, not a write.
 +            if !is_in_assignment_position(self.cx, expr) {
 +                span_lint_and_note(
 +                    self.cx,
 +                    EVAL_ORDER_DEPENDENCE,
 +                    expr.span,
 +                    &format!("unsequenced read of `{}`", self.cx.tcx.hir().name(self.var)),
 +                    Some(self.write_expr.span),
 +                    "whether read occurs before this write depends on evaluation order",
 +                );
 +            }
 +        }
 +        match expr.kind {
 +            // We're about to descend a closure. Since we don't know when (or
 +            // if) the closure will be evaluated, any reads in it might not
 +            // occur here (or ever). Like above, bail to avoid false positives.
 +            ExprKind::Closure(_, _, _, _, _) |
 +
 +            // We want to avoid a false positive when a variable name occurs
 +            // only to have its address taken, so we stop here. Technically,
 +            // this misses some weird cases, eg.
 +            //
 +            // ```rust
 +            // let mut x = 0;
 +            // let a = foo(&{x = 1; x}, x);
 +            // ```
 +            //
 +            // TODO: fix this
 +            ExprKind::AddrOf(_, _, _) => {
 +                return;
 +            }
 +            _ => {}
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
 +/// Returns `true` if `expr` is the LHS of an assignment, like `expr = ...`.
 +fn is_in_assignment_position(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if let Some(parent) = get_parent_expr(cx, expr) {
 +        if let ExprKind::Assign(lhs, ..) = parent.kind {
 +            return lhs.hir_id == expr.hir_id;
 +        }
 +    }
 +    false
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eef1407a80cf15e2209993cc948e44e229dc63be
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,164 @@@
++use clippy_utils::diagnostics::span_lint_and_help;
++use clippy_utils::{diagnostics::span_lint, is_lint_allowed};
++use rustc_hir::{Crate, CRATE_HIR_ID};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::DUMMY_SP;
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for feature names with prefix `use-`, `with-` or suffix `-support`
++    ///
++    /// ### Why is this bad?
++    /// These prefixes and suffixes have no significant meaning.
++    ///
++    /// ### Example
++    /// ```toml
++    /// # The `Cargo.toml` with feature name redundancy
++    /// [features]
++    /// default = ["use-abc", "with-def", "ghi-support"]
++    /// use-abc = []  // redundant
++    /// with-def = []   // redundant
++    /// ghi-support = []   // redundant
++    /// ```
++    ///
++    /// Use instead:
++    /// ```toml
++    /// [features]
++    /// default = ["abc", "def", "ghi"]
++    /// abc = []
++    /// def = []
++    /// ghi = []
++    /// ```
++    ///
++    pub REDUNDANT_FEATURE_NAMES,
++    cargo,
++    "usage of a redundant feature name"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for negative feature names with prefix `no-` or `not-`
++    ///
++    /// ### Why is this bad?
++    /// Features are supposed to be additive, and negatively-named features violate it.
++    ///
++    /// ### Example
++    /// ```toml
++    /// # The `Cargo.toml` with negative feature names
++    /// [features]
++    /// default = []
++    /// no-abc = []
++    /// not-def = []
++    ///
++    /// ```
++    /// Use instead:
++    /// ```toml
++    /// [features]
++    /// default = ["abc", "def"]
++    /// abc = []
++    /// def = []
++    ///
++    /// ```
++    pub NEGATIVE_FEATURE_NAMES,
++    cargo,
++    "usage of a negative feature name"
++}
++
++declare_lint_pass!(FeatureName => [REDUNDANT_FEATURE_NAMES, NEGATIVE_FEATURE_NAMES]);
++
++static PREFIXES: [&str; 8] = ["no-", "no_", "not-", "not_", "use-", "use_", "with-", "with_"];
++static SUFFIXES: [&str; 2] = ["-support", "_support"];
++
++fn is_negative_prefix(s: &str) -> bool {
++    s.starts_with("no")
++}
++
++fn lint(cx: &LateContext<'_>, feature: &str, substring: &str, is_prefix: bool) {
++    let is_negative = is_prefix && is_negative_prefix(substring);
++    span_lint_and_help(
++        cx,
++        if is_negative {
++            NEGATIVE_FEATURE_NAMES
++        } else {
++            REDUNDANT_FEATURE_NAMES
++        },
++        DUMMY_SP,
++        &format!(
++            "the \"{}\" {} in the feature name \"{}\" is {}",
++            substring,
++            if is_prefix { "prefix" } else { "suffix" },
++            feature,
++            if is_negative { "negative" } else { "redundant" }
++        ),
++        None,
++        &format!(
++            "consider renaming the feature to \"{}\"{}",
++            if is_prefix {
++                feature.strip_prefix(substring)
++            } else {
++                feature.strip_suffix(substring)
++            }
++            .unwrap(),
++            if is_negative {
++                ", but make sure the feature adds functionality"
++            } else {
++                ""
++            }
++        ),
++    );
++}
++
++impl LateLintPass<'_> for FeatureName {
++    fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
++        if is_lint_allowed(cx, REDUNDANT_FEATURE_NAMES, CRATE_HIR_ID)
++            && is_lint_allowed(cx, NEGATIVE_FEATURE_NAMES, CRATE_HIR_ID)
++        {
++            return;
++        }
++
++        let metadata = unwrap_cargo_metadata!(cx, REDUNDANT_FEATURE_NAMES, false);
++
++        for package in metadata.packages {
++            let mut features: Vec<&String> = package.features.keys().collect();
++            features.sort();
++            for feature in features {
++                let prefix_opt = {
++                    let i = PREFIXES.partition_point(|prefix| prefix < &feature.as_str());
++                    if i > 0 && feature.starts_with(PREFIXES[i - 1]) {
++                        Some(PREFIXES[i - 1])
++                    } else {
++                        None
++                    }
++                };
++                if let Some(prefix) = prefix_opt {
++                    lint(cx, feature, prefix, true);
++                }
++
++                let suffix_opt: Option<&str> = {
++                    let i = SUFFIXES.partition_point(|suffix| {
++                        suffix.bytes().rev().cmp(feature.bytes().rev()) == std::cmp::Ordering::Less
++                    });
++                    if i > 0 && feature.ends_with(SUFFIXES[i - 1]) {
++                        Some(SUFFIXES[i - 1])
++                    } else {
++                        None
++                    }
++                };
++                if let Some(suffix) = suffix_opt {
++                    lint(cx, feature, suffix, false);
++                }
++            }
++        }
++    }
++}
++
++#[test]
++fn test_prefixes_sorted() {
++    let mut sorted_prefixes = PREFIXES;
++    sorted_prefixes.sort_unstable();
++    assert_eq!(PREFIXES, sorted_prefixes);
++    let mut sorted_suffixes = SUFFIXES;
++    sorted_suffixes.sort_by(|a, b| a.bytes().rev().cmp(b.bytes().rev()));
++    assert_eq!(SUFFIXES, sorted_suffixes);
++}
index d12482e7b7bb9531bb4fe569e29321b1976d121c,0000000000000000000000000000000000000000..eda611117babf9c5561b09dd4b6bea8d94164b10
mode 100644,000000..100644
--- /dev/null
@@@ -1,711 -1,0 +1,709 @@@
-                     return;
 +use clippy_utils::consts::{
 +    constant, constant_simple, Constant,
 +    Constant::{Int, F32, F64},
 +};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::{eq_expr_value, get_parent_expr, numeric_literal, sugg};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Spanned;
 +
 +use rustc_ast::ast;
 +use std::f32::consts as f32_consts;
 +use std::f64::consts as f64_consts;
 +use sugg::Sugg;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Looks for floating-point expressions that
 +    /// can be expressed using built-in methods to improve accuracy
 +    /// at the cost of performance.
 +    ///
 +    /// ### Why is this bad?
 +    /// Negatively impacts accuracy.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = 3f32;
 +    /// let _ = a.powf(1.0 / 3.0);
 +    /// let _ = (1.0 + a).ln();
 +    /// let _ = a.exp() - 1.0;
 +    /// ```
 +    ///
 +    /// is better expressed as
 +    ///
 +    /// ```rust
 +    /// let a = 3f32;
 +    /// let _ = a.cbrt();
 +    /// let _ = a.ln_1p();
 +    /// let _ = a.exp_m1();
 +    /// ```
 +    pub IMPRECISE_FLOPS,
 +    nursery,
 +    "usage of imprecise floating point operations"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Looks for floating-point expressions that
 +    /// can be expressed using built-in methods to improve both
 +    /// accuracy and performance.
 +    ///
 +    /// ### Why is this bad?
 +    /// Negatively impacts accuracy and performance.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::f32::consts::E;
 +    ///
 +    /// let a = 3f32;
 +    /// let _ = (2f32).powf(a);
 +    /// let _ = E.powf(a);
 +    /// let _ = a.powf(1.0 / 2.0);
 +    /// let _ = a.log(2.0);
 +    /// let _ = a.log(10.0);
 +    /// let _ = a.log(E);
 +    /// let _ = a.powf(2.0);
 +    /// let _ = a * 2.0 + 4.0;
 +    /// let _ = if a < 0.0 {
 +    ///     -a
 +    /// } else {
 +    ///     a
 +    /// };
 +    /// let _ = if a < 0.0 {
 +    ///     a
 +    /// } else {
 +    ///     -a
 +    /// };
 +    /// ```
 +    ///
 +    /// is better expressed as
 +    ///
 +    /// ```rust
 +    /// use std::f32::consts::E;
 +    ///
 +    /// let a = 3f32;
 +    /// let _ = a.exp2();
 +    /// let _ = a.exp();
 +    /// let _ = a.sqrt();
 +    /// let _ = a.log2();
 +    /// let _ = a.log10();
 +    /// let _ = a.ln();
 +    /// let _ = a.powi(2);
 +    /// let _ = a.mul_add(2.0, 4.0);
 +    /// let _ = a.abs();
 +    /// let _ = -a.abs();
 +    /// ```
 +    pub SUBOPTIMAL_FLOPS,
 +    nursery,
 +    "usage of sub-optimal floating point operations"
 +}
 +
 +declare_lint_pass!(FloatingPointArithmetic => [
 +    IMPRECISE_FLOPS,
 +    SUBOPTIMAL_FLOPS
 +]);
 +
 +// Returns the specialized log method for a given base if base is constant
 +// and is one of 2, 10 and e
 +fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> {
 +    if let Some((value, _)) = constant(cx, cx.typeck_results(), base) {
 +        if F32(2.0) == value || F64(2.0) == value {
 +            return Some("log2");
 +        } else if F32(10.0) == value || F64(10.0) == value {
 +            return Some("log10");
 +        } else if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
 +            return Some("ln");
 +        }
 +    }
 +
 +    None
 +}
 +
 +// Adds type suffixes and parenthesis to method receivers if necessary
 +fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Sugg<'a> {
 +    let mut suggestion = Sugg::hir(cx, expr, "..");
 +
 +    if let ExprKind::Unary(UnOp::Neg, inner_expr) = &expr.kind {
 +        expr = inner_expr;
 +    }
 +
 +    if_chain! {
 +        // if the expression is a float literal and it is unsuffixed then
 +        // add a suffix so the suggestion is valid and unambiguous
 +        if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind();
 +        if let ExprKind::Lit(lit) = &expr.kind;
 +        if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node;
 +        then {
 +            let op = format!(
 +                "{}{}{}",
 +                suggestion,
 +                // Check for float literals without numbers following the decimal
 +                // separator such as `2.` and adds a trailing zero
 +                if sym.as_str().ends_with('.') {
 +                    "0"
 +                } else {
 +                    ""
 +                },
 +                float_ty.name_str()
 +            ).into();
 +
 +            suggestion = match suggestion {
 +                Sugg::MaybeParen(_) => Sugg::MaybeParen(op),
 +                _ => Sugg::NonParen(op)
 +            };
 +        }
 +    }
 +
 +    suggestion.maybe_par()
 +}
 +
 +fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 +    if let Some(method) = get_specialized_log_method(cx, &args[1]) {
 +        span_lint_and_sugg(
 +            cx,
 +            SUBOPTIMAL_FLOPS,
 +            expr.span,
 +            "logarithm for bases 2, 10 and e can be computed more accurately",
 +            "consider using",
 +            format!("{}.{}()", Sugg::hir(cx, &args[0], ".."), method),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +// TODO: Lint expressions of the form `(x + y).ln()` where y > 1 and
 +// suggest usage of `(x + (y - 1)).ln_1p()` instead
 +fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 +    if let ExprKind::Binary(
 +        Spanned {
 +            node: BinOpKind::Add, ..
 +        },
 +        lhs,
 +        rhs,
 +    ) = &args[0].kind
 +    {
 +        let recv = match (
 +            constant(cx, cx.typeck_results(), lhs),
 +            constant(cx, cx.typeck_results(), rhs),
 +        ) {
 +            (Some((value, _)), _) if F32(1.0) == value || F64(1.0) == value => rhs,
 +            (_, Some((value, _))) if F32(1.0) == value || F64(1.0) == value => lhs,
 +            _ => return,
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            IMPRECISE_FLOPS,
 +            expr.span,
 +            "ln(1 + x) can be computed more accurately",
 +            "consider using",
 +            format!("{}.ln_1p()", prepare_receiver_sugg(cx, recv)),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +// Returns an integer if the float constant is a whole number and it can be
 +// converted to an integer without loss of precision. For now we only check
 +// ranges [-16777215, 16777216) for type f32 as whole number floats outside
 +// this range are lossy and ambiguous.
 +#[allow(clippy::cast_possible_truncation)]
 +fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
 +    match value {
 +        F32(num) if num.fract() == 0.0 => {
 +            if (-16_777_215.0..16_777_216.0).contains(num) {
 +                Some(num.round() as i32)
 +            } else {
 +                None
 +            }
 +        },
 +        F64(num) if num.fract() == 0.0 => {
 +            if (-2_147_483_648.0..2_147_483_648.0).contains(num) {
 +                Some(num.round() as i32)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 +    // Check receiver
 +    if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) {
 +        let method = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
 +            "exp"
 +        } else if F32(2.0) == value || F64(2.0) == value {
 +            "exp2"
 +        } else {
 +            return;
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            SUBOPTIMAL_FLOPS,
 +            expr.span,
 +            "exponent for bases 2 and e can be computed more accurately",
 +            "consider using",
 +            format!("{}.{}()", prepare_receiver_sugg(cx, &args[1]), method),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +
 +    // Check argument
 +    if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[1]) {
 +        let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value {
 +            (
 +                SUBOPTIMAL_FLOPS,
 +                "square-root of a number can be computed more efficiently and accurately",
 +                format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..")),
 +            )
 +        } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
 +            (
 +                IMPRECISE_FLOPS,
 +                "cube-root of a number can be computed more accurately",
 +                format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..")),
 +            )
 +        } else if let Some(exponent) = get_integer_from_float_constant(&value) {
 +            (
 +                SUBOPTIMAL_FLOPS,
 +                "exponentiation with integer powers can be computed more efficiently",
 +                format!(
 +                    "{}.powi({})",
 +                    Sugg::hir(cx, &args[0], ".."),
 +                    numeric_literal::format(&exponent.to_string(), None, false)
 +                ),
 +            )
 +        } else {
 +            return;
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            lint,
 +            expr.span,
 +            help,
 +            "consider using",
 +            suggestion,
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 +    if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[1]) {
 +        if value == Int(2) {
 +            if let Some(parent) = get_parent_expr(cx, expr) {
 +                if let Some(grandparent) = get_parent_expr(cx, parent) {
 +                    if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = grandparent.kind {
 +                        if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
 +                            return;
 +                        }
 +                    }
 +                }
 +
 +                if let ExprKind::Binary(
 +                    Spanned {
 +                        node: BinOpKind::Add, ..
 +                    },
 +                    lhs,
 +                    rhs,
 +                ) = parent.kind
 +                {
 +                    let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
 +
 +                    span_lint_and_sugg(
 +                        cx,
 +                        SUBOPTIMAL_FLOPS,
 +                        parent.span,
 +                        "multiply and add expressions can be calculated more efficiently and accurately",
 +                        "consider using",
 +                        format!(
 +                            "{}.mul_add({}, {})",
 +                            Sugg::hir(cx, &args[0], ".."),
 +                            Sugg::hir(cx, &args[0], ".."),
 +                            Sugg::hir(cx, other_addend, ".."),
 +                        ),
 +                        Applicability::MachineApplicable,
 +                    );
-                 ref _lspan,
-                 largs,
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> {
 +    if let ExprKind::Binary(
 +        Spanned {
 +            node: BinOpKind::Add, ..
 +        },
 +        add_lhs,
 +        add_rhs,
 +    ) = args[0].kind
 +    {
 +        // check if expression of the form x * x + y * y
 +        if_chain! {
 +            if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lmul_lhs, lmul_rhs) = add_lhs.kind;
 +            if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, rmul_lhs, rmul_rhs) = add_rhs.kind;
 +            if eq_expr_value(cx, lmul_lhs, lmul_rhs);
 +            if eq_expr_value(cx, rmul_lhs, rmul_rhs);
 +            then {
 +                return Some(format!("{}.hypot({})", Sugg::hir(cx, lmul_lhs, ".."), Sugg::hir(cx, rmul_lhs, "..")));
 +            }
 +        }
 +
 +        // check if expression of the form x.powi(2) + y.powi(2)
 +        if_chain! {
 +            if let ExprKind::MethodCall(
 +                PathSegment { ident: lmethod_name, .. },
-             ) = add_lhs.kind;
++                _lspan,
++                [largs_0, largs_1, ..],
 +                _
-                 ref _rspan,
-                 rargs,
++            ) = &add_lhs.kind;
 +            if let ExprKind::MethodCall(
 +                PathSegment { ident: rmethod_name, .. },
-             ) = add_rhs.kind;
++                _rspan,
++                [rargs_0, rargs_1, ..],
 +                _
-             if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), &largs[1]);
-             if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), &rargs[1]);
++            ) = &add_rhs.kind;
 +            if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi";
-                 return Some(format!("{}.hypot({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], "..")));
++            if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), largs_1);
++            if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), rargs_1);
 +            if Int(2) == lvalue && Int(2) == rvalue;
 +            then {
-         if let ExprKind::MethodCall(path, _, method_args, _) = lhs.kind;
-         if cx.typeck_results().expr_ty(&method_args[0]).is_floating_point();
++                return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, ".."), Sugg::hir(cx, rargs_0, "..")));
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 +    if let Some(message) = detect_hypot(cx, args) {
 +        span_lint_and_sugg(
 +            cx,
 +            IMPRECISE_FLOPS,
 +            expr.span,
 +            "hypotenuse can be computed more accurately",
 +            "consider using",
 +            message,
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +// TODO: Lint expressions of the form `x.exp() - y` where y > 1
 +// and suggest usage of `x.exp_m1() - (y - 1)` instead
 +fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, lhs, rhs) = expr.kind;
 +        if cx.typeck_results().expr_ty(lhs).is_floating_point();
 +        if let Some((value, _)) = constant(cx, cx.typeck_results(), rhs);
 +        if F32(1.0) == value || F64(1.0) == value;
-                     Sugg::hir(cx, &method_args[0], "..")
++        if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &lhs.kind;
++        if cx.typeck_results().expr_ty(self_arg).is_floating_point();
 +        if path.ident.name.as_str() == "exp";
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                IMPRECISE_FLOPS,
 +                expr.span,
 +                "(e.pow(x) - 1) can be computed more accurately",
 +                "consider using",
 +                format!(
 +                    "{}.exp_m1()",
-         if let ExprKind::MethodCall(_, _, largs, _) = lhs.kind;
-         if let ExprKind::MethodCall(_, _, rargs, _) = rhs.kind;
++                    Sugg::hir(cx, self_arg, "..")
 +                ),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
 +    if_chain! {
 +        if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lhs, rhs) = &expr.kind;
 +        if cx.typeck_results().expr_ty(lhs).is_floating_point();
 +        if cx.typeck_results().expr_ty(rhs).is_floating_point();
 +        then {
 +            return Some((lhs, rhs));
 +        }
 +    }
 +
 +    None
 +}
 +
 +// TODO: Fix rust-lang/rust-clippy#4735
 +fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if let ExprKind::Binary(
 +        Spanned {
 +            node: BinOpKind::Add, ..
 +        },
 +        lhs,
 +        rhs,
 +    ) = &expr.kind
 +    {
 +        if let Some(parent) = get_parent_expr(cx, expr) {
 +            if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = parent.kind {
 +                if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
 +                    return;
 +                }
 +            }
 +        }
 +
 +        let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) {
 +            (inner_lhs, inner_rhs, rhs)
 +        } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) {
 +            (inner_lhs, inner_rhs, lhs)
 +        } else {
 +            return;
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            SUBOPTIMAL_FLOPS,
 +            expr.span,
 +            "multiply and add expressions can be calculated more efficiently and accurately",
 +            "consider using",
 +            format!(
 +                "{}.mul_add({}, {})",
 +                prepare_receiver_sugg(cx, recv),
 +                Sugg::hir(cx, arg1, ".."),
 +                Sugg::hir(cx, arg2, ".."),
 +            ),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +/// Returns true iff expr is an expression which tests whether or not
 +/// test is positive or an expression which tests whether or not test
 +/// is nonnegative.
 +/// Used for check-custom-abs function below
 +fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
 +    if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
 +        match op {
 +            BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, left, test),
 +            BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test),
 +            _ => false,
 +        }
 +    } else {
 +        false
 +    }
 +}
 +
 +/// See [`is_testing_positive`]
 +fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
 +    if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
 +        match op {
 +            BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, right, test),
 +            BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test),
 +            _ => false,
 +        }
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Returns true iff expr is some zero literal
 +fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    match constant_simple(cx, cx.typeck_results(), expr) {
 +        Some(Constant::Int(i)) => i == 0,
 +        Some(Constant::F32(f)) => f == 0.0,
 +        Some(Constant::F64(f)) => f == 0.0,
 +        _ => false,
 +    }
 +}
 +
 +/// If the two expressions are negations of each other, then it returns
 +/// a tuple, in which the first element is true iff expr1 is the
 +/// positive expressions, and the second element is the positive
 +/// one of the two expressions
 +/// If the two expressions are not negations of each other, then it
 +/// returns None.
 +fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> {
 +    if let ExprKind::Unary(UnOp::Neg, expr1_negated) = &expr1.kind {
 +        if eq_expr_value(cx, expr1_negated, expr2) {
 +            return Some((false, expr2));
 +        }
 +    }
 +    if let ExprKind::Unary(UnOp::Neg, expr2_negated) = &expr2.kind {
 +        if eq_expr_value(cx, expr1, expr2_negated) {
 +            return Some((true, expr1));
 +        }
 +    }
 +    None
 +}
 +
 +fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
 +        if let ExprKind::Block(block, _) = then.kind;
 +        if block.stmts.is_empty();
 +        if let Some(if_body_expr) = block.expr;
 +        if let Some(ExprKind::Block(else_block, _)) = r#else.map(|el| &el.kind);
 +        if else_block.stmts.is_empty();
 +        if let Some(else_body_expr) = else_block.expr;
 +        if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr);
 +        then {
 +            let positive_abs_sugg = (
 +                "manual implementation of `abs` method",
 +                format!("{}.abs()", Sugg::hir(cx, body, "..")),
 +            );
 +            let negative_abs_sugg = (
 +                "manual implementation of negation of `abs` method",
 +                format!("-{}.abs()", Sugg::hir(cx, body, "..")),
 +            );
 +            let sugg = if is_testing_positive(cx, cond, body) {
 +                if if_expr_positive {
 +                    positive_abs_sugg
 +                } else {
 +                    negative_abs_sugg
 +                }
 +            } else if is_testing_negative(cx, cond, body) {
 +                if if_expr_positive {
 +                    negative_abs_sugg
 +                } else {
 +                    positive_abs_sugg
 +                }
 +            } else {
 +                return;
 +            };
 +            span_lint_and_sugg(
 +                cx,
 +                SUBOPTIMAL_FLOPS,
 +                expr.span,
 +                sugg.0,
 +                "try",
 +                sugg.1,
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool {
 +    if_chain! {
 +        if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, args_a, _) = expr_a.kind;
 +        if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, args_b, _) = expr_b.kind;
 +        then {
 +            return method_name_a.as_str() == method_name_b.as_str() &&
 +                args_a.len() == args_b.len() &&
 +                (
 +                    ["ln", "log2", "log10"].contains(&&*method_name_a.as_str()) ||
 +                    method_name_a.as_str() == "log" && args_a.len() == 2 && eq_expr_value(cx, &args_a[1], &args_b[1])
 +                );
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    // check if expression of the form x.logN() / y.logN()
 +    if_chain! {
 +        if let ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Div, ..
 +            },
 +            lhs,
 +            rhs,
 +        ) = &expr.kind;
 +        if are_same_base_logs(cx, lhs, rhs);
-                 format!("{}.log({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."),),
++        if let ExprKind::MethodCall(_, _, [largs_self, ..], _) = &lhs.kind;
++        if let ExprKind::MethodCall(_, _, [rargs_self, ..], _) = &rhs.kind;
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                SUBOPTIMAL_FLOPS,
 +                expr.span,
 +                "log base can be expressed more clearly",
 +                "consider using",
++                format!("{}.log({})", Sugg::hir(cx, largs_self, ".."), Sugg::hir(cx, rargs_self, ".."),),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Div, ..
 +            },
 +            div_lhs,
 +            div_rhs,
 +        ) = &expr.kind;
 +        if let ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Mul, ..
 +            },
 +            mul_lhs,
 +            mul_rhs,
 +        ) = &div_lhs.kind;
 +        if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), div_rhs);
 +        if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), mul_rhs);
 +        then {
 +            // TODO: also check for constant values near PI/180 or 180/PI
 +            if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
 +               (F32(180_f32) == lvalue || F64(180_f64) == lvalue)
 +            {
 +                span_lint_and_sugg(
 +                    cx,
 +                    SUBOPTIMAL_FLOPS,
 +                    expr.span,
 +                    "conversion to degrees can be done more accurately",
 +                    "consider using",
 +                    format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..")),
 +                    Applicability::MachineApplicable,
 +                );
 +            } else if
 +                (F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
 +                (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
 +            {
 +                span_lint_and_sugg(
 +                    cx,
 +                    SUBOPTIMAL_FLOPS,
 +                    expr.span,
 +                    "conversion to radians can be done more accurately",
 +                    "consider using",
 +                    format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..")),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::MethodCall(path, _, args, _) = &expr.kind {
 +            let recv_ty = cx.typeck_results().expr_ty(&args[0]);
 +
 +            if recv_ty.is_floating_point() {
 +                match &*path.ident.name.as_str() {
 +                    "ln" => check_ln1p(cx, expr, args),
 +                    "log" => check_log_base(cx, expr, args),
 +                    "powf" => check_powf(cx, expr, args),
 +                    "powi" => check_powi(cx, expr, args),
 +                    "sqrt" => check_hypot(cx, expr, args),
 +                    _ => {},
 +                }
 +            }
 +        } else {
 +            check_expm1(cx, expr);
 +            check_mul_add(cx, expr);
 +            check_custom_abs(cx, expr);
 +            check_log_division(cx, expr);
 +            check_radians(cx, expr);
 +        }
 +    }
 +}
index ea6193acbe84518ade96aef8d33277b5c5bedac7,0000000000000000000000000000000000000000..77d08081c07f1d2bf07911b33de74f4a9f0148ef
mode 100644,000000..100644
--- /dev/null
@@@ -1,272 -1,0 +1,271 @@@
-             return;
 +use rustc_ast::ast::Attribute;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::{DefIdSet, LocalDefId};
 +use rustc_hir::{self as hir, def::Res, intravisit, QPath};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::{
 +    hir::map::Map,
 +    lint::in_external_macro,
 +    ty::{self, Ty},
 +};
 +use rustc_span::{sym, Span};
 +
 +use clippy_utils::attrs::is_proc_macro;
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::is_must_use_ty;
 +use clippy_utils::{match_def_path, must_use_attr, return_ty, trait_ref_of_method};
 +
 +use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT};
 +
 +pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +    let attrs = cx.tcx.hir().attrs(item.hir_id());
 +    let attr = must_use_attr(attrs);
 +    if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind {
 +        let is_public = cx.access_levels.is_exported(item.def_id);
 +        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +        if let Some(attr) = attr {
 +            check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
 +        } else if is_public && !is_proc_macro(cx.sess(), attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) {
 +            check_must_use_candidate(
 +                cx,
 +                sig.decl,
 +                cx.tcx.hir().body(*body_id),
 +                item.span,
 +                item.def_id,
 +                item.span.with_hi(sig.decl.output.span().hi()),
 +                "this function could have a `#[must_use]` attribute",
 +            );
 +        }
 +    }
 +}
 +
 +pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
 +    if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind {
 +        let is_public = cx.access_levels.is_exported(item.def_id);
 +        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let attr = must_use_attr(attrs);
 +        if let Some(attr) = attr {
 +            check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
 +        } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.hir_id()).is_none() {
 +            check_must_use_candidate(
 +                cx,
 +                sig.decl,
 +                cx.tcx.hir().body(*body_id),
 +                item.span,
 +                item.def_id,
 +                item.span.with_hi(sig.decl.output.span().hi()),
 +                "this method could have a `#[must_use]` attribute",
 +            );
 +        }
 +    }
 +}
 +
 +pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
 +    if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind {
 +        let is_public = cx.access_levels.is_exported(item.def_id);
 +        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let attr = must_use_attr(attrs);
 +        if let Some(attr) = attr {
 +            check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
 +        } else if let hir::TraitFn::Provided(eid) = *eid {
 +            let body = cx.tcx.hir().body(eid);
 +            if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) {
 +                check_must_use_candidate(
 +                    cx,
 +                    sig.decl,
 +                    body,
 +                    item.span,
 +                    item.def_id,
 +                    item.span.with_hi(sig.decl.output.span().hi()),
 +                    "this method could have a `#[must_use]` attribute",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn check_needless_must_use(
 +    cx: &LateContext<'_>,
 +    decl: &hir::FnDecl<'_>,
 +    item_id: hir::HirId,
 +    item_span: Span,
 +    fn_header_span: Span,
 +    attr: &Attribute,
 +) {
 +    if in_external_macro(cx.sess(), item_span) {
 +        return;
 +    }
 +    if returns_unit(decl) {
 +        span_lint_and_then(
 +            cx,
 +            MUST_USE_UNIT,
 +            fn_header_span,
 +            "this unit-returning function has a `#[must_use]` attribute",
 +            |diag| {
 +                diag.span_suggestion(
 +                    attr.span,
 +                    "remove the attribute",
 +                    "".into(),
 +                    Applicability::MachineApplicable,
 +                );
 +            },
 +        );
 +    } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
 +        span_lint_and_help(
 +            cx,
 +            DOUBLE_MUST_USE,
 +            fn_header_span,
 +            "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`",
 +            None,
 +            "either add some descriptive text or remove the attribute",
 +        );
 +    }
 +}
 +
 +fn check_must_use_candidate<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    decl: &'tcx hir::FnDecl<'_>,
 +    body: &'tcx hir::Body<'_>,
 +    item_span: Span,
 +    item_id: LocalDefId,
 +    fn_span: Span,
 +    msg: &str,
 +) {
 +    if has_mutable_arg(cx, body)
 +        || mutates_static(cx, body)
 +        || in_external_macro(cx.sess(), item_span)
 +        || returns_unit(decl)
 +        || !cx.access_levels.is_exported(item_id)
 +        || is_must_use_ty(cx, return_ty(cx, cx.tcx.hir().local_def_id_to_hir_id(item_id)))
 +    {
 +        return;
 +    }
 +    span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| {
 +        if let Some(snippet) = snippet_opt(cx, fn_span) {
 +            diag.span_suggestion(
 +                fn_span,
 +                "add the attribute",
 +                format!("#[must_use] {}", snippet),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    });
 +}
 +
 +fn returns_unit(decl: &hir::FnDecl<'_>) -> bool {
 +    match decl.output {
 +        hir::FnRetTy::DefaultReturn(_) => true,
 +        hir::FnRetTy::Return(ty) => match ty.kind {
 +            hir::TyKind::Tup(tys) => tys.is_empty(),
 +            hir::TyKind::Never => true,
 +            _ => false,
 +        },
 +    }
 +}
 +
 +fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool {
 +    let mut tys = DefIdSet::default();
 +    body.params.iter().any(|param| is_mutable_pat(cx, param.pat, &mut tys))
 +}
 +
 +fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) -> bool {
 +    if let hir::PatKind::Wild = pat.kind {
 +        return false; // ignore `_` patterns
 +    }
 +    if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) {
 +        is_mutable_ty(cx, cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys)
 +    } else {
 +        false
 +    }
 +}
 +
 +static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]];
 +
 +fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut DefIdSet) -> bool {
 +    match *ty.kind() {
 +        // primitive types are never mutable
 +        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
 +        ty::Adt(adt, substs) => {
 +            tys.insert(adt.did) && !ty.is_freeze(cx.tcx.at(span), cx.param_env)
 +                || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path))
 +                    && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys))
 +        },
 +        ty::Tuple(substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)),
 +        ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys),
 +        ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => {
 +            mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys)
 +        },
 +        // calling something constitutes a side effect, so return true on all callables
 +        // also never calls need not be used, so return true for them, too
 +        _ => true,
 +    }
 +}
 +
 +struct StaticMutVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    mutates_static: bool,
 +}
 +
 +impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
 +        use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
 +
 +        if self.mutates_static {
 +            return;
 +        }
 +        match expr.kind {
 +            Call(_, args) | MethodCall(_, _, args, _) => {
 +                let mut tys = DefIdSet::default();
 +                for arg in args {
 +                    if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
 +                        && is_mutable_ty(
 +                            self.cx,
 +                            self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg),
 +                            arg.span,
 +                            &mut tys,
 +                        )
 +                        && is_mutated_static(arg)
 +                    {
 +                        self.mutates_static = true;
 +                        return;
 +                    }
 +                    tys.clear();
 +                }
 +            },
 +            Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target) => {
 +                self.mutates_static |= is_mutated_static(target);
 +            },
 +            _ => {},
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
 +        intravisit::NestedVisitorMap::None
 +    }
 +}
 +
 +fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
 +    use hir::ExprKind::{Field, Index, Path};
 +
 +    match e.kind {
 +        Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)),
 +        Path(_) => true,
 +        Field(inner, _) | Index(inner, _) => is_mutated_static(inner),
 +        _ => false,
 +    }
 +}
 +
 +fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool {
 +    let mut v = StaticMutVisitor {
 +        cx,
 +        mutates_static: false,
 +    };
 +    intravisit::walk_expr(&mut v, &body.value);
 +    v.mutates_static
 +}
index 7dad1c31150e2cc49b6dfba66391a7f0dbce6062,0000000000000000000000000000000000000000..ef72b88b3c7735fed581648e8df34c8ad250f253
mode 100644,000000..100644
--- /dev/null
@@@ -1,151 -1,0 +1,151 @@@
-         if let ExprKind::MethodCall(path, _span, args, _) = &expr.kind;
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::higher;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::SpanlessEq;
 +use if_chain::if_chain;
 +use rustc_hir::intravisit::{self as visit, NestedVisitorMap, Visitor};
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::map::Map;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `Mutex::lock` calls in `if let` expression
 +    /// with lock calls in any of the else blocks.
 +    ///
 +    /// ### Why is this bad?
 +    /// The Mutex lock remains held for the whole
 +    /// `if let ... else` block and deadlocks.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// if let Ok(thing) = mutex.lock() {
 +    ///     do_thing();
 +    /// } else {
 +    ///     mutex.lock();
 +    /// }
 +    /// ```
 +    /// Should be written
 +    /// ```rust,ignore
 +    /// let locked = mutex.lock();
 +    /// if let Ok(thing) = locked {
 +    ///     do_thing(thing);
 +    /// } else {
 +    ///     use_locked(locked);
 +    /// }
 +    /// ```
 +    pub IF_LET_MUTEX,
 +    correctness,
 +    "locking a `Mutex` in an `if let` block can cause deadlocks"
 +}
 +
 +declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]);
 +
 +impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
 +        let mut arm_visit = ArmVisitor {
 +            mutex_lock_called: false,
 +            found_mutex: None,
 +            cx,
 +        };
 +        let mut op_visit = OppVisitor {
 +            mutex_lock_called: false,
 +            found_mutex: None,
 +            cx,
 +        };
 +        if let Some(higher::IfLet {
 +            let_expr,
 +            if_then,
 +            if_else: Some(if_else),
 +            ..
 +        }) = higher::IfLet::hir(cx, expr)
 +        {
 +            op_visit.visit_expr(let_expr);
 +            if op_visit.mutex_lock_called {
 +                arm_visit.visit_expr(if_then);
 +                arm_visit.visit_expr(if_else);
 +
 +                if arm_visit.mutex_lock_called && arm_visit.same_mutex(cx, op_visit.found_mutex.unwrap()) {
 +                    span_lint_and_help(
 +                        cx,
 +                        IF_LET_MUTEX,
 +                        expr.span,
 +                        "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
 +                        None,
 +                        "move the lock call outside of the `if let ...` expression",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Checks if `Mutex::lock` is called in the `if let` expr.
 +pub struct OppVisitor<'a, 'tcx> {
 +    mutex_lock_called: bool,
 +    found_mutex: Option<&'tcx Expr<'tcx>>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
 +            self.found_mutex = Some(mutex);
 +            self.mutex_lock_called = true;
 +            return;
 +        }
 +        visit::walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
 +/// Checks if `Mutex::lock` is called in any of the branches.
 +pub struct ArmVisitor<'a, 'tcx> {
 +    mutex_lock_called: bool,
 +    found_mutex: Option<&'tcx Expr<'tcx>>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 +        if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
 +            self.found_mutex = Some(mutex);
 +            self.mutex_lock_called = true;
 +            return;
 +        }
 +        visit::walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
 +impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
 +    fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool {
 +        self.found_mutex
 +            .map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex))
 +    }
 +}
 +
 +fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    if_chain! {
-         let ty = cx.typeck_results().expr_ty(&args[0]);
++        if let ExprKind::MethodCall(path, _span, [self_arg, ..], _) = &expr.kind;
 +        if path.ident.as_str() == "lock";
-             Some(&args[0])
++        let ty = cx.typeck_results().expr_ty(self_arg);
 +        if is_type_diagnostic_item(cx, ty, sym!(mutex_type));
 +        then {
++            Some(self_arg)
 +        } else {
 +            None
 +        }
 +    }
 +}
index fb5637fcec183559a9b18cc5c53a13784031c943,0000000000000000000000000000000000000000..adcd78ed0d42745adc2e0cb2d6518a3f98c5243e
mode 100644,000000..100644
--- /dev/null
@@@ -1,76 -1,0 +1,76 @@@
-             if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
-             if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _)  = let_pat.kind; //get operation
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::method_chain_args;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, PatKind, QPath};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///* Checks for unnecessary `ok()` in if let.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `ok()` in if let is unnecessary, instead match
 +    /// on `Ok(pat)`
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// for i in iter {
 +    ///     if let Some(value) = i.parse().ok() {
 +    ///         vec.push(value)
 +    ///     }
 +    /// }
 +    /// ```
 +    /// Could be written:
 +    ///
 +    /// ```ignore
 +    /// for i in iter {
 +    ///     if let Ok(value) = i.parse() {
 +    ///         vec.push(value)
 +    ///     }
 +    /// }
 +    /// ```
 +    pub IF_LET_SOME_RESULT,
 +    style,
 +    "usage of `ok()` in `if let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
 +}
 +
 +declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for OkIfLet {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! { //begin checking variables
 +            if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr);
-             if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym::result_type);
++            if let ExprKind::MethodCall(_, ok_span, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
++            if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _)  = let_pat.kind; //get operation
 +            if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
++            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::result_type);
 +            if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
 +
 +            then {
 +                let mut applicability = Applicability::MachineApplicable;
 +                let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
 +                let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_span), "", &mut applicability);
 +                let sugg = format!(
 +                    "if let Ok({}) = {}",
 +                    some_expr_string,
 +                    trimmed_ok.trim().trim_end_matches('.'),
 +                );
 +                span_lint_and_sugg(
 +                    cx,
 +                    IF_LET_SOME_RESULT,
 +                    expr.span.with_hi(let_expr.span.hi()),
 +                    "matching on `Some` with `ok()` is redundant",
 +                    &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),
 +                    sugg,
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +}
index 0594b73dd38378dd17f4821f639fb76ea8e4815a,0000000000000000000000000000000000000000..7f2c7b707f0b1290968371fbb65e45b4dd32b86d
mode 100644,000000..100644
--- /dev/null
@@@ -1,162 -1,0 +1,159 @@@
- use clippy_utils::{path_to_local_id, visitors::LocalUsedVisitor};
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet;
-                 let mut used_visitor = LocalUsedVisitor::new(cx, canonical_id);
-                 if !used_visitor.check_expr(cond);
++use clippy_utils::{path_to_local_id, visitors::is_local_used};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::BindingAnnotation;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for variable declarations immediately followed by a
 +    /// conditional affectation.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is not idiomatic Rust.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let foo;
 +    ///
 +    /// if bar() {
 +    ///     foo = 42;
 +    /// } else {
 +    ///     foo = 0;
 +    /// }
 +    ///
 +    /// let mut baz = None;
 +    ///
 +    /// if bar() {
 +    ///     baz = Some(42);
 +    /// }
 +    /// ```
 +    ///
 +    /// should be written
 +    ///
 +    /// ```rust,ignore
 +    /// let foo = if bar() {
 +    ///     42
 +    /// } else {
 +    ///     0
 +    /// };
 +    ///
 +    /// let baz = if bar() {
 +    ///     Some(42)
 +    /// } else {
 +    ///     None
 +    /// };
 +    /// ```
 +    pub USELESS_LET_IF_SEQ,
 +    nursery,
 +    "unidiomatic `let mut` declaration followed by initialization in `if`"
 +}
 +
 +declare_lint_pass!(LetIfSeq => [USELESS_LET_IF_SEQ]);
 +
 +impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
 +        let mut it = block.stmts.iter().peekable();
 +        while let Some(stmt) = it.next() {
 +            if_chain! {
 +                if let Some(expr) = it.peek();
 +                if let hir::StmtKind::Local(local) = stmt.kind;
 +                if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind;
 +                if let hir::StmtKind::Expr(if_) = expr.kind;
 +                if let hir::ExprKind::If(hir::Expr { kind: hir::ExprKind::DropTemps(cond), ..}, then, else_) = if_.kind;
-                 if !used_visitor.check_expr(value);
++                if !is_local_used(cx, *cond, canonical_id);
 +                if let hir::ExprKind::Block(then, _) = then.kind;
 +                if let Some(value) = check_assign(cx, canonical_id, &*then);
-             let mut v = LocalUsedVisitor::new(cx, decl);
-             if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| v.check_stmt(stmt)) {
-                 return None;
++                if !is_local_used(cx, value, canonical_id);
 +                then {
 +                    let span = stmt.span.to(if_.span);
 +
 +                    let has_interior_mutability = !cx.typeck_results().node_type(canonical_id).is_freeze(
 +                        cx.tcx.at(span),
 +                        cx.param_env,
 +                    );
 +                    if has_interior_mutability { return; }
 +
 +                    let (default_multi_stmts, default) = if let Some(else_) = else_ {
 +                        if let hir::ExprKind::Block(else_, _) = else_.kind {
 +                            if let Some(default) = check_assign(cx, canonical_id, else_) {
 +                                (else_.stmts.len() > 1, default)
 +                            } else if let Some(default) = local.init {
 +                                (true, default)
 +                            } else {
 +                                continue;
 +                            }
 +                        } else {
 +                            continue;
 +                        }
 +                    } else if let Some(default) = local.init {
 +                        (false, default)
 +                    } else {
 +                        continue;
 +                    };
 +
 +                    let mutability = match mode {
 +                        BindingAnnotation::RefMut | BindingAnnotation::Mutable => "<mut> ",
 +                        _ => "",
 +                    };
 +
 +                    // FIXME: this should not suggest `mut` if we can detect that the variable is not
 +                    // use mutably after the `if`
 +
 +                    let sug = format!(
 +                        "let {mut}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};",
 +                        mut=mutability,
 +                        name=ident.name,
 +                        cond=snippet(cx, cond.span, "_"),
 +                        then=if then.stmts.len() > 1 { " ..;" } else { "" },
 +                        else=if default_multi_stmts { " ..;" } else { "" },
 +                        value=snippet(cx, value.span, "<value>"),
 +                        default=snippet(cx, default.span, "<default>"),
 +                    );
 +                    span_lint_and_then(cx,
 +                                       USELESS_LET_IF_SEQ,
 +                                       span,
 +                                       "`if _ { .. } else { .. }` is an expression",
 +                                       |diag| {
 +                                           diag.span_suggestion(
 +                                                span,
 +                                                "it is more idiomatic to write",
 +                                                sug,
 +                                                Applicability::HasPlaceholders,
 +                                            );
 +                                           if !mutability.is_empty() {
 +                                               diag.note("you might not need `mut` at all");
 +                                           }
 +                                       });
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn check_assign<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    decl: hir::HirId,
 +    block: &'tcx hir::Block<'_>,
 +) -> Option<&'tcx hir::Expr<'tcx>> {
 +    if_chain! {
 +        if block.expr.is_none();
 +        if let Some(expr) = block.stmts.iter().last();
 +        if let hir::StmtKind::Semi(expr) = expr.kind;
 +        if let hir::ExprKind::Assign(var, value, _) = expr.kind;
 +        if path_to_local_id(var, decl);
 +        then {
-             return Some(value);
++            if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| is_local_used(cx, stmt, decl)) {
++                None
++            } else {
++                Some(value)
 +            }
-     None
++        } else {
++            None
 +        }
 +    }
 +}
index 19719502870bd0df3f2fe2fc70fbe7b3c45660e7,0000000000000000000000000000000000000000..92a13be81f411374547df641fe588a8220a80e58
mode 100644,000000..100644
--- /dev/null
@@@ -1,2191 -1,0 +1,2220 @@@
-         LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
 +// error-pattern:cargo-clippy
 +
 +#![feature(box_patterns)]
 +#![feature(drain_filter)]
 +#![feature(in_band_lifetimes)]
 +#![feature(iter_zip)]
 +#![feature(once_cell)]
 +#![feature(rustc_private)]
 +#![feature(stmt_expr_attributes)]
 +#![feature(control_flow_enum)]
 +#![recursion_limit = "512"]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
 +#![warn(trivial_casts, trivial_numeric_casts)]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_ast;
 +extern crate rustc_ast_pretty;
 +extern crate rustc_data_structures;
 +extern crate rustc_driver;
 +extern crate rustc_errors;
 +extern crate rustc_hir;
 +extern crate rustc_hir_pretty;
 +extern crate rustc_index;
 +extern crate rustc_infer;
 +extern crate rustc_lexer;
 +extern crate rustc_lint;
 +extern crate rustc_middle;
 +extern crate rustc_mir;
 +extern crate rustc_parse;
 +extern crate rustc_parse_format;
 +extern crate rustc_session;
 +extern crate rustc_span;
 +extern crate rustc_target;
 +extern crate rustc_trait_selection;
 +extern crate rustc_typeck;
 +
 +#[macro_use]
 +extern crate clippy_utils;
 +
 +use clippy_utils::parse_msrv;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_lint::LintId;
 +use rustc_session::Session;
 +
 +/// Macro used to declare a Clippy lint.
 +///
 +/// Every lint declaration consists of 4 parts:
 +///
 +/// 1. The documentation, which is used for the website
 +/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions.
 +/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or
 +///    `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of.
 +/// 4. The `description` that contains a short explanation on what's wrong with code where the
 +///    lint is triggered.
 +///
 +/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are
 +/// enabled by default. As said in the README.md of this repository, if the lint level mapping
 +/// changes, please update README.md.
 +///
 +/// # Example
 +///
 +/// ```
 +/// #![feature(rustc_private)]
 +/// extern crate rustc_session;
 +/// use rustc_session::declare_tool_lint;
 +/// use clippy_lints::declare_clippy_lint;
 +///
 +/// declare_clippy_lint! {
 +///     /// ### What it does
 +///     /// Checks for ... (describe what the lint matches).
 +///     ///
 +///     /// ### Why is this bad?
 +///     /// Supply the reason for linting the code.
 +///     ///
 +///     /// ### Example
 +///     /// ```rust
 +///     /// // Bad
 +///     /// Insert a short example of code that triggers the lint
 +///     ///
 +///     /// // Good
 +///     /// Insert a short example of improved code that doesn't trigger the lint
 +///     /// ```
 +///     pub LINT_NAME,
 +///     pedantic,
 +///     "description"
 +/// }
 +/// ```
 +/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
 +#[macro_export]
 +macro_rules! declare_clippy_lint {
 +    { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, suspicious, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +}
 +
 +#[cfg(feature = "metadata-collector-lint")]
 +mod deprecated_lints;
 +mod utils;
 +
 +// begin lints modules, do not remove this comment, it’s used in `update_lints`
 +mod absurd_extreme_comparisons;
 +mod approx_const;
 +mod arithmetic;
 +mod as_conversions;
 +mod asm_syntax;
 +mod assertions_on_constants;
 +mod assign_ops;
 +mod async_yields_async;
 +mod attrs;
 +mod await_holding_invalid;
 +mod bit_mask;
 +mod blacklisted_name;
 +mod blocks_in_if_conditions;
 +mod bool_assert_comparison;
 +mod booleans;
 +mod bytecount;
 +mod cargo_common_metadata;
 +mod case_sensitive_file_extension_comparisons;
 +mod casts;
 +mod checked_conversions;
 +mod cognitive_complexity;
 +mod collapsible_if;
 +mod collapsible_match;
 +mod comparison_chain;
 +mod copies;
 +mod copy_iterator;
 +mod create_dir;
 +mod dbg_macro;
 +mod default;
 +mod default_numeric_fallback;
 +mod dereference;
++mod derivable_impls;
 +mod derive;
 +mod disallowed_method;
 +mod disallowed_script_idents;
 +mod disallowed_type;
 +mod doc;
 +mod double_comparison;
 +mod double_parens;
 +mod drop_forget_ref;
 +mod duration_subsec;
 +mod else_if_without_else;
 +mod empty_enum;
 +mod entry;
 +mod enum_clike;
 +mod enum_variants;
 +mod eq_op;
 +mod erasing_op;
 +mod escape;
 +mod eta_reduction;
 +mod eval_order_dependence;
 +mod excessive_bools;
 +mod exhaustive_items;
 +mod exit;
 +mod explicit_write;
 +mod fallible_impl_from;
++mod feature_name;
 +mod float_equality_without_abs;
 +mod float_literal;
 +mod floating_point_arithmetic;
 +mod format;
 +mod formatting;
 +mod from_over_into;
 +mod from_str_radix_10;
 +mod functions;
 +mod future_not_send;
 +mod get_last_with_len;
 +mod identity_op;
 +mod if_let_mutex;
 +mod if_let_some_result;
 +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 indexing_slicing;
 +mod infinite_iter;
 +mod inherent_impl;
 +mod inherent_to_string;
 +mod inline_fn_without_body;
 +mod int_plus_one;
 +mod integer_division;
 +mod invalid_upcast_comparisons;
 +mod items_after_statements;
 +mod large_const_arrays;
 +mod large_enum_variant;
 +mod large_stack_arrays;
 +mod len_zero;
 +mod let_if_seq;
 +mod let_underscore;
 +mod lifetimes;
 +mod literal_representation;
 +mod loops;
 +mod macro_use;
 +mod main_recursion;
 +mod manual_async_fn;
 +mod manual_map;
 +mod manual_non_exhaustive;
 +mod manual_ok_or;
 +mod manual_strip;
 +mod manual_unwrap_or;
 +mod map_clone;
 +mod map_err_ignore;
 +mod map_unit_fn;
 +mod match_on_vec_items;
 +mod matches;
 +mod mem_discriminant;
 +mod mem_forget;
 +mod mem_replace;
 +mod methods;
 +mod minmax;
 +mod misc;
 +mod misc_early;
 +mod missing_const_for_fn;
 +mod missing_doc;
 +mod missing_enforced_import_rename;
 +mod missing_inline;
++mod module_style;
 +mod modulo_arithmetic;
 +mod multiple_crate_versions;
 +mod mut_key;
 +mod mut_mut;
 +mod mut_mutex_lock;
 +mod mut_reference;
 +mod mutable_debug_assertion;
 +mod mutex_atomic;
 +mod needless_arbitrary_self_type;
 +mod needless_bitwise_bool;
 +mod needless_bool;
 +mod needless_borrow;
 +mod needless_borrowed_ref;
 +mod needless_continue;
 +mod needless_for_each;
++mod needless_option_as_deref;
 +mod needless_pass_by_value;
 +mod needless_question_mark;
 +mod needless_update;
 +mod neg_cmp_op_on_partial_ord;
 +mod neg_multiply;
 +mod new_without_default;
 +mod no_effect;
 +mod non_copy_const;
 +mod non_expressive_names;
 +mod non_octal_unix_permissions;
 +mod nonstandard_macro_braces;
 +mod open_options;
 +mod option_env_unwrap;
 +mod option_if_let_else;
 +mod overflow_check_conditional;
 +mod panic_in_result_fn;
 +mod panic_unimplemented;
 +mod partialeq_ne_impl;
 +mod pass_by_ref_or_value;
 +mod path_buf_push_overwrite;
 +mod pattern_type_mismatch;
 +mod precedence;
 +mod ptr;
 +mod ptr_eq;
 +mod ptr_offset_with_cast;
 +mod question_mark;
 +mod ranges;
 +mod redundant_clone;
 +mod redundant_closure_call;
 +mod redundant_else;
 +mod redundant_field_names;
 +mod redundant_pub_crate;
 +mod redundant_slicing;
 +mod redundant_static_lifetimes;
 +mod ref_option_ref;
 +mod reference;
 +mod regex;
 +mod repeat_once;
 +mod returns;
 +mod self_assignment;
 +mod self_named_constructors;
 +mod semicolon_if_nothing_returned;
 +mod serde_api;
 +mod shadow;
 +mod single_component_path_imports;
 +mod size_of_in_element_count;
 +mod slow_vector_initialization;
 +mod stable_sort_primitive;
 +mod strings;
 +mod strlen_on_c_strings;
 +mod suspicious_operation_groupings;
 +mod suspicious_trait_impl;
 +mod swap;
 +mod tabs_in_doc_comments;
 +mod temporary_assignment;
 +mod to_digit_is_some;
 +mod to_string_in_display;
 +mod trait_bounds;
 +mod transmute;
 +mod transmuting_null;
 +mod try_err;
 +mod types;
 +mod undropped_manually_drops;
 +mod unicode;
 +mod unit_return_expecting_ord;
 +mod unit_types;
 +mod unnamed_address;
 +mod unnecessary_self_imports;
 +mod unnecessary_sort_by;
 +mod unnecessary_wraps;
 +mod unnested_or_patterns;
 +mod unsafe_removed_from_name;
 +mod unused_async;
 +mod unused_io_amount;
 +mod unused_self;
 +mod unused_unit;
 +mod unwrap;
 +mod unwrap_in_result;
 +mod upper_case_acronyms;
 +mod use_self;
 +mod useless_conversion;
 +mod vec;
 +mod vec_init_then_push;
 +mod vec_resize_to_zero;
 +mod verbose_file_reads;
 +mod wildcard_dependencies;
 +mod wildcard_imports;
 +mod write;
 +mod zero_div_zero;
 +mod zero_sized_map_values;
 +// end lints modules, do not remove this comment, it’s used in `update_lints`
 +
 +pub use crate::utils::conf::Conf;
 +use crate::utils::conf::TryConf;
 +
 +/// Register all pre expansion lints
 +///
 +/// Pre-expansion lints run before any macro expansion has happened.
 +///
 +/// Note that due to the architecture of the compiler, currently `cfg_attr` attributes on crate
 +/// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
 +///
 +/// Used in `./src/driver.rs`.
 +pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) {
 +    // NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
 +    store.register_pre_expansion_pass(|| Box::new(write::Write::default()));
 +    store.register_pre_expansion_pass(|| Box::new(attrs::EarlyAttributes));
 +    store.register_pre_expansion_pass(|| Box::new(dbg_macro::DbgMacro));
 +}
 +
 +#[doc(hidden)]
 +pub fn read_conf(sess: &Session) -> Conf {
 +    let file_name = match utils::conf::lookup_conf_file() {
 +        Ok(Some(path)) => path,
 +        Ok(None) => return Conf::default(),
 +        Err(error) => {
 +            sess.struct_err(&format!("error finding Clippy's configuration file: {}", error))
 +                .emit();
 +            return Conf::default();
 +        },
 +    };
 +
 +    let TryConf { conf, errors } = utils::conf::read(&file_name);
 +    // all conf errors are non-fatal, we just use the default conf in case of error
 +    for error in errors {
 +        sess.struct_err(&format!(
 +            "error reading Clippy's configuration file `{}`: {}",
 +            file_name.display(),
 +            error
 +        ))
 +        .emit();
 +    }
 +
 +    conf
 +}
 +
 +/// Register all lints and lint groups with the rustc plugin registry
 +///
 +/// Used in `./src/driver.rs`.
 +#[allow(clippy::too_many_lines)]
 +#[rustfmt::skip]
 +pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
 +    register_removed_non_tool_lints(store);
 +
 +    // begin deprecated lints, do not remove this comment, it’s used in `update_lints`
 +    store.register_removed(
 +        "clippy::should_assert_eq",
 +        "`assert!()` will be more flexible with RFC 2011",
 +    );
 +    store.register_removed(
 +        "clippy::extend_from_slice",
 +        "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice",
 +    );
 +    store.register_removed(
 +        "clippy::range_step_by_zero",
 +        "`iterator.step_by(0)` panics nowadays",
 +    );
 +    store.register_removed(
 +        "clippy::unstable_as_slice",
 +        "`Vec::as_slice` has been stabilized in 1.7",
 +    );
 +    store.register_removed(
 +        "clippy::unstable_as_mut_slice",
 +        "`Vec::as_mut_slice` has been stabilized in 1.7",
 +    );
 +    store.register_removed(
 +        "clippy::misaligned_transmute",
 +        "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
 +    );
 +    store.register_removed(
 +        "clippy::assign_ops",
 +        "using compound assignment operators (e.g., `+=`) is harmless",
 +    );
 +    store.register_removed(
 +        "clippy::if_let_redundant_pattern_matching",
 +        "this lint has been changed to redundant_pattern_matching",
 +    );
 +    store.register_removed(
 +        "clippy::unsafe_vector_initialization",
 +        "the replacement suggested by this lint had substantially different behavior",
 +    );
 +    store.register_removed(
 +        "clippy::unused_collect",
 +        "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint",
 +    );
 +    store.register_removed(
 +        "clippy::replace_consts",
 +        "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants",
 +    );
 +    store.register_removed(
 +        "clippy::regex_macro",
 +        "the regex! macro has been removed from the regex crate in 2018",
 +    );
 +    store.register_removed(
 +        "clippy::find_map",
 +        "this lint has been replaced by `manual_find_map`, a more specific lint",
 +    );
 +    store.register_removed(
 +        "clippy::filter_map",
 +        "this lint has been replaced by `manual_filter_map`, a more specific lint",
 +    );
 +    store.register_removed(
 +        "clippy::pub_enum_variant_names",
 +        "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items",
 +    );
 +    store.register_removed(
 +        "clippy::wrong_pub_self_convention",
 +        "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items",
 +    );
 +    // end deprecated lints, do not remove this comment, it’s used in `update_lints`
 +
 +    // begin register lints, do not remove this comment, it’s used in `update_lints`
 +    store.register_lints(&[
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::CLIPPY_LINTS_INTERNAL,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::COMPILER_LINT_FUNCTIONS,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::DEFAULT_LINT,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::IF_CHAIN_STYLE,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::INTERNING_DEFINED_SYMBOL,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::INVALID_PATHS,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::LINT_WITHOUT_LINT_PASS,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::OUTER_EXPN_EXPN_DATA,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::PRODUCE_ICE,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::UNNECESSARY_SYMBOL_STR,
 +        absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
 +        approx_const::APPROX_CONSTANT,
 +        arithmetic::FLOAT_ARITHMETIC,
 +        arithmetic::INTEGER_ARITHMETIC,
 +        as_conversions::AS_CONVERSIONS,
 +        asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
 +        asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
 +        assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
 +        assign_ops::ASSIGN_OP_PATTERN,
 +        assign_ops::MISREFACTORED_ASSIGN_OP,
 +        async_yields_async::ASYNC_YIELDS_ASYNC,
 +        attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
 +        attrs::DEPRECATED_CFG_ATTR,
 +        attrs::DEPRECATED_SEMVER,
 +        attrs::EMPTY_LINE_AFTER_OUTER_ATTR,
 +        attrs::INLINE_ALWAYS,
 +        attrs::MISMATCHED_TARGET_OS,
 +        attrs::USELESS_ATTRIBUTE,
 +        await_holding_invalid::AWAIT_HOLDING_LOCK,
 +        await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
 +        bit_mask::BAD_BIT_MASK,
 +        bit_mask::INEFFECTIVE_BIT_MASK,
 +        bit_mask::VERBOSE_BIT_MASK,
 +        blacklisted_name::BLACKLISTED_NAME,
 +        blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
 +        bool_assert_comparison::BOOL_ASSERT_COMPARISON,
 +        booleans::LOGIC_BUG,
 +        booleans::NONMINIMAL_BOOL,
 +        bytecount::NAIVE_BYTECOUNT,
 +        cargo_common_metadata::CARGO_COMMON_METADATA,
 +        case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
 +        casts::CAST_LOSSLESS,
 +        casts::CAST_POSSIBLE_TRUNCATION,
 +        casts::CAST_POSSIBLE_WRAP,
 +        casts::CAST_PRECISION_LOSS,
 +        casts::CAST_PTR_ALIGNMENT,
 +        casts::CAST_REF_TO_MUT,
 +        casts::CAST_SIGN_LOSS,
 +        casts::CHAR_LIT_AS_U8,
 +        casts::FN_TO_NUMERIC_CAST,
 +        casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
 +        casts::PTR_AS_PTR,
 +        casts::UNNECESSARY_CAST,
 +        checked_conversions::CHECKED_CONVERSIONS,
 +        cognitive_complexity::COGNITIVE_COMPLEXITY,
 +        collapsible_if::COLLAPSIBLE_ELSE_IF,
 +        collapsible_if::COLLAPSIBLE_IF,
 +        collapsible_match::COLLAPSIBLE_MATCH,
 +        comparison_chain::COMPARISON_CHAIN,
 +        copies::BRANCHES_SHARING_CODE,
 +        copies::IFS_SAME_COND,
 +        copies::IF_SAME_THEN_ELSE,
 +        copies::SAME_FUNCTIONS_IN_IF_CONDITION,
 +        copy_iterator::COPY_ITERATOR,
 +        create_dir::CREATE_DIR,
 +        dbg_macro::DBG_MACRO,
 +        default::DEFAULT_TRAIT_ACCESS,
 +        default::FIELD_REASSIGN_WITH_DEFAULT,
 +        default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
 +        dereference::EXPLICIT_DEREF_METHODS,
++        derivable_impls::DERIVABLE_IMPLS,
 +        derive::DERIVE_HASH_XOR_EQ,
 +        derive::DERIVE_ORD_XOR_PARTIAL_ORD,
 +        derive::EXPL_IMPL_CLONE_ON_COPY,
 +        derive::UNSAFE_DERIVE_DESERIALIZE,
 +        disallowed_method::DISALLOWED_METHOD,
 +        disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
 +        disallowed_type::DISALLOWED_TYPE,
 +        doc::DOC_MARKDOWN,
 +        doc::MISSING_ERRORS_DOC,
 +        doc::MISSING_PANICS_DOC,
 +        doc::MISSING_SAFETY_DOC,
 +        doc::NEEDLESS_DOCTEST_MAIN,
 +        double_comparison::DOUBLE_COMPARISONS,
 +        double_parens::DOUBLE_PARENS,
 +        drop_forget_ref::DROP_COPY,
 +        drop_forget_ref::DROP_REF,
 +        drop_forget_ref::FORGET_COPY,
 +        drop_forget_ref::FORGET_REF,
 +        duration_subsec::DURATION_SUBSEC,
 +        else_if_without_else::ELSE_IF_WITHOUT_ELSE,
 +        empty_enum::EMPTY_ENUM,
 +        entry::MAP_ENTRY,
 +        enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
 +        enum_variants::ENUM_VARIANT_NAMES,
 +        enum_variants::MODULE_INCEPTION,
 +        enum_variants::MODULE_NAME_REPETITIONS,
 +        eq_op::EQ_OP,
 +        eq_op::OP_REF,
 +        erasing_op::ERASING_OP,
 +        escape::BOXED_LOCAL,
 +        eta_reduction::REDUNDANT_CLOSURE,
 +        eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
 +        eval_order_dependence::DIVERGING_SUB_EXPRESSION,
 +        eval_order_dependence::EVAL_ORDER_DEPENDENCE,
 +        excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
 +        excessive_bools::STRUCT_EXCESSIVE_BOOLS,
 +        exhaustive_items::EXHAUSTIVE_ENUMS,
 +        exhaustive_items::EXHAUSTIVE_STRUCTS,
 +        exit::EXIT,
 +        explicit_write::EXPLICIT_WRITE,
 +        fallible_impl_from::FALLIBLE_IMPL_FROM,
++        feature_name::NEGATIVE_FEATURE_NAMES,
++        feature_name::REDUNDANT_FEATURE_NAMES,
 +        float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS,
 +        float_literal::EXCESSIVE_PRECISION,
 +        float_literal::LOSSY_FLOAT_LITERAL,
 +        floating_point_arithmetic::IMPRECISE_FLOPS,
 +        floating_point_arithmetic::SUBOPTIMAL_FLOPS,
 +        format::USELESS_FORMAT,
 +        formatting::POSSIBLE_MISSING_COMMA,
 +        formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
 +        formatting::SUSPICIOUS_ELSE_FORMATTING,
 +        formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
 +        from_over_into::FROM_OVER_INTO,
 +        from_str_radix_10::FROM_STR_RADIX_10,
 +        functions::DOUBLE_MUST_USE,
 +        functions::MUST_USE_CANDIDATE,
 +        functions::MUST_USE_UNIT,
 +        functions::NOT_UNSAFE_PTR_ARG_DEREF,
 +        functions::RESULT_UNIT_ERR,
 +        functions::TOO_MANY_ARGUMENTS,
 +        functions::TOO_MANY_LINES,
 +        future_not_send::FUTURE_NOT_SEND,
 +        get_last_with_len::GET_LAST_WITH_LEN,
 +        identity_op::IDENTITY_OP,
 +        if_let_mutex::IF_LET_MUTEX,
 +        if_let_some_result::IF_LET_SOME_RESULT,
 +        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,
 +        indexing_slicing::INDEXING_SLICING,
 +        indexing_slicing::OUT_OF_BOUNDS_INDEXING,
 +        infinite_iter::INFINITE_ITER,
 +        infinite_iter::MAYBE_INFINITE_ITER,
 +        inherent_impl::MULTIPLE_INHERENT_IMPL,
 +        inherent_to_string::INHERENT_TO_STRING,
 +        inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY,
 +        inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
 +        int_plus_one::INT_PLUS_ONE,
 +        integer_division::INTEGER_DIVISION,
 +        invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
 +        items_after_statements::ITEMS_AFTER_STATEMENTS,
 +        large_const_arrays::LARGE_CONST_ARRAYS,
 +        large_enum_variant::LARGE_ENUM_VARIANT,
 +        large_stack_arrays::LARGE_STACK_ARRAYS,
 +        len_zero::COMPARISON_TO_EMPTY,
 +        len_zero::LEN_WITHOUT_IS_EMPTY,
 +        len_zero::LEN_ZERO,
 +        let_if_seq::USELESS_LET_IF_SEQ,
 +        let_underscore::LET_UNDERSCORE_DROP,
 +        let_underscore::LET_UNDERSCORE_LOCK,
 +        let_underscore::LET_UNDERSCORE_MUST_USE,
 +        lifetimes::EXTRA_UNUSED_LIFETIMES,
 +        lifetimes::NEEDLESS_LIFETIMES,
 +        literal_representation::DECIMAL_LITERAL_REPRESENTATION,
 +        literal_representation::INCONSISTENT_DIGIT_GROUPING,
 +        literal_representation::LARGE_DIGIT_GROUPS,
 +        literal_representation::MISTYPED_LITERAL_SUFFIXES,
 +        literal_representation::UNREADABLE_LITERAL,
 +        literal_representation::UNUSUAL_BYTE_GROUPINGS,
 +        loops::EMPTY_LOOP,
 +        loops::EXPLICIT_COUNTER_LOOP,
 +        loops::EXPLICIT_INTO_ITER_LOOP,
 +        loops::EXPLICIT_ITER_LOOP,
 +        loops::FOR_KV_MAP,
 +        loops::FOR_LOOPS_OVER_FALLIBLES,
 +        loops::ITER_NEXT_LOOP,
 +        loops::MANUAL_FLATTEN,
 +        loops::MANUAL_MEMCPY,
 +        loops::MUT_RANGE_BOUND,
 +        loops::NEEDLESS_COLLECT,
 +        loops::NEEDLESS_RANGE_LOOP,
 +        loops::NEVER_LOOP,
 +        loops::SAME_ITEM_PUSH,
 +        loops::SINGLE_ELEMENT_LOOP,
 +        loops::WHILE_IMMUTABLE_CONDITION,
 +        loops::WHILE_LET_LOOP,
 +        loops::WHILE_LET_ON_ITERATOR,
 +        macro_use::MACRO_USE_IMPORTS,
 +        main_recursion::MAIN_RECURSION,
 +        manual_async_fn::MANUAL_ASYNC_FN,
 +        manual_map::MANUAL_MAP,
 +        manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
 +        manual_ok_or::MANUAL_OK_OR,
 +        manual_strip::MANUAL_STRIP,
 +        manual_unwrap_or::MANUAL_UNWRAP_OR,
 +        map_clone::MAP_CLONE,
 +        map_err_ignore::MAP_ERR_IGNORE,
 +        map_unit_fn::OPTION_MAP_UNIT_FN,
 +        map_unit_fn::RESULT_MAP_UNIT_FN,
 +        match_on_vec_items::MATCH_ON_VEC_ITEMS,
 +        matches::INFALLIBLE_DESTRUCTURING_MATCH,
 +        matches::MATCH_AS_REF,
 +        matches::MATCH_BOOL,
 +        matches::MATCH_LIKE_MATCHES_MACRO,
 +        matches::MATCH_OVERLAPPING_ARM,
 +        matches::MATCH_REF_PATS,
 +        matches::MATCH_SAME_ARMS,
 +        matches::MATCH_SINGLE_BINDING,
 +        matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +        matches::MATCH_WILD_ERR_ARM,
 +        matches::REDUNDANT_PATTERN_MATCHING,
 +        matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +        matches::SINGLE_MATCH,
 +        matches::SINGLE_MATCH_ELSE,
 +        matches::WILDCARD_ENUM_MATCH_ARM,
 +        matches::WILDCARD_IN_OR_PATTERNS,
 +        mem_discriminant::MEM_DISCRIMINANT_NON_ENUM,
 +        mem_forget::MEM_FORGET,
 +        mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
 +        mem_replace::MEM_REPLACE_WITH_DEFAULT,
 +        mem_replace::MEM_REPLACE_WITH_UNINIT,
 +        methods::BIND_INSTEAD_OF_MAP,
 +        methods::BYTES_NTH,
 +        methods::CHARS_LAST_CMP,
 +        methods::CHARS_NEXT_CMP,
 +        methods::CLONED_INSTEAD_OF_COPIED,
 +        methods::CLONE_DOUBLE_REF,
 +        methods::CLONE_ON_COPY,
 +        methods::CLONE_ON_REF_PTR,
 +        methods::EXPECT_FUN_CALL,
 +        methods::EXPECT_USED,
 +        methods::EXTEND_WITH_DRAIN,
 +        methods::FILETYPE_IS_FILE,
 +        methods::FILTER_MAP_IDENTITY,
 +        methods::FILTER_MAP_NEXT,
 +        methods::FILTER_NEXT,
 +        methods::FLAT_MAP_IDENTITY,
 +        methods::FLAT_MAP_OPTION,
 +        methods::FROM_ITER_INSTEAD_OF_COLLECT,
 +        methods::GET_UNWRAP,
 +        methods::IMPLICIT_CLONE,
 +        methods::INEFFICIENT_TO_STRING,
 +        methods::INSPECT_FOR_EACH,
 +        methods::INTO_ITER_ON_REF,
 +        methods::ITERATOR_STEP_BY_ZERO,
 +        methods::ITER_CLONED_COLLECT,
 +        methods::ITER_COUNT,
 +        methods::ITER_NEXT_SLICE,
 +        methods::ITER_NTH,
 +        methods::ITER_NTH_ZERO,
 +        methods::ITER_SKIP_NEXT,
 +        methods::MANUAL_FILTER_MAP,
 +        methods::MANUAL_FIND_MAP,
 +        methods::MANUAL_SATURATING_ARITHMETIC,
++        methods::MANUAL_SPLIT_ONCE,
 +        methods::MANUAL_STR_REPEAT,
 +        methods::MAP_COLLECT_RESULT_UNIT,
 +        methods::MAP_FLATTEN,
 +        methods::MAP_IDENTITY,
 +        methods::MAP_UNWRAP_OR,
 +        methods::NEW_RET_NO_SELF,
 +        methods::OK_EXPECT,
 +        methods::OPTION_AS_REF_DEREF,
 +        methods::OPTION_FILTER_MAP,
 +        methods::OPTION_MAP_OR_NONE,
 +        methods::OR_FUN_CALL,
 +        methods::RESULT_MAP_OR_INTO_OPTION,
 +        methods::SEARCH_IS_SOME,
 +        methods::SHOULD_IMPLEMENT_TRAIT,
 +        methods::SINGLE_CHAR_ADD_STR,
 +        methods::SINGLE_CHAR_PATTERN,
 +        methods::SKIP_WHILE_NEXT,
 +        methods::STRING_EXTEND_CHARS,
 +        methods::SUSPICIOUS_MAP,
 +        methods::SUSPICIOUS_SPLITN,
 +        methods::UNINIT_ASSUMED_INIT,
 +        methods::UNNECESSARY_FILTER_MAP,
 +        methods::UNNECESSARY_FOLD,
 +        methods::UNNECESSARY_LAZY_EVALUATIONS,
 +        methods::UNWRAP_OR_ELSE_DEFAULT,
 +        methods::UNWRAP_USED,
 +        methods::USELESS_ASREF,
 +        methods::WRONG_SELF_CONVENTION,
 +        methods::ZST_OFFSET,
 +        minmax::MIN_MAX,
 +        misc::CMP_NAN,
 +        misc::CMP_OWNED,
 +        misc::FLOAT_CMP,
 +        misc::FLOAT_CMP_CONST,
 +        misc::MODULO_ONE,
 +        misc::SHORT_CIRCUIT_STATEMENT,
 +        misc::TOPLEVEL_REF_ARG,
 +        misc::USED_UNDERSCORE_BINDING,
 +        misc::ZERO_PTR,
 +        misc_early::BUILTIN_TYPE_SHADOW,
 +        misc_early::DOUBLE_NEG,
 +        misc_early::DUPLICATE_UNDERSCORE_ARGUMENT,
 +        misc_early::MIXED_CASE_HEX_LITERALS,
 +        misc_early::REDUNDANT_PATTERN,
 +        misc_early::UNNEEDED_FIELD_PATTERN,
 +        misc_early::UNNEEDED_WILDCARD_PATTERN,
 +        misc_early::UNSEPARATED_LITERAL_SUFFIX,
 +        misc_early::ZERO_PREFIXED_LITERAL,
 +        missing_const_for_fn::MISSING_CONST_FOR_FN,
 +        missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
 +        missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
 +        missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
++        module_style::MOD_MODULE_FILES,
++        module_style::SELF_NAMED_MODULE_FILES,
 +        modulo_arithmetic::MODULO_ARITHMETIC,
 +        multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
 +        mut_key::MUTABLE_KEY_TYPE,
 +        mut_mut::MUT_MUT,
 +        mut_mutex_lock::MUT_MUTEX_LOCK,
 +        mut_reference::UNNECESSARY_MUT_PASSED,
 +        mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
 +        mutex_atomic::MUTEX_ATOMIC,
 +        mutex_atomic::MUTEX_INTEGER,
 +        needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
 +        needless_bitwise_bool::NEEDLESS_BITWISE_BOOL,
 +        needless_bool::BOOL_COMPARISON,
 +        needless_bool::NEEDLESS_BOOL,
 +        needless_borrow::NEEDLESS_BORROW,
 +        needless_borrow::REF_BINDING_TO_REFERENCE,
 +        needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
 +        needless_continue::NEEDLESS_CONTINUE,
 +        needless_for_each::NEEDLESS_FOR_EACH,
++        needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF,
 +        needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
 +        needless_question_mark::NEEDLESS_QUESTION_MARK,
 +        needless_update::NEEDLESS_UPDATE,
 +        neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD,
 +        neg_multiply::NEG_MULTIPLY,
 +        new_without_default::NEW_WITHOUT_DEFAULT,
 +        no_effect::NO_EFFECT,
 +        no_effect::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,
 +        nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
 +        open_options::NONSENSICAL_OPEN_OPTIONS,
 +        option_env_unwrap::OPTION_ENV_UNWRAP,
 +        option_if_let_else::OPTION_IF_LET_ELSE,
 +        overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
 +        panic_in_result_fn::PANIC_IN_RESULT_FN,
 +        panic_unimplemented::PANIC,
 +        panic_unimplemented::TODO,
 +        panic_unimplemented::UNIMPLEMENTED,
 +        panic_unimplemented::UNREACHABLE,
 +        partialeq_ne_impl::PARTIALEQ_NE_IMPL,
 +        pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
 +        pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
 +        path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
 +        pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
 +        precedence::PRECEDENCE,
 +        ptr::CMP_NULL,
 +        ptr::INVALID_NULL_PTR_USAGE,
 +        ptr::MUT_FROM_REF,
 +        ptr::PTR_ARG,
 +        ptr_eq::PTR_EQ,
 +        ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
 +        question_mark::QUESTION_MARK,
 +        ranges::MANUAL_RANGE_CONTAINS,
 +        ranges::RANGE_MINUS_ONE,
 +        ranges::RANGE_PLUS_ONE,
 +        ranges::RANGE_ZIP_WITH_LEN,
 +        ranges::REVERSED_EMPTY_RANGES,
 +        redundant_clone::REDUNDANT_CLONE,
 +        redundant_closure_call::REDUNDANT_CLOSURE_CALL,
 +        redundant_else::REDUNDANT_ELSE,
 +        redundant_field_names::REDUNDANT_FIELD_NAMES,
 +        redundant_pub_crate::REDUNDANT_PUB_CRATE,
 +        redundant_slicing::REDUNDANT_SLICING,
 +        redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
 +        ref_option_ref::REF_OPTION_REF,
 +        reference::DEREF_ADDROF,
 +        reference::REF_IN_DEREF,
 +        regex::INVALID_REGEX,
 +        regex::TRIVIAL_REGEX,
 +        repeat_once::REPEAT_ONCE,
 +        returns::LET_AND_RETURN,
 +        returns::NEEDLESS_RETURN,
 +        self_assignment::SELF_ASSIGNMENT,
 +        self_named_constructors::SELF_NAMED_CONSTRUCTORS,
 +        semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
 +        serde_api::SERDE_API_MISUSE,
 +        shadow::SHADOW_REUSE,
 +        shadow::SHADOW_SAME,
 +        shadow::SHADOW_UNRELATED,
 +        single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
 +        size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
 +        slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
 +        stable_sort_primitive::STABLE_SORT_PRIMITIVE,
 +        strings::STRING_ADD,
 +        strings::STRING_ADD_ASSIGN,
 +        strings::STRING_FROM_UTF8_AS_BYTES,
 +        strings::STRING_LIT_AS_BYTES,
 +        strings::STRING_TO_STRING,
 +        strings::STR_TO_STRING,
 +        strlen_on_c_strings::STRLEN_ON_C_STRINGS,
 +        suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS,
 +        suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
 +        suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
 +        swap::ALMOST_SWAPPED,
 +        swap::MANUAL_SWAP,
 +        tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
 +        temporary_assignment::TEMPORARY_ASSIGNMENT,
 +        to_digit_is_some::TO_DIGIT_IS_SOME,
 +        to_string_in_display::TO_STRING_IN_DISPLAY,
 +        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_PTR_TO_PTR,
 +        transmute::TRANSMUTE_PTR_TO_REF,
 +        transmute::UNSOUND_COLLECTION_TRANSMUTE,
 +        transmute::USELESS_TRANSMUTE,
 +        transmute::WRONG_TRANSMUTE,
 +        transmuting_null::TRANSMUTING_NULL,
 +        try_err::TRY_ERR,
 +        types::BORROWED_BOX,
 +        types::BOX_VEC,
 +        types::LINKEDLIST,
 +        types::OPTION_OPTION,
 +        types::RC_BUFFER,
 +        types::RC_MUTEX,
 +        types::REDUNDANT_ALLOCATION,
 +        types::TYPE_COMPLEXITY,
 +        types::VEC_BOX,
 +        undropped_manually_drops::UNDROPPED_MANUALLY_DROPS,
 +        unicode::INVISIBLE_CHARACTERS,
 +        unicode::NON_ASCII_LITERAL,
 +        unicode::UNICODE_NOT_NFC,
 +        unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
 +        unit_types::LET_UNIT_VALUE,
 +        unit_types::UNIT_ARG,
 +        unit_types::UNIT_CMP,
 +        unnamed_address::FN_ADDRESS_COMPARISONS,
 +        unnamed_address::VTABLE_ADDRESS_COMPARISONS,
 +        unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
 +        unnecessary_sort_by::UNNECESSARY_SORT_BY,
 +        unnecessary_wraps::UNNECESSARY_WRAPS,
 +        unnested_or_patterns::UNNESTED_OR_PATTERNS,
 +        unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
 +        unused_async::UNUSED_ASYNC,
 +        unused_io_amount::UNUSED_IO_AMOUNT,
 +        unused_self::UNUSED_SELF,
 +        unused_unit::UNUSED_UNIT,
 +        unwrap::PANICKING_UNWRAP,
 +        unwrap::UNNECESSARY_UNWRAP,
 +        unwrap_in_result::UNWRAP_IN_RESULT,
 +        upper_case_acronyms::UPPER_CASE_ACRONYMS,
 +        use_self::USE_SELF,
 +        useless_conversion::USELESS_CONVERSION,
 +        vec::USELESS_VEC,
 +        vec_init_then_push::VEC_INIT_THEN_PUSH,
 +        vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
 +        verbose_file_reads::VERBOSE_FILE_READS,
 +        wildcard_dependencies::WILDCARD_DEPENDENCIES,
 +        wildcard_imports::ENUM_GLOB_USE,
 +        wildcard_imports::WILDCARD_IMPORTS,
 +        write::PRINTLN_EMPTY_STRING,
 +        write::PRINT_LITERAL,
 +        write::PRINT_STDERR,
 +        write::PRINT_STDOUT,
 +        write::PRINT_WITH_NEWLINE,
 +        write::USE_DEBUG,
 +        write::WRITELN_EMPTY_STRING,
 +        write::WRITE_LITERAL,
 +        write::WRITE_WITH_NEWLINE,
 +        zero_div_zero::ZERO_DIVIDED_BY_ZERO,
 +        zero_sized_map_values::ZERO_SIZED_MAP_VALUES,
 +    ]);
 +    // end register lints, do not remove this comment, it’s used in `update_lints`
 +
 +    store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
 +        LintId::of(arithmetic::FLOAT_ARITHMETIC),
 +        LintId::of(arithmetic::INTEGER_ARITHMETIC),
 +        LintId::of(as_conversions::AS_CONVERSIONS),
 +        LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
 +        LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
 +        LintId::of(create_dir::CREATE_DIR),
 +        LintId::of(dbg_macro::DBG_MACRO),
 +        LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),
 +        LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
 +        LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
 +        LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
 +        LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
 +        LintId::of(exit::EXIT),
 +        LintId::of(float_literal::LOSSY_FLOAT_LITERAL),
 +        LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE),
 +        LintId::of(implicit_return::IMPLICIT_RETURN),
 +        LintId::of(indexing_slicing::INDEXING_SLICING),
 +        LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL),
 +        LintId::of(integer_division::INTEGER_DIVISION),
 +        LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
 +        LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
 +        LintId::of(map_err_ignore::MAP_ERR_IGNORE),
 +        LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
 +        LintId::of(matches::WILDCARD_ENUM_MATCH_ARM),
 +        LintId::of(mem_forget::MEM_FORGET),
 +        LintId::of(methods::CLONE_ON_REF_PTR),
 +        LintId::of(methods::EXPECT_USED),
 +        LintId::of(methods::FILETYPE_IS_FILE),
 +        LintId::of(methods::GET_UNWRAP),
 +        LintId::of(methods::UNWRAP_USED),
 +        LintId::of(misc::FLOAT_CMP_CONST),
 +        LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
 +        LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
 +        LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES),
 +        LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
++        LintId::of(module_style::MOD_MODULE_FILES),
++        LintId::of(module_style::SELF_NAMED_MODULE_FILES),
 +        LintId::of(modulo_arithmetic::MODULO_ARITHMETIC),
 +        LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
 +        LintId::of(panic_unimplemented::PANIC),
 +        LintId::of(panic_unimplemented::TODO),
 +        LintId::of(panic_unimplemented::UNIMPLEMENTED),
 +        LintId::of(panic_unimplemented::UNREACHABLE),
 +        LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
 +        LintId::of(shadow::SHADOW_REUSE),
 +        LintId::of(shadow::SHADOW_SAME),
 +        LintId::of(strings::STRING_ADD),
 +        LintId::of(strings::STRING_TO_STRING),
 +        LintId::of(strings::STR_TO_STRING),
 +        LintId::of(types::RC_BUFFER),
 +        LintId::of(types::RC_MUTEX),
 +        LintId::of(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),
 +    ]);
 +
 +    store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
 +        LintId::of(attrs::INLINE_ALWAYS),
 +        LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
 +        LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
 +        LintId::of(bit_mask::VERBOSE_BIT_MASK),
 +        LintId::of(bytecount::NAIVE_BYTECOUNT),
 +        LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
 +        LintId::of(casts::CAST_LOSSLESS),
 +        LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
 +        LintId::of(casts::CAST_POSSIBLE_WRAP),
 +        LintId::of(casts::CAST_PRECISION_LOSS),
 +        LintId::of(casts::CAST_PTR_ALIGNMENT),
 +        LintId::of(casts::CAST_SIGN_LOSS),
 +        LintId::of(casts::PTR_AS_PTR),
 +        LintId::of(checked_conversions::CHECKED_CONVERSIONS),
 +        LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION),
 +        LintId::of(copy_iterator::COPY_ITERATOR),
 +        LintId::of(default::DEFAULT_TRAIT_ACCESS),
 +        LintId::of(dereference::EXPLICIT_DEREF_METHODS),
 +        LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY),
 +        LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE),
 +        LintId::of(doc::DOC_MARKDOWN),
 +        LintId::of(doc::MISSING_ERRORS_DOC),
 +        LintId::of(doc::MISSING_PANICS_DOC),
 +        LintId::of(empty_enum::EMPTY_ENUM),
 +        LintId::of(enum_variants::MODULE_NAME_REPETITIONS),
 +        LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),
 +        LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS),
 +        LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS),
 +        LintId::of(functions::MUST_USE_CANDIDATE),
 +        LintId::of(functions::TOO_MANY_LINES),
 +        LintId::of(if_not_else::IF_NOT_ELSE),
 +        LintId::of(implicit_hasher::IMPLICIT_HASHER),
 +        LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
 +        LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR),
 +        LintId::of(infinite_iter::MAYBE_INFINITE_ITER),
 +        LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS),
 +        LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS),
 +        LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS),
 +        LintId::of(let_underscore::LET_UNDERSCORE_DROP),
 +        LintId::of(literal_representation::LARGE_DIGIT_GROUPS),
 +        LintId::of(literal_representation::UNREADABLE_LITERAL),
 +        LintId::of(loops::EXPLICIT_INTO_ITER_LOOP),
 +        LintId::of(loops::EXPLICIT_ITER_LOOP),
 +        LintId::of(macro_use::MACRO_USE_IMPORTS),
 +        LintId::of(manual_ok_or::MANUAL_OK_OR),
 +        LintId::of(match_on_vec_items::MATCH_ON_VEC_ITEMS),
 +        LintId::of(matches::MATCH_BOOL),
 +        LintId::of(matches::MATCH_SAME_ARMS),
 +        LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
 +        LintId::of(matches::MATCH_WILD_ERR_ARM),
 +        LintId::of(matches::SINGLE_MATCH_ELSE),
 +        LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
 +        LintId::of(methods::FILTER_MAP_NEXT),
 +        LintId::of(methods::FLAT_MAP_OPTION),
 +        LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
 +        LintId::of(methods::IMPLICIT_CLONE),
 +        LintId::of(methods::INEFFICIENT_TO_STRING),
 +        LintId::of(methods::MAP_FLATTEN),
 +        LintId::of(methods::MAP_UNWRAP_OR),
 +        LintId::of(misc::USED_UNDERSCORE_BINDING),
 +        LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
 +        LintId::of(mut_mut::MUT_MUT),
 +        LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL),
 +        LintId::of(needless_borrow::REF_BINDING_TO_REFERENCE),
 +        LintId::of(needless_continue::NEEDLESS_CONTINUE),
 +        LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
 +        LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
 +        LintId::of(non_expressive_names::SIMILAR_NAMES),
-         LintId::of(copies::BRANCHES_SHARING_CODE),
 +        LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
 +        LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
 +        LintId::of(ranges::RANGE_MINUS_ONE),
 +        LintId::of(ranges::RANGE_PLUS_ONE),
 +        LintId::of(redundant_else::REDUNDANT_ELSE),
 +        LintId::of(ref_option_ref::REF_OPTION_REF),
 +        LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
 +        LintId::of(shadow::SHADOW_UNRELATED),
 +        LintId::of(strings::STRING_ADD_ASSIGN),
 +        LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
 +        LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
 +        LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
 +        LintId::of(types::LINKEDLIST),
 +        LintId::of(types::OPTION_OPTION),
 +        LintId::of(unicode::NON_ASCII_LITERAL),
 +        LintId::of(unicode::UNICODE_NOT_NFC),
 +        LintId::of(unit_types::LET_UNIT_VALUE),
 +        LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS),
 +        LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS),
 +        LintId::of(unused_async::UNUSED_ASYNC),
 +        LintId::of(unused_self::UNUSED_SELF),
 +        LintId::of(wildcard_imports::ENUM_GLOB_USE),
 +        LintId::of(wildcard_imports::WILDCARD_IMPORTS),
 +        LintId::of(zero_sized_map_values::ZERO_SIZED_MAP_VALUES),
 +    ]);
 +
 +    #[cfg(feature = "internal-lints")]
 +    store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![
 +        LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL),
 +        LintId::of(utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
 +        LintId::of(utils::internal_lints::COMPILER_LINT_FUNCTIONS),
 +        LintId::of(utils::internal_lints::DEFAULT_LINT),
 +        LintId::of(utils::internal_lints::IF_CHAIN_STYLE),
 +        LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL),
 +        LintId::of(utils::internal_lints::INVALID_PATHS),
 +        LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS),
 +        LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
 +        LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA),
 +        LintId::of(utils::internal_lints::PRODUCE_ICE),
 +        LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR),
 +    ]);
 +
 +    store.register_group(true, "clippy::all", Some("clippy"), vec![
 +        LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
 +        LintId::of(approx_const::APPROX_CONSTANT),
 +        LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
 +        LintId::of(assign_ops::ASSIGN_OP_PATTERN),
 +        LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
 +        LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
 +        LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
 +        LintId::of(attrs::DEPRECATED_CFG_ATTR),
 +        LintId::of(attrs::DEPRECATED_SEMVER),
 +        LintId::of(attrs::MISMATCHED_TARGET_OS),
 +        LintId::of(attrs::USELESS_ATTRIBUTE),
 +        LintId::of(bit_mask::BAD_BIT_MASK),
 +        LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
 +        LintId::of(blacklisted_name::BLACKLISTED_NAME),
 +        LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
 +        LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
 +        LintId::of(booleans::LOGIC_BUG),
 +        LintId::of(booleans::NONMINIMAL_BOOL),
 +        LintId::of(casts::CAST_REF_TO_MUT),
 +        LintId::of(casts::CHAR_LIT_AS_U8),
 +        LintId::of(casts::FN_TO_NUMERIC_CAST),
 +        LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
 +        LintId::of(casts::UNNECESSARY_CAST),
 +        LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
 +        LintId::of(collapsible_if::COLLAPSIBLE_IF),
 +        LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
 +        LintId::of(comparison_chain::COMPARISON_CHAIN),
-         LintId::of(copies::BRANCHES_SHARING_CODE),
 +        LintId::of(copies::IFS_SAME_COND),
 +        LintId::of(copies::IF_SAME_THEN_ELSE),
 +        LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
++        LintId::of(derivable_impls::DERIVABLE_IMPLS),
 +        LintId::of(derive::DERIVE_HASH_XOR_EQ),
 +        LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
 +        LintId::of(doc::MISSING_SAFETY_DOC),
 +        LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
 +        LintId::of(double_comparison::DOUBLE_COMPARISONS),
 +        LintId::of(double_parens::DOUBLE_PARENS),
 +        LintId::of(drop_forget_ref::DROP_COPY),
 +        LintId::of(drop_forget_ref::DROP_REF),
 +        LintId::of(drop_forget_ref::FORGET_COPY),
 +        LintId::of(drop_forget_ref::FORGET_REF),
 +        LintId::of(duration_subsec::DURATION_SUBSEC),
 +        LintId::of(entry::MAP_ENTRY),
 +        LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
 +        LintId::of(enum_variants::ENUM_VARIANT_NAMES),
 +        LintId::of(enum_variants::MODULE_INCEPTION),
 +        LintId::of(eq_op::EQ_OP),
 +        LintId::of(eq_op::OP_REF),
 +        LintId::of(erasing_op::ERASING_OP),
 +        LintId::of(escape::BOXED_LOCAL),
 +        LintId::of(eta_reduction::REDUNDANT_CLOSURE),
 +        LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION),
 +        LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
 +        LintId::of(explicit_write::EXPLICIT_WRITE),
 +        LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
 +        LintId::of(float_literal::EXCESSIVE_PRECISION),
 +        LintId::of(format::USELESS_FORMAT),
 +        LintId::of(formatting::POSSIBLE_MISSING_COMMA),
 +        LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
 +        LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
 +        LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
 +        LintId::of(from_over_into::FROM_OVER_INTO),
 +        LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
 +        LintId::of(functions::DOUBLE_MUST_USE),
 +        LintId::of(functions::MUST_USE_UNIT),
 +        LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
 +        LintId::of(functions::RESULT_UNIT_ERR),
 +        LintId::of(functions::TOO_MANY_ARGUMENTS),
 +        LintId::of(get_last_with_len::GET_LAST_WITH_LEN),
 +        LintId::of(identity_op::IDENTITY_OP),
 +        LintId::of(if_let_mutex::IF_LET_MUTEX),
 +        LintId::of(if_let_some_result::IF_LET_SOME_RESULT),
 +        LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
 +        LintId::of(infinite_iter::INFINITE_ITER),
 +        LintId::of(inherent_to_string::INHERENT_TO_STRING),
 +        LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
 +        LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
 +        LintId::of(int_plus_one::INT_PLUS_ONE),
 +        LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
 +        LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
 +        LintId::of(len_zero::COMPARISON_TO_EMPTY),
 +        LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
 +        LintId::of(len_zero::LEN_ZERO),
 +        LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
 +        LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
 +        LintId::of(lifetimes::NEEDLESS_LIFETIMES),
 +        LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
 +        LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
 +        LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
 +        LintId::of(loops::EMPTY_LOOP),
 +        LintId::of(loops::EXPLICIT_COUNTER_LOOP),
 +        LintId::of(loops::FOR_KV_MAP),
 +        LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
 +        LintId::of(loops::ITER_NEXT_LOOP),
 +        LintId::of(loops::MANUAL_FLATTEN),
 +        LintId::of(loops::MANUAL_MEMCPY),
 +        LintId::of(loops::MUT_RANGE_BOUND),
 +        LintId::of(loops::NEEDLESS_COLLECT),
 +        LintId::of(loops::NEEDLESS_RANGE_LOOP),
 +        LintId::of(loops::NEVER_LOOP),
 +        LintId::of(loops::SAME_ITEM_PUSH),
 +        LintId::of(loops::SINGLE_ELEMENT_LOOP),
 +        LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
 +        LintId::of(loops::WHILE_LET_LOOP),
 +        LintId::of(loops::WHILE_LET_ON_ITERATOR),
 +        LintId::of(main_recursion::MAIN_RECURSION),
 +        LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
 +        LintId::of(manual_map::MANUAL_MAP),
 +        LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
 +        LintId::of(manual_strip::MANUAL_STRIP),
 +        LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
 +        LintId::of(map_clone::MAP_CLONE),
 +        LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
 +        LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
 +        LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
 +        LintId::of(matches::MATCH_AS_REF),
 +        LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
 +        LintId::of(matches::MATCH_OVERLAPPING_ARM),
 +        LintId::of(matches::MATCH_REF_PATS),
 +        LintId::of(matches::MATCH_SINGLE_BINDING),
 +        LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
 +        LintId::of(matches::SINGLE_MATCH),
 +        LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
 +        LintId::of(mem_discriminant::MEM_DISCRIMINANT_NON_ENUM),
 +        LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
 +        LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
 +        LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
 +        LintId::of(methods::BIND_INSTEAD_OF_MAP),
 +        LintId::of(methods::BYTES_NTH),
 +        LintId::of(methods::CHARS_LAST_CMP),
 +        LintId::of(methods::CHARS_NEXT_CMP),
 +        LintId::of(methods::CLONE_DOUBLE_REF),
 +        LintId::of(methods::CLONE_ON_COPY),
 +        LintId::of(methods::EXPECT_FUN_CALL),
 +        LintId::of(methods::EXTEND_WITH_DRAIN),
 +        LintId::of(methods::FILTER_MAP_IDENTITY),
 +        LintId::of(methods::FILTER_NEXT),
 +        LintId::of(methods::FLAT_MAP_IDENTITY),
 +        LintId::of(methods::INSPECT_FOR_EACH),
 +        LintId::of(methods::INTO_ITER_ON_REF),
 +        LintId::of(methods::ITERATOR_STEP_BY_ZERO),
 +        LintId::of(methods::ITER_CLONED_COLLECT),
 +        LintId::of(methods::ITER_COUNT),
 +        LintId::of(methods::ITER_NEXT_SLICE),
 +        LintId::of(methods::ITER_NTH),
 +        LintId::of(methods::ITER_NTH_ZERO),
 +        LintId::of(methods::ITER_SKIP_NEXT),
 +        LintId::of(methods::MANUAL_FILTER_MAP),
 +        LintId::of(methods::MANUAL_FIND_MAP),
 +        LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
++        LintId::of(methods::MANUAL_SPLIT_ONCE),
 +        LintId::of(methods::MANUAL_STR_REPEAT),
 +        LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
 +        LintId::of(methods::MAP_IDENTITY),
 +        LintId::of(methods::NEW_RET_NO_SELF),
 +        LintId::of(methods::OK_EXPECT),
 +        LintId::of(methods::OPTION_AS_REF_DEREF),
 +        LintId::of(methods::OPTION_FILTER_MAP),
 +        LintId::of(methods::OPTION_MAP_OR_NONE),
 +        LintId::of(methods::OR_FUN_CALL),
 +        LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
 +        LintId::of(methods::SEARCH_IS_SOME),
 +        LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
 +        LintId::of(methods::SINGLE_CHAR_ADD_STR),
 +        LintId::of(methods::SINGLE_CHAR_PATTERN),
 +        LintId::of(methods::SKIP_WHILE_NEXT),
 +        LintId::of(methods::STRING_EXTEND_CHARS),
 +        LintId::of(methods::SUSPICIOUS_MAP),
 +        LintId::of(methods::SUSPICIOUS_SPLITN),
 +        LintId::of(methods::UNINIT_ASSUMED_INIT),
 +        LintId::of(methods::UNNECESSARY_FILTER_MAP),
 +        LintId::of(methods::UNNECESSARY_FOLD),
 +        LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
 +        LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
 +        LintId::of(methods::USELESS_ASREF),
 +        LintId::of(methods::WRONG_SELF_CONVENTION),
 +        LintId::of(methods::ZST_OFFSET),
 +        LintId::of(minmax::MIN_MAX),
 +        LintId::of(misc::CMP_NAN),
 +        LintId::of(misc::CMP_OWNED),
 +        LintId::of(misc::FLOAT_CMP),
 +        LintId::of(misc::MODULO_ONE),
 +        LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
 +        LintId::of(misc::TOPLEVEL_REF_ARG),
 +        LintId::of(misc::ZERO_PTR),
 +        LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
 +        LintId::of(misc_early::DOUBLE_NEG),
 +        LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
 +        LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
 +        LintId::of(misc_early::REDUNDANT_PATTERN),
 +        LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
 +        LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
 +        LintId::of(mut_key::MUTABLE_KEY_TYPE),
 +        LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
 +        LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
 +        LintId::of(mutex_atomic::MUTEX_ATOMIC),
 +        LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
 +        LintId::of(needless_bool::BOOL_COMPARISON),
 +        LintId::of(needless_bool::NEEDLESS_BOOL),
 +        LintId::of(needless_borrow::NEEDLESS_BORROW),
 +        LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
++        LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
 +        LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
 +        LintId::of(needless_update::NEEDLESS_UPDATE),
 +        LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
 +        LintId::of(neg_multiply::NEG_MULTIPLY),
 +        LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
 +        LintId::of(no_effect::NO_EFFECT),
 +        LintId::of(no_effect::UNNECESSARY_OPERATION),
 +        LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
 +        LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
 +        LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
 +        LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
 +        LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
 +        LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
 +        LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
 +        LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
 +        LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
 +        LintId::of(precedence::PRECEDENCE),
 +        LintId::of(ptr::CMP_NULL),
 +        LintId::of(ptr::INVALID_NULL_PTR_USAGE),
 +        LintId::of(ptr::MUT_FROM_REF),
 +        LintId::of(ptr::PTR_ARG),
 +        LintId::of(ptr_eq::PTR_EQ),
 +        LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
 +        LintId::of(question_mark::QUESTION_MARK),
 +        LintId::of(ranges::MANUAL_RANGE_CONTAINS),
 +        LintId::of(ranges::RANGE_ZIP_WITH_LEN),
 +        LintId::of(ranges::REVERSED_EMPTY_RANGES),
 +        LintId::of(redundant_clone::REDUNDANT_CLONE),
 +        LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
 +        LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
 +        LintId::of(redundant_slicing::REDUNDANT_SLICING),
 +        LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
 +        LintId::of(reference::DEREF_ADDROF),
 +        LintId::of(reference::REF_IN_DEREF),
 +        LintId::of(regex::INVALID_REGEX),
 +        LintId::of(repeat_once::REPEAT_ONCE),
 +        LintId::of(returns::LET_AND_RETURN),
 +        LintId::of(returns::NEEDLESS_RETURN),
 +        LintId::of(self_assignment::SELF_ASSIGNMENT),
 +        LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
 +        LintId::of(serde_api::SERDE_API_MISUSE),
 +        LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
 +        LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
 +        LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
 +        LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
 +        LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
 +        LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
 +        LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
 +        LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
 +        LintId::of(swap::ALMOST_SWAPPED),
 +        LintId::of(swap::MANUAL_SWAP),
 +        LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
 +        LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
 +        LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
 +        LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY),
 +        LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
 +        LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
 +        LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
 +        LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
 +        LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
 +        LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
 +        LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
 +        LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
 +        LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
 +        LintId::of(transmute::WRONG_TRANSMUTE),
 +        LintId::of(transmuting_null::TRANSMUTING_NULL),
 +        LintId::of(try_err::TRY_ERR),
 +        LintId::of(types::BORROWED_BOX),
 +        LintId::of(types::BOX_VEC),
 +        LintId::of(types::REDUNDANT_ALLOCATION),
 +        LintId::of(types::TYPE_COMPLEXITY),
 +        LintId::of(types::VEC_BOX),
 +        LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
 +        LintId::of(unicode::INVISIBLE_CHARACTERS),
 +        LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
 +        LintId::of(unit_types::UNIT_ARG),
 +        LintId::of(unit_types::UNIT_CMP),
 +        LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
 +        LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
 +        LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
 +        LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
 +        LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
 +        LintId::of(unused_unit::UNUSED_UNIT),
 +        LintId::of(unwrap::PANICKING_UNWRAP),
 +        LintId::of(unwrap::UNNECESSARY_UNWRAP),
 +        LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
 +        LintId::of(useless_conversion::USELESS_CONVERSION),
 +        LintId::of(vec::USELESS_VEC),
 +        LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
 +        LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
 +        LintId::of(write::PRINTLN_EMPTY_STRING),
 +        LintId::of(write::PRINT_LITERAL),
 +        LintId::of(write::PRINT_WITH_NEWLINE),
 +        LintId::of(write::WRITELN_EMPTY_STRING),
 +        LintId::of(write::WRITE_LITERAL),
 +        LintId::of(write::WRITE_WITH_NEWLINE),
 +        LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
 +    ]);
 +
 +    store.register_group(true, "clippy::style", Some("clippy_style"), vec![
 +        LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
 +        LintId::of(assign_ops::ASSIGN_OP_PATTERN),
 +        LintId::of(blacklisted_name::BLACKLISTED_NAME),
 +        LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
 +        LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
 +        LintId::of(casts::FN_TO_NUMERIC_CAST),
 +        LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
 +        LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
 +        LintId::of(collapsible_if::COLLAPSIBLE_IF),
 +        LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
 +        LintId::of(comparison_chain::COMPARISON_CHAIN),
 +        LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
 +        LintId::of(doc::MISSING_SAFETY_DOC),
 +        LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
 +        LintId::of(enum_variants::ENUM_VARIANT_NAMES),
 +        LintId::of(enum_variants::MODULE_INCEPTION),
 +        LintId::of(eq_op::OP_REF),
 +        LintId::of(eta_reduction::REDUNDANT_CLOSURE),
 +        LintId::of(float_literal::EXCESSIVE_PRECISION),
 +        LintId::of(from_over_into::FROM_OVER_INTO),
 +        LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
 +        LintId::of(functions::DOUBLE_MUST_USE),
 +        LintId::of(functions::MUST_USE_UNIT),
 +        LintId::of(functions::RESULT_UNIT_ERR),
 +        LintId::of(if_let_some_result::IF_LET_SOME_RESULT),
 +        LintId::of(inherent_to_string::INHERENT_TO_STRING),
 +        LintId::of(len_zero::COMPARISON_TO_EMPTY),
 +        LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
 +        LintId::of(len_zero::LEN_ZERO),
 +        LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
 +        LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
 +        LintId::of(loops::FOR_KV_MAP),
 +        LintId::of(loops::NEEDLESS_RANGE_LOOP),
 +        LintId::of(loops::SAME_ITEM_PUSH),
 +        LintId::of(loops::WHILE_LET_ON_ITERATOR),
 +        LintId::of(main_recursion::MAIN_RECURSION),
 +        LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
 +        LintId::of(manual_map::MANUAL_MAP),
 +        LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
 +        LintId::of(map_clone::MAP_CLONE),
 +        LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
 +        LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
 +        LintId::of(matches::MATCH_OVERLAPPING_ARM),
 +        LintId::of(matches::MATCH_REF_PATS),
 +        LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
 +        LintId::of(matches::SINGLE_MATCH),
 +        LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
 +        LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
 +        LintId::of(methods::BYTES_NTH),
 +        LintId::of(methods::CHARS_LAST_CMP),
 +        LintId::of(methods::CHARS_NEXT_CMP),
 +        LintId::of(methods::INTO_ITER_ON_REF),
 +        LintId::of(methods::ITER_CLONED_COLLECT),
 +        LintId::of(methods::ITER_NEXT_SLICE),
 +        LintId::of(methods::ITER_NTH_ZERO),
 +        LintId::of(methods::ITER_SKIP_NEXT),
 +        LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
 +        LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
 +        LintId::of(methods::NEW_RET_NO_SELF),
 +        LintId::of(methods::OK_EXPECT),
 +        LintId::of(methods::OPTION_MAP_OR_NONE),
 +        LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
 +        LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
 +        LintId::of(methods::SINGLE_CHAR_ADD_STR),
 +        LintId::of(methods::STRING_EXTEND_CHARS),
 +        LintId::of(methods::UNNECESSARY_FOLD),
 +        LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
 +        LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
 +        LintId::of(methods::WRONG_SELF_CONVENTION),
 +        LintId::of(misc::TOPLEVEL_REF_ARG),
 +        LintId::of(misc::ZERO_PTR),
 +        LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
 +        LintId::of(misc_early::DOUBLE_NEG),
 +        LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
 +        LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
 +        LintId::of(misc_early::REDUNDANT_PATTERN),
 +        LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
 +        LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
 +        LintId::of(needless_borrow::NEEDLESS_BORROW),
 +        LintId::of(neg_multiply::NEG_MULTIPLY),
 +        LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
 +        LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
 +        LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
 +        LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
 +        LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
 +        LintId::of(ptr::CMP_NULL),
 +        LintId::of(ptr::PTR_ARG),
 +        LintId::of(ptr_eq::PTR_EQ),
 +        LintId::of(question_mark::QUESTION_MARK),
 +        LintId::of(ranges::MANUAL_RANGE_CONTAINS),
 +        LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
 +        LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
 +        LintId::of(returns::LET_AND_RETURN),
 +        LintId::of(returns::NEEDLESS_RETURN),
 +        LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
 +        LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
 +        LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
 +        LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
 +        LintId::of(try_err::TRY_ERR),
 +        LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
 +        LintId::of(unused_unit::UNUSED_UNIT),
 +        LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
 +        LintId::of(write::PRINTLN_EMPTY_STRING),
 +        LintId::of(write::PRINT_LITERAL),
 +        LintId::of(write::PRINT_WITH_NEWLINE),
 +        LintId::of(write::WRITELN_EMPTY_STRING),
 +        LintId::of(write::WRITE_LITERAL),
 +        LintId::of(write::WRITE_WITH_NEWLINE),
 +    ]);
 +
 +    store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![
 +        LintId::of(attrs::DEPRECATED_CFG_ATTR),
 +        LintId::of(booleans::NONMINIMAL_BOOL),
 +        LintId::of(casts::CHAR_LIT_AS_U8),
 +        LintId::of(casts::UNNECESSARY_CAST),
-     store.register_late_pass(move || Box::new(types::Types::new(vec_box_size_threshold, type_complexity_threshold)));
++        LintId::of(derivable_impls::DERIVABLE_IMPLS),
 +        LintId::of(double_comparison::DOUBLE_COMPARISONS),
 +        LintId::of(double_parens::DOUBLE_PARENS),
 +        LintId::of(duration_subsec::DURATION_SUBSEC),
 +        LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION),
 +        LintId::of(explicit_write::EXPLICIT_WRITE),
 +        LintId::of(format::USELESS_FORMAT),
 +        LintId::of(functions::TOO_MANY_ARGUMENTS),
 +        LintId::of(get_last_with_len::GET_LAST_WITH_LEN),
 +        LintId::of(identity_op::IDENTITY_OP),
 +        LintId::of(int_plus_one::INT_PLUS_ONE),
 +        LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
 +        LintId::of(lifetimes::NEEDLESS_LIFETIMES),
 +        LintId::of(loops::EXPLICIT_COUNTER_LOOP),
 +        LintId::of(loops::MANUAL_FLATTEN),
 +        LintId::of(loops::SINGLE_ELEMENT_LOOP),
 +        LintId::of(loops::WHILE_LET_LOOP),
 +        LintId::of(manual_strip::MANUAL_STRIP),
 +        LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
 +        LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
 +        LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
 +        LintId::of(matches::MATCH_AS_REF),
 +        LintId::of(matches::MATCH_SINGLE_BINDING),
 +        LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
 +        LintId::of(methods::BIND_INSTEAD_OF_MAP),
 +        LintId::of(methods::CLONE_ON_COPY),
 +        LintId::of(methods::FILTER_MAP_IDENTITY),
 +        LintId::of(methods::FILTER_NEXT),
 +        LintId::of(methods::FLAT_MAP_IDENTITY),
 +        LintId::of(methods::INSPECT_FOR_EACH),
 +        LintId::of(methods::ITER_COUNT),
 +        LintId::of(methods::MANUAL_FILTER_MAP),
 +        LintId::of(methods::MANUAL_FIND_MAP),
++        LintId::of(methods::MANUAL_SPLIT_ONCE),
 +        LintId::of(methods::MAP_IDENTITY),
 +        LintId::of(methods::OPTION_AS_REF_DEREF),
 +        LintId::of(methods::OPTION_FILTER_MAP),
 +        LintId::of(methods::SEARCH_IS_SOME),
 +        LintId::of(methods::SKIP_WHILE_NEXT),
 +        LintId::of(methods::UNNECESSARY_FILTER_MAP),
 +        LintId::of(methods::USELESS_ASREF),
 +        LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
 +        LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
 +        LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
 +        LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
 +        LintId::of(needless_bool::BOOL_COMPARISON),
 +        LintId::of(needless_bool::NEEDLESS_BOOL),
 +        LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
++        LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
 +        LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
 +        LintId::of(needless_update::NEEDLESS_UPDATE),
 +        LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
 +        LintId::of(no_effect::NO_EFFECT),
 +        LintId::of(no_effect::UNNECESSARY_OPERATION),
 +        LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
 +        LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
 +        LintId::of(precedence::PRECEDENCE),
 +        LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
 +        LintId::of(ranges::RANGE_ZIP_WITH_LEN),
 +        LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
 +        LintId::of(redundant_slicing::REDUNDANT_SLICING),
 +        LintId::of(reference::DEREF_ADDROF),
 +        LintId::of(reference::REF_IN_DEREF),
 +        LintId::of(repeat_once::REPEAT_ONCE),
 +        LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
 +        LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
 +        LintId::of(swap::MANUAL_SWAP),
 +        LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
 +        LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
 +        LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
 +        LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
 +        LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
 +        LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
 +        LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
 +        LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
 +        LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
 +        LintId::of(types::BORROWED_BOX),
 +        LintId::of(types::TYPE_COMPLEXITY),
 +        LintId::of(types::VEC_BOX),
 +        LintId::of(unit_types::UNIT_ARG),
 +        LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
 +        LintId::of(unwrap::UNNECESSARY_UNWRAP),
 +        LintId::of(useless_conversion::USELESS_CONVERSION),
 +        LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
 +    ]);
 +
 +    store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![
 +        LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
 +        LintId::of(approx_const::APPROX_CONSTANT),
 +        LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
 +        LintId::of(attrs::DEPRECATED_SEMVER),
 +        LintId::of(attrs::MISMATCHED_TARGET_OS),
 +        LintId::of(attrs::USELESS_ATTRIBUTE),
 +        LintId::of(bit_mask::BAD_BIT_MASK),
 +        LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
 +        LintId::of(booleans::LOGIC_BUG),
 +        LintId::of(casts::CAST_REF_TO_MUT),
 +        LintId::of(copies::IFS_SAME_COND),
 +        LintId::of(copies::IF_SAME_THEN_ELSE),
 +        LintId::of(derive::DERIVE_HASH_XOR_EQ),
 +        LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
 +        LintId::of(drop_forget_ref::DROP_COPY),
 +        LintId::of(drop_forget_ref::DROP_REF),
 +        LintId::of(drop_forget_ref::FORGET_COPY),
 +        LintId::of(drop_forget_ref::FORGET_REF),
 +        LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
 +        LintId::of(eq_op::EQ_OP),
 +        LintId::of(erasing_op::ERASING_OP),
 +        LintId::of(formatting::POSSIBLE_MISSING_COMMA),
 +        LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
 +        LintId::of(if_let_mutex::IF_LET_MUTEX),
 +        LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
 +        LintId::of(infinite_iter::INFINITE_ITER),
 +        LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
 +        LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
 +        LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
 +        LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
 +        LintId::of(loops::ITER_NEXT_LOOP),
 +        LintId::of(loops::NEVER_LOOP),
 +        LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
 +        LintId::of(mem_discriminant::MEM_DISCRIMINANT_NON_ENUM),
 +        LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
 +        LintId::of(methods::CLONE_DOUBLE_REF),
 +        LintId::of(methods::ITERATOR_STEP_BY_ZERO),
 +        LintId::of(methods::SUSPICIOUS_SPLITN),
 +        LintId::of(methods::UNINIT_ASSUMED_INIT),
 +        LintId::of(methods::ZST_OFFSET),
 +        LintId::of(minmax::MIN_MAX),
 +        LintId::of(misc::CMP_NAN),
 +        LintId::of(misc::FLOAT_CMP),
 +        LintId::of(misc::MODULO_ONE),
 +        LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
 +        LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
 +        LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
 +        LintId::of(ptr::INVALID_NULL_PTR_USAGE),
 +        LintId::of(ptr::MUT_FROM_REF),
 +        LintId::of(ranges::REVERSED_EMPTY_RANGES),
 +        LintId::of(regex::INVALID_REGEX),
 +        LintId::of(self_assignment::SELF_ASSIGNMENT),
 +        LintId::of(serde_api::SERDE_API_MISUSE),
 +        LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
 +        LintId::of(swap::ALMOST_SWAPPED),
 +        LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY),
 +        LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
 +        LintId::of(transmute::WRONG_TRANSMUTE),
 +        LintId::of(transmuting_null::TRANSMUTING_NULL),
 +        LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
 +        LintId::of(unicode::INVISIBLE_CHARACTERS),
 +        LintId::of(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),
 +    ]);
 +
 +    store.register_group(true, "clippy::suspicious", None, vec![
 +        LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
 +        LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
 +        LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
 +        LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
 +        LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
 +        LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
 +        LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
 +        LintId::of(loops::EMPTY_LOOP),
 +        LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
 +        LintId::of(loops::MUT_RANGE_BOUND),
 +        LintId::of(methods::SUSPICIOUS_MAP),
 +        LintId::of(mut_key::MUTABLE_KEY_TYPE),
 +        LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
 +        LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
 +    ]);
 +
 +    store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
 +        LintId::of(entry::MAP_ENTRY),
 +        LintId::of(escape::BOXED_LOCAL),
 +        LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
 +        LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
 +        LintId::of(loops::MANUAL_MEMCPY),
 +        LintId::of(loops::NEEDLESS_COLLECT),
 +        LintId::of(methods::EXPECT_FUN_CALL),
 +        LintId::of(methods::EXTEND_WITH_DRAIN),
 +        LintId::of(methods::ITER_NTH),
 +        LintId::of(methods::MANUAL_STR_REPEAT),
 +        LintId::of(methods::OR_FUN_CALL),
 +        LintId::of(methods::SINGLE_CHAR_PATTERN),
 +        LintId::of(misc::CMP_OWNED),
 +        LintId::of(mutex_atomic::MUTEX_ATOMIC),
 +        LintId::of(redundant_clone::REDUNDANT_CLONE),
 +        LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
 +        LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
 +        LintId::of(types::BOX_VEC),
 +        LintId::of(types::REDUNDANT_ALLOCATION),
 +        LintId::of(vec::USELESS_VEC),
 +        LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
 +    ]);
 +
 +    store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![
 +        LintId::of(cargo_common_metadata::CARGO_COMMON_METADATA),
++        LintId::of(feature_name::NEGATIVE_FEATURE_NAMES),
++        LintId::of(feature_name::REDUNDANT_FEATURE_NAMES),
 +        LintId::of(multiple_crate_versions::MULTIPLE_CRATE_VERSIONS),
 +        LintId::of(wildcard_dependencies::WILDCARD_DEPENDENCIES),
 +    ]);
 +
 +    store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
 +        LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
 +        LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
++        LintId::of(copies::BRANCHES_SHARING_CODE),
 +        LintId::of(disallowed_method::DISALLOWED_METHOD),
 +        LintId::of(disallowed_type::DISALLOWED_TYPE),
 +        LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
 +        LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
 +        LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS),
 +        LintId::of(future_not_send::FUTURE_NOT_SEND),
 +        LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
 +        LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
 +        LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
 +        LintId::of(mutex_atomic::MUTEX_INTEGER),
 +        LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
++        LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
 +        LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
 +        LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
 +        LintId::of(regex::TRIVIAL_REGEX),
 +        LintId::of(strings::STRING_LIT_AS_BYTES),
 +        LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
 +        LintId::of(transmute::USELESS_TRANSMUTE),
 +        LintId::of(use_self::USE_SELF),
 +    ]);
 +
 +    #[cfg(feature = "metadata-collector-lint")]
 +    {
 +        if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
 +            store.register_late_pass(|| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
 +            return;
 +        }
 +    }
 +
 +    // all the internal lints
 +    #[cfg(feature = "internal-lints")]
 +    {
 +        store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal));
 +        store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce));
 +        store.register_late_pass(|| Box::new(utils::inspector::DeepCodeInspector));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::CollapsibleCalls));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::CompilerLintFunctions::new()));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::IfChainStyle));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::InvalidPaths));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::InterningDefinedSymbol::default()));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::LintWithoutLintPass::default()));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::MatchTypeOnDiagItem));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::OuterExpnDataPass));
 +    }
 +
 +    store.register_late_pass(|| Box::new(utils::author::Author));
 +    store.register_late_pass(|| Box::new(await_holding_invalid::AwaitHolding));
 +    store.register_late_pass(|| Box::new(serde_api::SerdeApi));
 +    let vec_box_size_threshold = conf.vec_box_size_threshold;
 +    let type_complexity_threshold = conf.type_complexity_threshold;
-     store.register_late_pass(|| Box::new(approx_const::ApproxConstant));
++    let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
++    store.register_late_pass(move || Box::new(types::Types::new(
++        vec_box_size_threshold,
++        type_complexity_threshold,
++        avoid_breaking_exported_api,
++    )));
 +    store.register_late_pass(|| Box::new(booleans::NonminimalBool));
 +    store.register_late_pass(|| Box::new(needless_bitwise_bool::NeedlessBitwiseBool));
 +    store.register_late_pass(|| Box::new(eq_op::EqOp));
 +    store.register_late_pass(|| Box::new(enum_clike::UnportableVariant));
 +    store.register_late_pass(|| Box::new(float_literal::FloatLiteral));
 +    let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
 +    store.register_late_pass(move || Box::new(bit_mask::BitMask::new(verbose_bit_mask_threshold)));
 +    store.register_late_pass(|| Box::new(ptr::Ptr));
 +    store.register_late_pass(|| Box::new(ptr_eq::PtrEq));
 +    store.register_late_pass(|| Box::new(needless_bool::NeedlessBool));
++    store.register_late_pass(|| Box::new(needless_option_as_deref::OptionNeedlessDeref));
 +    store.register_late_pass(|| Box::new(needless_bool::BoolComparison));
 +    store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach));
-     store.register_early_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison));
 +    store.register_late_pass(|| Box::new(misc::MiscLints));
 +    store.register_late_pass(|| Box::new(eta_reduction::EtaReduction));
 +    store.register_late_pass(|| Box::new(identity_op::IdentityOp));
 +    store.register_late_pass(|| Box::new(erasing_op::ErasingOp));
 +    store.register_late_pass(|| Box::new(mut_mut::MutMut));
 +    store.register_late_pass(|| Box::new(mut_reference::UnnecessaryMutPassed));
 +    store.register_late_pass(|| Box::new(len_zero::LenZero));
 +    store.register_late_pass(|| Box::new(attrs::Attributes));
 +    store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
 +    store.register_late_pass(|| Box::new(collapsible_match::CollapsibleMatch));
 +    store.register_late_pass(|| Box::new(unicode::Unicode));
 +    store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
 +    store.register_late_pass(|| Box::new(strings::StringAdd));
 +    store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn));
 +    store.register_late_pass(|| Box::new(implicit_saturating_sub::ImplicitSaturatingSub));
 +    store.register_late_pass(|| Box::new(default_numeric_fallback::DefaultNumericFallback));
 +    store.register_late_pass(|| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor));
 +    store.register_late_pass(|| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
 +    store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
 +
 +    let msrv = conf.msrv.as_ref().and_then(|s| {
 +        parse_msrv(s, None, None).or_else(|| {
 +            sess.err(&format!("error reading Clippy's configuration file. `{}` is not a valid Rust version", s));
 +            None
 +        })
 +    });
 +
 +    let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
++    store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv)));
 +    store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv)));
 +    store.register_late_pass(move || Box::new(matches::Matches::new(msrv)));
 +    store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustive::new(msrv)));
 +    store.register_late_pass(move || Box::new(manual_strip::ManualStrip::new(msrv)));
 +    store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)));
 +    store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv)));
 +    store.register_late_pass(move || Box::new(checked_conversions::CheckedConversions::new(msrv)));
 +    store.register_late_pass(move || Box::new(mem_replace::MemReplace::new(msrv)));
 +    store.register_late_pass(move || Box::new(ranges::Ranges::new(msrv)));
 +    store.register_late_pass(move || Box::new(from_over_into::FromOverInto::new(msrv)));
 +    store.register_late_pass(move || Box::new(use_self::UseSelf::new(msrv)));
 +    store.register_late_pass(move || Box::new(missing_const_for_fn::MissingConstForFn::new(msrv)));
 +    store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark));
 +    store.register_late_pass(move || Box::new(casts::Casts::new(msrv)));
 +    store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
 +
 +    store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
 +    store.register_late_pass(|| Box::new(map_clone::MapClone));
 +    store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
 +    store.register_late_pass(|| Box::new(shadow::Shadow));
 +    store.register_late_pass(|| Box::new(unit_types::UnitTypes));
 +    store.register_late_pass(|| Box::new(loops::Loops));
 +    store.register_late_pass(|| Box::new(main_recursion::MainRecursion::default()));
 +    store.register_late_pass(|| Box::new(lifetimes::Lifetimes));
 +    store.register_late_pass(|| Box::new(entry::HashMapPass));
 +    store.register_late_pass(|| Box::new(minmax::MinMaxPass));
 +    store.register_late_pass(|| Box::new(open_options::OpenOptions));
 +    store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv));
 +    store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
 +    store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate));
 +    store.register_late_pass(|| Box::new(needless_borrow::NeedlessBorrow::default()));
 +    store.register_late_pass(|| Box::new(needless_borrowed_ref::NeedlessBorrowedRef));
 +    store.register_late_pass(|| Box::new(no_effect::NoEffect));
 +    store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment));
 +    store.register_late_pass(|| Box::new(transmute::Transmute));
 +    let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
 +    store.register_late_pass(move || Box::new(cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold)));
 +    let too_large_for_stack = conf.too_large_for_stack;
 +    store.register_late_pass(move || Box::new(escape::BoxedLocal{too_large_for_stack}));
 +    store.register_late_pass(move || Box::new(vec::UselessVec{too_large_for_stack}));
 +    store.register_late_pass(|| Box::new(panic_unimplemented::PanicUnimplemented));
 +    store.register_late_pass(|| Box::new(strings::StringLitAsBytes));
 +    store.register_late_pass(|| Box::new(derive::Derive));
++    store.register_late_pass(|| Box::new(derivable_impls::DerivableImpls));
 +    store.register_late_pass(|| Box::new(get_last_with_len::GetLastWithLen));
 +    store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef));
 +    store.register_late_pass(|| Box::new(empty_enum::EmptyEnum));
 +    store.register_late_pass(|| Box::new(absurd_extreme_comparisons::AbsurdExtremeComparisons));
 +    store.register_late_pass(|| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
 +    store.register_late_pass(|| Box::new(regex::Regex::default()));
 +    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_discriminant::MemDiscriminant));
 +    store.register_late_pass(|| Box::new(mem_forget::MemForget));
 +    store.register_late_pass(|| Box::new(arithmetic::Arithmetic::default()));
 +    store.register_late_pass(|| Box::new(assign_ops::AssignOps));
 +    store.register_late_pass(|| Box::new(let_if_seq::LetIfSeq));
 +    store.register_late_pass(|| Box::new(eval_order_dependence::EvalOrderDependence));
 +    store.register_late_pass(|| Box::new(missing_doc::MissingDoc::new()));
 +    store.register_late_pass(|| Box::new(missing_inline::MissingInline));
 +    store.register_late_pass(move || Box::new(exhaustive_items::ExhaustiveItems));
 +    store.register_late_pass(|| Box::new(if_let_some_result::OkIfLet));
 +    store.register_late_pass(|| Box::new(partialeq_ne_impl::PartialEqNeImpl));
 +    store.register_late_pass(|| Box::new(unused_io_amount::UnusedIoAmount));
 +    let enum_variant_size_threshold = conf.enum_variant_size_threshold;
 +    store.register_late_pass(move || Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)));
 +    store.register_late_pass(|| Box::new(explicit_write::ExplicitWrite));
 +    store.register_late_pass(|| Box::new(needless_pass_by_value::NeedlessPassByValue));
 +    let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new(
 +        conf.trivial_copy_size_limit,
 +        conf.pass_by_value_size_limit,
 +        conf.avoid_breaking_exported_api,
 +        &sess.target,
 +    );
 +    store.register_late_pass(move || Box::new(pass_by_ref_or_value));
 +    store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef));
 +    store.register_late_pass(|| Box::new(try_err::TryErr));
 +    store.register_late_pass(|| Box::new(bytecount::ByteCount));
 +    store.register_late_pass(|| Box::new(infinite_iter::InfiniteIter));
 +    store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody));
 +    store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default()));
 +    store.register_late_pass(|| Box::new(implicit_hasher::ImplicitHasher));
 +    store.register_late_pass(|| Box::new(fallible_impl_from::FallibleImplFrom));
 +    store.register_late_pass(|| Box::new(double_comparison::DoubleComparisons));
 +    store.register_late_pass(|| Box::new(question_mark::QuestionMark));
 +    store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
 +    store.register_late_pass(|| Box::new(suspicious_trait_impl::SuspiciousImpl));
 +    store.register_late_pass(|| Box::new(map_unit_fn::MapUnit));
 +    store.register_late_pass(|| Box::new(inherent_impl::MultipleInherentImpl));
 +    store.register_late_pass(|| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd));
 +    store.register_late_pass(|| Box::new(unwrap::Unwrap));
 +    store.register_late_pass(|| Box::new(duration_subsec::DurationSubsec));
 +    store.register_late_pass(|| Box::new(indexing_slicing::IndexingSlicing));
 +    store.register_late_pass(|| Box::new(non_copy_const::NonCopyConst));
 +    store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
 +    store.register_late_pass(|| Box::new(redundant_clone::RedundantClone));
 +    store.register_late_pass(|| Box::new(slow_vector_initialization::SlowVectorInit));
 +    store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
 +    store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
 +    store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
 +    store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
 +    store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
 +    store.register_late_pass(|| Box::new(integer_division::IntegerDivision));
 +    store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
 +    let max_trait_bounds = conf.max_trait_bounds;
 +    store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
 +    store.register_late_pass(|| Box::new(comparison_chain::ComparisonChain));
 +    store.register_late_pass(|| Box::new(mut_key::MutableKeyType));
 +    store.register_late_pass(|| Box::new(modulo_arithmetic::ModuloArithmetic));
 +    store.register_early_pass(|| Box::new(reference::DerefAddrOf));
 +    store.register_early_pass(|| Box::new(reference::RefInDeref));
 +    store.register_early_pass(|| Box::new(double_parens::DoubleParens));
 +    store.register_late_pass(|| Box::new(to_string_in_display::ToStringInDisplay::new()));
 +    store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
 +    store.register_early_pass(|| Box::new(if_not_else::IfNotElse));
 +    store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse));
 +    store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne));
 +    store.register_early_pass(|| Box::new(formatting::Formatting));
 +    store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints));
 +    store.register_early_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
 +    store.register_late_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
 +    store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
 +    store.register_late_pass(|| Box::new(returns::Return));
 +    store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf));
 +    store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements));
 +    store.register_early_pass(|| Box::new(precedence::Precedence));
 +    store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue));
 +    store.register_early_pass(|| Box::new(redundant_else::RedundantElse));
 +    store.register_late_pass(|| Box::new(create_dir::CreateDir));
 +    store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType));
 +    let cargo_ignore_publish = conf.cargo_ignore_publish;
 +    store.register_late_pass(move || Box::new(cargo_common_metadata::CargoCommonMetadata::new(cargo_ignore_publish)));
 +    store.register_late_pass(|| Box::new(multiple_crate_versions::MultipleCrateVersions));
 +    store.register_late_pass(|| Box::new(wildcard_dependencies::WildcardDependencies));
 +    let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions;
 +    store.register_early_pass(move || Box::new(literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability)));
 +    let literal_representation_threshold = conf.literal_representation_threshold;
 +    store.register_early_pass(move || Box::new(literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)));
 +    let enum_variant_name_threshold = conf.enum_variant_name_threshold;
 +    store.register_late_pass(move || Box::new(enum_variants::EnumVariantNames::new(enum_variant_name_threshold, avoid_breaking_exported_api)));
 +    store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
 +    let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive;
 +    store.register_late_pass(move || Box::new(upper_case_acronyms::UpperCaseAcronyms::new(avoid_breaking_exported_api, upper_case_acronyms_aggressive)));
 +    store.register_late_pass(|| Box::new(default::Default::default()));
 +    store.register_late_pass(|| Box::new(unused_self::UnusedSelf));
 +    store.register_late_pass(|| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
 +    store.register_late_pass(|| Box::new(exit::Exit));
 +    store.register_late_pass(|| Box::new(to_digit_is_some::ToDigitIsSome));
 +    let array_size_threshold = conf.array_size_threshold;
 +    store.register_late_pass(move || Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold)));
 +    store.register_late_pass(move || Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold)));
 +    store.register_late_pass(|| Box::new(floating_point_arithmetic::FloatingPointArithmetic));
 +    store.register_early_pass(|| Box::new(as_conversions::AsConversions));
 +    store.register_late_pass(|| Box::new(let_underscore::LetUnderscore));
 +    store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports));
 +    let max_fn_params_bools = conf.max_fn_params_bools;
 +    let max_struct_bools = conf.max_struct_bools;
 +    store.register_early_pass(move || Box::new(excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools)));
 +    store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
 +    let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
 +    store.register_late_pass(move || Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
 +    store.register_late_pass(|| Box::new(verbose_file_reads::VerboseFileReads));
 +    store.register_late_pass(|| Box::new(redundant_pub_crate::RedundantPubCrate::default()));
 +    store.register_late_pass(|| Box::new(unnamed_address::UnnamedAddress));
 +    store.register_late_pass(|| Box::new(dereference::Dereferencing::default()));
 +    store.register_late_pass(|| Box::new(option_if_let_else::OptionIfLetElse));
 +    store.register_late_pass(|| Box::new(future_not_send::FutureNotSend));
 +    store.register_late_pass(|| Box::new(if_let_mutex::IfLetMutex));
 +    store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock));
 +    store.register_late_pass(|| Box::new(match_on_vec_items::MatchOnVecItems));
 +    store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn));
 +    store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
 +    store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn));
 +    let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
 +    store.register_early_pass(move || Box::new(non_expressive_names::NonExpressiveNames {
 +        single_char_binding_names_threshold,
 +    }));
 +    let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(&macro_matcher)));
 +    store.register_late_pass(|| Box::new(macro_use::MacroUseImports::default()));
 +    store.register_late_pass(|| Box::new(pattern_type_mismatch::PatternTypeMismatch));
 +    store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive));
 +    store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
 +    store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
 +    store.register_late_pass(|| Box::new(self_assignment::SelfAssignment));
 +    store.register_late_pass(|| Box::new(manual_unwrap_or::ManualUnwrapOr));
 +    store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
 +    store.register_late_pass(|| Box::new(float_equality_without_abs::FloatEqualityWithoutAbs));
 +    store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
 +    store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
 +    let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_late_pass(move || Box::new(disallowed_method::DisallowedMethod::new(&disallowed_methods)));
 +    store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
 +    store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
 +    store.register_late_pass(|| Box::new(undropped_manually_drops::UndroppedManuallyDrops));
 +    store.register_late_pass(|| Box::new(strings::StrToString));
 +    store.register_late_pass(|| Box::new(strings::StringToString));
 +    store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
 +    store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default()));
 +    store.register_late_pass(|| Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons));
 +    store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
 +    store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
 +    store.register_late_pass(|| Box::new(manual_map::ManualMap));
 +    store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
++    store.register_late_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison));
++    store.register_early_pass(move || Box::new(module_style::ModStyle));
 +    store.register_late_pass(|| Box::new(unused_async::UnusedAsync));
 +    let disallowed_types = conf.disallowed_types.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_late_pass(move || Box::new(disallowed_type::DisallowedType::new(&disallowed_types)));
 +    let import_renames = conf.enforced_import_renames.clone();
 +    store.register_late_pass(move || Box::new(missing_enforced_import_rename::ImportRename::new(import_renames.clone())));
 +    let scripts = conf.allowed_scripts.clone();
 +    store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(&scripts)));
 +    store.register_late_pass(|| Box::new(strlen_on_c_strings::StrlenOnCStrings));
 +    store.register_late_pass(move || Box::new(self_named_constructors::SelfNamedConstructors));
++    store.register_late_pass(move || Box::new(feature_name::FeatureName));
 +}
 +
 +#[rustfmt::skip]
 +fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) {
 +    store.register_removed(
 +        "should_assert_eq",
 +        "`assert!()` will be more flexible with RFC 2011",
 +    );
 +    store.register_removed(
 +        "extend_from_slice",
 +        "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice",
 +    );
 +    store.register_removed(
 +        "range_step_by_zero",
 +        "`iterator.step_by(0)` panics nowadays",
 +    );
 +    store.register_removed(
 +        "unstable_as_slice",
 +        "`Vec::as_slice` has been stabilized in 1.7",
 +    );
 +    store.register_removed(
 +        "unstable_as_mut_slice",
 +        "`Vec::as_mut_slice` has been stabilized in 1.7",
 +    );
 +    store.register_removed(
 +        "misaligned_transmute",
 +        "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
 +    );
 +    store.register_removed(
 +        "assign_ops",
 +        "using compound assignment operators (e.g., `+=`) is harmless",
 +    );
 +    store.register_removed(
 +        "if_let_redundant_pattern_matching",
 +        "this lint has been changed to redundant_pattern_matching",
 +    );
 +    store.register_removed(
 +        "unsafe_vector_initialization",
 +        "the replacement suggested by this lint had substantially different behavior",
 +    );
 +    store.register_removed(
 +        "reverse_range_loop",
 +        "this lint is now included in reversed_empty_ranges",
 +    );
 +}
 +
 +/// Register renamed lints.
 +///
 +/// Used in `./src/driver.rs`.
 +pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
 +    ls.register_renamed("clippy::stutter", "clippy::module_name_repetitions");
 +    ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default");
 +    ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity");
 +    ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes");
 +    ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map");
 +    ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions");
 +    ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions");
 +    ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or");
 +    ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or");
 +    ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or");
 +    ls.register_renamed("clippy::option_unwrap_used", "clippy::unwrap_used");
 +    ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used");
 +    ls.register_renamed("clippy::option_expect_used", "clippy::expect_used");
 +    ls.register_renamed("clippy::result_expect_used", "clippy::expect_used");
 +    ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles");
 +    ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles");
 +    ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
 +    ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
 +    ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
 +
 +    // uplifted lints
 +    ls.register_renamed("clippy::invalid_ref", "invalid_value");
 +    ls.register_renamed("clippy::into_iter_on_array", "array_into_iter");
 +    ls.register_renamed("clippy::unused_label", "unused_labels");
 +    ls.register_renamed("clippy::drop_bounds", "drop_bounds");
 +    ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr");
 +    ls.register_renamed("clippy::panic_params", "non_fmt_panics");
 +    ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints");
 +    ls.register_renamed("clippy::invalid_atomic_ordering", "invalid_atomic_ordering");
 +}
 +
 +// 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 82bf49f5b49a46c499158445258d5f1808835464,0000000000000000000000000000000000000000..68bef2f4c8bbb351e808dcd6c78193e8896a5bc6
mode 100644,000000..100644
--- /dev/null
@@@ -1,74 -1,0 +1,72 @@@
- use clippy_utils::visitors::LocalUsedVisitor;
 +use super::FOR_KV_MAP;
 +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet;
 +use clippy_utils::sugg;
 +use clippy_utils::ty::is_type_diagnostic_item;
-         PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
-             !LocalUsedVisitor::new(cx, id).check_expr(body)
-         },
++use clippy_utils::visitors::is_local_used;
 +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +use rustc_span::sym;
 +
 +/// Checks for the `FOR_KV_MAP` lint.
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    pat: &'tcx Pat<'_>,
 +    arg: &'tcx Expr<'_>,
 +    body: &'tcx Expr<'_>,
 +    expr: &'tcx Expr<'_>,
 +) {
 +    let pat_span = pat.span;
 +
 +    if let PatKind::Tuple(pat, _) = pat.kind {
 +        if pat.len() == 2 {
 +            let arg_span = arg.span;
 +            let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() {
 +                ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) {
 +                    (key, _) if pat_is_wild(cx, key, body) => (pat[1].span, "value", ty, mutbl),
 +                    (_, value) if pat_is_wild(cx, value, body) => (pat[0].span, "key", ty, Mutability::Not),
 +                    _ => return,
 +                },
 +                _ => return,
 +            };
 +            let mutbl = match mutbl {
 +                Mutability::Not => "",
 +                Mutability::Mut => "_mut",
 +            };
 +            let arg = match arg.kind {
 +                ExprKind::AddrOf(BorrowKind::Ref, _, expr) => expr,
 +                _ => arg,
 +            };
 +
 +            if is_type_diagnostic_item(cx, ty, sym::hashmap_type) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) {
 +                span_lint_and_then(
 +                    cx,
 +                    FOR_KV_MAP,
 +                    expr.span,
 +                    &format!("you seem to want to iterate on a map's {}s", kind),
 +                    |diag| {
 +                        let map = sugg::Sugg::hir(cx, arg, "map");
 +                        multispan_sugg(
 +                            diag,
 +                            "use the corresponding method",
 +                            vec![
 +                                (pat_span, snippet(cx, new_pat_span, kind).into_owned()),
 +                                (arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl)),
 +                            ],
 +                        );
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +/// Returns `true` if the pattern is a `PatWild` or an ident prefixed with `_`.
 +fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
 +    match *pat {
 +        PatKind::Wild => true,
++        PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => !is_local_used(cx, body, id),
 +        _ => false,
 +    }
 +}
index 5852674da578067661fcf961501aa890cd531d02,0000000000000000000000000000000000000000..5b6e27085d580986bd892ac46552dc6b2d253e11
mode 100644,000000..100644
--- /dev/null
@@@ -1,87 -1,0 +1,91 @@@
-             if let Some(higher::IfLet { let_pat, let_expr, if_else: None, .. }) = higher::IfLet::hir(cx, inner_expr);
 +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};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::LangItem::{OptionSome, ResultOk};
 +use rustc_hir::{Expr, ExprKind, Pat, PatKind, StmtKind};
 +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,
 +) {
 +    if let ExprKind::Block(block, _) = body.kind {
 +        // Ensure the `if let` statement is the only expression or statement in the for-loop
 +        let inner_expr = if block.stmts.len() == 1 && block.expr.is_none() {
 +            let match_stmt = &block.stmts[0];
 +            if let StmtKind::Semi(inner_expr) = match_stmt.kind {
 +                Some(inner_expr)
 +            } else {
 +                None
 +            }
 +        } else if block.stmts.is_empty() {
 +            block.expr
 +        } else {
 +            None
 +        };
 +
 +        if_chain! {
 +            if let Some(inner_expr) = inner_expr;
++            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 epxr 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()",
 +                        _ => ""
 +                    }
 +                    _ => ""
 +                };
 +
 +                span_lint_and_then(
 +                    cx,
 +                    MANUAL_FLATTEN,
 +                    span,
 +                    &msg,
 +                    |diag| {
 +                        let sugg = format!("{}{}.flatten()", arg_snippet, copied);
 +                        diag.span_suggestion(
 +                            arg.span,
 +                            "try",
 +                            sugg,
 +                            Applicability::MaybeIncorrect,
 +                        );
 +                        diag.span_help(
 +                            inner_expr.span,
 +                            "...and remove the `if let` statement in the for loop",
 +                        );
 +                    }
 +                );
 +            }
 +        }
 +    }
 +}
index bd9de5e08d736e877f013b15bcf878afe8666877,0000000000000000000000000000000000000000..2860cb68f42f2c5bc74a371aa2a68ef2fb78209d
mode 100644,000000..100644
--- /dev/null
@@@ -1,634 -1,0 +1,649 @@@
-         if let Some(higher::While { if_cond, if_then, .. }) = higher::While::hir(&expr) {
-             while_immutable_condition::check(cx, if_cond, if_then);
 +mod empty_loop;
 +mod explicit_counter_loop;
 +mod explicit_into_iter_loop;
 +mod explicit_iter_loop;
 +mod for_kv_map;
 +mod for_loops_over_fallibles;
 +mod iter_next_loop;
 +mod manual_flatten;
 +mod manual_memcpy;
 +mod mut_range_bound;
 +mod needless_collect;
 +mod needless_range_loop;
 +mod never_loop;
 +mod same_item_push;
 +mod single_element_loop;
 +mod utils;
 +mod while_immutable_condition;
 +mod while_let_loop;
 +mod while_let_on_iterator;
 +
 +use clippy_utils::higher;
 +use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use utils::{get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor, InitializeVisitor};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for for-loops that manually copy items between
 +    /// slices that could be optimized by having a memcpy.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is not as fast as a memcpy.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let src = vec![1];
 +    /// # let mut dst = vec![0; 65];
 +    /// for i in 0..src.len() {
 +    ///     dst[i + 64] = src[i];
 +    /// }
 +    /// ```
 +    /// Could be written as:
 +    /// ```rust
 +    /// # let src = vec![1];
 +    /// # let mut dst = vec![0; 65];
 +    /// dst[64..(src.len() + 64)].clone_from_slice(&src[..]);
 +    /// ```
 +    pub MANUAL_MEMCPY,
 +    perf,
 +    "manually copying items between slices"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for looping over the range of `0..len` of some
 +    /// collection just to get the values by index.
 +    ///
 +    /// ### Why is this bad?
 +    /// Just iterating the collection itself makes the intent
 +    /// more clear and is probably faster.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec = vec!['a', 'b', 'c'];
 +    /// for i in 0..vec.len() {
 +    ///     println!("{}", vec[i]);
 +    /// }
 +    /// ```
 +    /// Could be written as:
 +    /// ```rust
 +    /// let vec = vec!['a', 'b', 'c'];
 +    /// for i in vec {
 +    ///     println!("{}", i);
 +    /// }
 +    /// ```
 +    pub NEEDLESS_RANGE_LOOP,
 +    style,
 +    "for-looping over a range of indices where an iterator over items would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for loops on `x.iter()` where `&x` will do, and
 +    /// suggests the latter.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability.
 +    ///
 +    /// ### Known problems
 +    /// False negatives. We currently only warn on some known
 +    /// types.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // with `y` a `Vec` or slice:
 +    /// # let y = vec![1];
 +    /// for x in y.iter() {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    /// can be rewritten to
 +    /// ```rust
 +    /// # let y = vec![1];
 +    /// for x in &y {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    pub EXPLICIT_ITER_LOOP,
 +    pedantic,
 +    "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for loops on `y.into_iter()` where `y` will do, and
 +    /// suggests the latter.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let y = vec![1];
 +    /// // with `y` a `Vec` or slice:
 +    /// for x in y.into_iter() {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    /// can be rewritten to
 +    /// ```rust
 +    /// # let y = vec![1];
 +    /// for x in y {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    pub EXPLICIT_INTO_ITER_LOOP,
 +    pedantic,
 +    "for-looping over `_.into_iter()` when `_` would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for loops on `x.next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `next()` returns either `Some(value)` if there was a
 +    /// value, or `None` otherwise. The insidious thing is that `Option<_>`
 +    /// implements `IntoIterator`, so that possibly one value will be iterated,
 +    /// leading to some hard to find bugs. No one will want to write such code
 +    /// [except to win an Underhanded Rust
 +    /// Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// for x in y.next() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    pub ITER_NEXT_LOOP,
 +    correctness,
 +    "for-looping over `_.next()` which is probably not intended"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `for` loops over `Option` or `Result` values.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. This is more clearly expressed as an `if
 +    /// let`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    ///
 +    /// // Bad
 +    /// for x in opt {
 +    ///     // ..
 +    /// }
 +    ///
 +    /// // Good
 +    /// if let Some(x) = opt {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// // or
 +    ///
 +    /// ```rust
 +    /// # let res: Result<i32, std::io::Error> = Ok(1);
 +    ///
 +    /// // Bad
 +    /// for x in &res {
 +    ///     // ..
 +    /// }
 +    ///
 +    /// // Good
 +    /// if let Ok(x) = res {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    pub FOR_LOOPS_OVER_FALLIBLES,
 +    suspicious,
 +    "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects `loop + match` combinations that are easier
 +    /// written as a `while let` loop.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `while let` loop is usually shorter and more
 +    /// readable.
 +    ///
 +    /// ### Known problems
 +    /// Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).
 +    ///
 +    /// ### Example
 +    /// ```rust,no_run
 +    /// # let y = Some(1);
 +    /// loop {
 +    ///     let x = match y {
 +    ///         Some(x) => x,
 +    ///         None => break,
 +    ///     };
 +    ///     // .. do something with x
 +    /// }
 +    /// // is easier written as
 +    /// while let Some(x) = y {
 +    ///     // .. do something with x
 +    /// };
 +    /// ```
 +    pub WHILE_LET_LOOP,
 +    complexity,
 +    "`loop { if let { ... } else break }`, which can be written as a `while let` loop"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for functions collecting an iterator when collect
 +    /// is not needed.
 +    ///
 +    /// ### Why is this bad?
 +    /// `collect` causes the allocation of a new data structure,
 +    /// when this allocation may not be needed.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iterator = vec![1].into_iter();
 +    /// let len = iterator.clone().collect::<Vec<_>>().len();
 +    /// // should be
 +    /// let len = iterator.count();
 +    /// ```
 +    pub NEEDLESS_COLLECT,
 +    perf,
 +    "collecting an iterator when collect is not needed"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks `for` loops over slices with an explicit counter
 +    /// and suggests the use of `.enumerate()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `.enumerate()` makes the intent more clear,
 +    /// declutters the code and may be faster in some instances.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let v = vec![1];
 +    /// # fn bar(bar: usize, baz: usize) {}
 +    /// let mut i = 0;
 +    /// for item in &v {
 +    ///     bar(i, *item);
 +    ///     i += 1;
 +    /// }
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// # let v = vec![1];
 +    /// # fn bar(bar: usize, baz: usize) {}
 +    /// for (i, item) in v.iter().enumerate() { bar(i, *item); }
 +    /// ```
 +    pub EXPLICIT_COUNTER_LOOP,
 +    complexity,
 +    "for-looping with an explicit counter when `_.enumerate()` would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for empty `loop` expressions.
 +    ///
 +    /// ### Why is this bad?
 +    /// These busy loops burn CPU cycles without doing
 +    /// anything. It is _almost always_ a better idea to `panic!` than to have
 +    /// a busy loop.
 +    ///
 +    /// If panicking isn't possible, think of the environment and either:
 +    ///   - block on something
 +    ///   - sleep the thread for some microseconds
 +    ///   - yield or pause the thread
 +    ///
 +    /// For `std` targets, this can be done with
 +    /// [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html)
 +    /// or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).
 +    ///
 +    /// For `no_std` targets, doing this is more complicated, especially because
 +    /// `#[panic_handler]`s can't panic. To stop/pause the thread, you will
 +    /// probably need to invoke some target-specific intrinsic. Examples include:
 +    ///   - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html)
 +    ///   - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html)
 +    ///
 +    /// ### Example
 +    /// ```no_run
 +    /// loop {}
 +    /// ```
 +    pub EMPTY_LOOP,
 +    suspicious,
 +    "empty `loop {}`, which should block or sleep"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `while let` expressions on iterators.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. A simple `for` loop is shorter and conveys
 +    /// the intent better.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// while let Some(val) = iter() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    pub WHILE_LET_ON_ITERATOR,
 +    style,
 +    "using a `while let` loop instead of a for loop on an iterator"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for iterating a map (`HashMap` or `BTreeMap`) and
 +    /// ignoring either the keys or values.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. There are `keys` and `values` methods that
 +    /// can be used to express that don't need the values or keys.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// for (k, _) in &map {
 +    ///     ..
 +    /// }
 +    /// ```
 +    ///
 +    /// could be replaced by
 +    ///
 +    /// ```ignore
 +    /// for k in map.keys() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    pub FOR_KV_MAP,
 +    style,
 +    "looping on a map using `iter` when `keys` or `values` would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for loops that will always `break`, `return` or
 +    /// `continue` an outer loop.
 +    ///
 +    /// ### Why is this bad?
 +    /// This loop never loops, all it does is obfuscating the
 +    /// code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// loop {
 +    ///     ..;
 +    ///     break;
 +    /// }
 +    /// ```
 +    pub NEVER_LOOP,
 +    correctness,
 +    "any loop that will always `break` or `return`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for loops which have a range bound that is a mutable variable
 +    ///
 +    /// ### Why is this bad?
 +    /// One might think that modifying the mutable variable changes the loop bounds
 +    ///
++    /// ### Known problems
++    /// False positive when mutation is followed by a `break`, but the `break` is not immediately
++    /// after the mutation:
++    ///
++    /// ```rust
++    /// let mut x = 5;
++    /// for _ in 0..x {
++    ///     x += 1; // x is a range bound that is mutated
++    ///     ..; // some other expression
++    ///     break; // leaves the loop, so mutation is not an issue
++    /// }
++    /// ```
++    ///
++    /// False positive on nested loops ([#6072](https://github.com/rust-lang/rust-clippy/issues/6072))
++    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut foo = 42;
 +    /// for i in 0..foo {
 +    ///     foo -= 1;
 +    ///     println!("{}", i); // prints numbers from 0 to 42, not 0 to 21
 +    /// }
 +    /// ```
 +    pub MUT_RANGE_BOUND,
 +    suspicious,
 +    "for loop over a range where one of the bounds is a mutable variable"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks whether variables used within while loop condition
 +    /// can be (and are) mutated in the body.
 +    ///
 +    /// ### Why is this bad?
 +    /// If the condition is unchanged, entering the body of the loop
 +    /// will lead to an infinite loop.
 +    ///
 +    /// ### Known problems
 +    /// If the `while`-loop is in a closure, the check for mutation of the
 +    /// condition variables in the body can cause false negatives. For example when only `Upvar` `a` is
 +    /// in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let i = 0;
 +    /// while i > 10 {
 +    ///     println!("let me loop forever!");
 +    /// }
 +    /// ```
 +    pub WHILE_IMMUTABLE_CONDITION,
 +    correctness,
 +    "variables used within while expression are not mutated in the body"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks whether a for loop is being used to push a constant
 +    /// value into a Vec.
 +    ///
 +    /// ### Why is this bad?
 +    /// This kind of operation can be expressed more succinctly with
 +    /// `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also
 +    /// have better performance.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let item1 = 2;
 +    /// let item2 = 3;
 +    /// let mut vec: Vec<u8> = Vec::new();
 +    /// for _ in 0..20 {
 +    ///    vec.push(item1);
 +    /// }
 +    /// for _ in 0..30 {
 +    ///     vec.push(item2);
 +    /// }
 +    /// ```
 +    /// could be written as
 +    /// ```rust
 +    /// let item1 = 2;
 +    /// let item2 = 3;
 +    /// let mut vec: Vec<u8> = vec![item1; 20];
 +    /// vec.resize(20 + 30, item2);
 +    /// ```
 +    pub SAME_ITEM_PUSH,
 +    style,
 +    "the same item is pushed inside of a for loop"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks whether a for loop has a single element.
 +    ///
 +    /// ### Why is this bad?
 +    /// There is no reason to have a loop of a
 +    /// single element.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let item1 = 2;
 +    /// for item in &[item1] {
 +    ///     println!("{}", item);
 +    /// }
 +    /// ```
 +    /// could be written as
 +    /// ```rust
 +    /// let item1 = 2;
 +    /// let item = &item1;
 +    /// println!("{}", item);
 +    /// ```
 +    pub SINGLE_ELEMENT_LOOP,
 +    complexity,
 +    "there is no reason to have a single element loop"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Check for unnecessary `if let` usage in a for loop
 +    /// where only the `Some` or `Ok` variant of the iterator element is used.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is verbose and can be simplified
 +    /// by first calling the `flatten` method on the `Iterator`.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust
 +    /// let x = vec![Some(1), Some(2), Some(3)];
 +    /// for n in x {
 +    ///     if let Some(n) = n {
 +    ///         println!("{}", n);
 +    ///     }
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = vec![Some(1), Some(2), Some(3)];
 +    /// for n in x.into_iter().flatten() {
 +    ///     println!("{}", n);
 +    /// }
 +    /// ```
 +    pub MANUAL_FLATTEN,
 +    complexity,
 +    "for loops over `Option`s or `Result`s with a single expression can be simplified"
 +}
 +
 +declare_lint_pass!(Loops => [
 +    MANUAL_MEMCPY,
 +    MANUAL_FLATTEN,
 +    NEEDLESS_RANGE_LOOP,
 +    EXPLICIT_ITER_LOOP,
 +    EXPLICIT_INTO_ITER_LOOP,
 +    ITER_NEXT_LOOP,
 +    FOR_LOOPS_OVER_FALLIBLES,
 +    WHILE_LET_LOOP,
 +    NEEDLESS_COLLECT,
 +    EXPLICIT_COUNTER_LOOP,
 +    EMPTY_LOOP,
 +    WHILE_LET_ON_ITERATOR,
 +    FOR_KV_MAP,
 +    NEVER_LOOP,
 +    MUT_RANGE_BOUND,
 +    WHILE_IMMUTABLE_CONDITION,
 +    SAME_ITEM_PUSH,
 +    SINGLE_ELEMENT_LOOP,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Loops {
 +    #[allow(clippy::too_many_lines)]
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let Some(higher::ForLoop { pat, arg, body, span }) = higher::ForLoop::hir(expr) {
 +            // we don't want to check expanded macros
 +            // this check is not at the top of the function
 +            // since higher::for_loop expressions are marked as expansions
 +            if body.span.from_expansion() {
 +                return;
 +            }
 +            check_for_loop(cx, pat, arg, body, expr, span);
 +        }
 +
 +        // we don't want to check expanded macros
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        // check for never_loop
 +        never_loop::check(cx, expr);
 +
 +        // check for `loop { if let {} else break }` that could be `while let`
 +        // (also matches an explicit "match" instead of "if let")
 +        // (even if the "match" or "if let" is used for declaration)
 +        if let ExprKind::Loop(block, _, LoopSource::Loop, _) = expr.kind {
 +            // also check for empty `loop {}` statements, skipping those in #[panic_handler]
 +            empty_loop::check(cx, expr, block);
 +            while_let_loop::check(cx, expr, block);
 +        }
 +
 +        while_let_on_iterator::check(cx, expr);
 +
++        if let Some(higher::While { condition, body }) = higher::While::hir(expr) {
++            while_immutable_condition::check(cx, condition, body);
 +        }
 +
 +        needless_collect::check(expr, cx);
 +    }
 +}
 +
 +fn check_for_loop<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    pat: &'tcx Pat<'_>,
 +    arg: &'tcx Expr<'_>,
 +    body: &'tcx Expr<'_>,
 +    expr: &'tcx Expr<'_>,
 +    span: Span,
 +) {
 +    let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
 +    if !is_manual_memcpy_triggered {
 +        needless_range_loop::check(cx, pat, arg, body, expr);
 +        explicit_counter_loop::check(cx, pat, arg, body, expr);
 +    }
 +    check_for_loop_arg(cx, pat, arg, expr);
 +    for_kv_map::check(cx, pat, arg, body, expr);
 +    mut_range_bound::check(cx, arg, body);
 +    single_element_loop::check(cx, pat, arg, body, expr);
 +    same_item_push::check(cx, pat, arg, body, expr);
 +    manual_flatten::check(cx, pat, arg, body, span);
 +}
 +
 +fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) {
 +    let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used
 +
 +    if let ExprKind::MethodCall(method, _, [self_arg], _) = arg.kind {
 +        let method_name = &*method.ident.as_str();
 +        // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
 +        match method_name {
 +            "iter" | "iter_mut" => explicit_iter_loop::check(cx, self_arg, arg, method_name),
 +            "into_iter" => {
 +                explicit_iter_loop::check(cx, self_arg, arg, method_name);
 +                explicit_into_iter_loop::check(cx, self_arg, arg);
 +            },
 +            "next" => {
 +                next_loop_linted = iter_next_loop::check(cx, arg, expr);
 +            },
 +            _ => {},
 +        }
 +    }
 +
 +    if !next_loop_linted {
 +        for_loops_over_fallibles::check(cx, pat, arg);
 +    }
 +}
index 344dc5074d369ff5e47e2751fbb46c79019963aa,0000000000000000000000000000000000000000..358d53e8859d08d06a7b72ff565da1f0f5608118
mode 100644,000000..100644
--- /dev/null
@@@ -1,118 -1,0 +1,174 @@@
- use clippy_utils::diagnostics::span_lint;
- use clippy_utils::{higher, path_to_local};
 +use super::MUT_RANGE_BOUND;
- use rustc_hir::{BindingAnnotation, Expr, HirId, Node, PatKind};
++use clippy_utils::diagnostics::span_lint_and_note;
++use clippy_utils::{get_enclosing_block, higher, path_to_local};
 +use if_chain::if_chain;
-     if let Some(higher::Range {
-         start: Some(start),
-         end: Some(end),
-         ..
-     }) = higher::Range::hir(arg)
-     {
-         let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)];
-         if mut_ids[0].is_some() || mut_ids[1].is_some() {
-             let (span_low, span_high) = check_for_mutation(cx, body, &mut_ids);
++use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
++use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::LateContext;
++use rustc_middle::hir::map::Map;
 +use rustc_middle::{mir::FakeReadCause, ty};
 +use rustc_span::source_map::Span;
 +use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 +
 +pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
-         span_lint(
++    if_chain! {
++        if let Some(higher::Range {
++            start: Some(start),
++            end: Some(end),
++            ..
++        }) = higher::Range::hir(arg);
++        let (mut_id_start, mut_id_end) = (check_for_mutability(cx, start), check_for_mutability(cx, end));
++        if mut_id_start.is_some() || mut_id_end.is_some();
++        then {
++            let (span_low, span_high) = check_for_mutation(cx, body, mut_id_start, mut_id_end);
 +            mut_warn_with_span(cx, span_low);
 +            mut_warn_with_span(cx, span_high);
 +        }
 +    }
 +}
 +
 +fn mut_warn_with_span(cx: &LateContext<'_>, span: Option<Span>) {
 +    if let Some(sp) = span {
-             "attempt to mutate range bound within loop; note that the range of the loop is unchanged",
++        span_lint_and_note(
 +            cx,
 +            MUT_RANGE_BOUND,
 +            sp,
-     bound_ids: &[Option<HirId>],
++            "attempt to mutate range bound within loop",
++            None,
++            "the range of the loop is unchanged",
 +        );
 +    }
 +}
 +
 +fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId> {
 +    if_chain! {
 +        if let Some(hir_id) = path_to_local(bound);
 +        if let Node::Binding(pat) = cx.tcx.hir().get(hir_id);
 +        if let PatKind::Binding(BindingAnnotation::Mutable, ..) = pat.kind;
 +        then {
 +            return Some(hir_id);
 +        }
 +    }
 +    None
 +}
 +
 +fn check_for_mutation<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    body: &Expr<'_>,
-         hir_id_low: bound_ids[0],
-         hir_id_high: bound_ids[1],
++    bound_id_start: Option<HirId>,
++    bound_id_end: Option<HirId>,
 +) -> (Option<Span>, Option<Span>) {
 +    let mut delegate = MutatePairDelegate {
 +        cx,
-                 if Some(id) == self.hir_id_low {
++        hir_id_low: bound_id_start,
++        hir_id_high: bound_id_end,
 +        span_low: None,
 +        span_high: None,
 +    };
 +    cx.tcx.infer_ctxt().enter(|infcx| {
 +        ExprUseVisitor::new(
 +            &mut delegate,
 +            &infcx,
 +            body.hir_id.owner,
 +            cx.param_env,
 +            cx.typeck_results(),
 +        )
 +        .walk_expr(body);
 +    });
++
 +    delegate.mutation_span()
 +}
 +
 +struct MutatePairDelegate<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    hir_id_low: Option<HirId>,
 +    hir_id_high: Option<HirId>,
 +    span_low: Option<Span>,
 +    span_high: Option<Span>,
 +}
 +
 +impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
 +    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
 +
 +    fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) {
 +        if let ty::BorrowKind::MutBorrow = bk {
 +            if let PlaceBase::Local(id) = cmt.place.base {
-                 if Some(id) == self.hir_id_high {
++                if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
 +                    self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id));
 +                }
-             if Some(id) == self.hir_id_low {
++                if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
 +                    self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id));
 +                }
 +            }
 +        }
 +    }
 +
 +    fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
 +        if let PlaceBase::Local(id) = cmt.place.base {
-             if Some(id) == self.hir_id_high {
++            if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
 +                self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id));
 +            }
++            if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
 +                self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id));
 +            }
 +        }
 +    }
 +
 +    fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
 +}
 +
 +impl MutatePairDelegate<'_, '_> {
 +    fn mutation_span(&self) -> (Option<Span>, Option<Span>) {
 +        (self.span_low, self.span_high)
 +    }
 +}
++
++struct BreakAfterExprVisitor {
++    hir_id: HirId,
++    past_expr: bool,
++    past_candidate: bool,
++    break_after_expr: bool,
++}
++
++impl BreakAfterExprVisitor {
++    pub fn is_found(cx: &LateContext<'_>, hir_id: HirId) -> bool {
++        let mut visitor = BreakAfterExprVisitor {
++            hir_id,
++            past_expr: false,
++            past_candidate: false,
++            break_after_expr: false,
++        };
++
++        get_enclosing_block(cx, hir_id).map_or(false, |block| {
++            visitor.visit_block(block);
++            visitor.break_after_expr
++        })
++    }
++}
++
++impl intravisit::Visitor<'tcx> for BreakAfterExprVisitor {
++    type Map = Map<'tcx>;
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
++        if self.past_candidate {
++            return;
++        }
++
++        if expr.hir_id == self.hir_id {
++            self.past_expr = true;
++        } else if self.past_expr {
++            if matches!(&expr.kind, ExprKind::Break(..)) {
++                self.break_after_expr = true;
++            }
++
++            self.past_candidate = true;
++        } else {
++            intravisit::walk_expr(self, expr);
++        }
++    }
++}
index 51d7def137e409eea10816378e774726fb29e026,0000000000000000000000000000000000000000..f90ed7397e18e19c4d1d9899521684d2d69a0577
mode 100644,000000..100644
--- /dev/null
@@@ -1,248 -1,0 +1,248 @@@
-             let mut applicability = Applicability::MachineApplicable;
 +use super::NEEDLESS_COLLECT;
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 +use clippy_utils::source::{snippet, snippet_with_applicability};
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{is_trait_method, path_to_local_id};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
 +use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, StmtKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::map::Map;
 +use rustc_span::sym;
 +use rustc_span::{MultiSpan, Span};
 +
 +const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
 +
 +pub(super) fn check<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
 +    check_needless_collect_direct_usage(expr, cx);
 +    check_needless_collect_indirect_usage(expr, cx);
 +}
 +fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
 +    if_chain! {
 +        if let ExprKind::MethodCall(method, _, args, _) = expr.kind;
 +        if let ExprKind::MethodCall(chain_method, method0_span, _, _) = args[0].kind;
 +        if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator);
 +        then {
 +            let ty = cx.typeck_results().expr_ty(&args[0]);
-                                 Applicability::MachineApplicable,// MaybeIncorrect,
++            let mut applicability = Applicability::MaybeIncorrect;
 +            let is_empty_sugg = "next().is_none()".to_string();
 +            let method_name = &*method.ident.name.as_str();
 +            let sugg = if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
 +                        is_type_diagnostic_item(cx, ty, sym::vecdeque_type) ||
 +                        is_type_diagnostic_item(cx, ty, sym::LinkedList) ||
 +                        is_type_diagnostic_item(cx, ty, sym::BinaryHeap) {
 +                match method_name {
 +                    "len" => "count()".to_string(),
 +                    "is_empty" => is_empty_sugg,
 +                    "contains" => {
 +                        let contains_arg = snippet_with_applicability(cx, args[1].span, "??", &mut applicability);
 +                        let (arg, pred) = contains_arg
 +                            .strip_prefix('&')
 +                            .map_or(("&x", &*contains_arg), |s| ("x", s));
 +                        format!("any(|{}| x == {})", arg, pred)
 +                    }
 +                    _ => return,
 +                }
 +            }
 +            else if is_type_diagnostic_item(cx, ty, sym::BTreeMap) ||
 +                is_type_diagnostic_item(cx, ty, sym::hashmap_type) {
 +                match method_name {
 +                    "is_empty" => is_empty_sugg,
 +                    _ => return,
 +                }
 +            }
 +            else {
 +                return;
 +            };
 +            span_lint_and_sugg(
 +                cx,
 +                NEEDLESS_COLLECT,
 +                method0_span.with_hi(expr.span.hi()),
 +                NEEDLESS_COLLECT_MSG,
 +                "replace with",
 +                sugg,
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
 +    if let ExprKind::Block(block, _) = expr.kind {
 +        for stmt in block.stmts {
 +            if_chain! {
 +                if let StmtKind::Local(local) = stmt.kind;
 +                if let PatKind::Binding(_, id, ..) = local.pat.kind;
 +                if let Some(init_expr) = local.init;
 +                if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind;
 +                if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator);
 +                let ty = cx.typeck_results().expr_ty(init_expr);
 +                if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
 +                    is_type_diagnostic_item(cx, ty, sym::vecdeque_type) ||
 +                    is_type_diagnostic_item(cx, ty, sym::BinaryHeap) ||
 +                    is_type_diagnostic_item(cx, ty, sym::LinkedList);
 +                if let Some(iter_calls) = detect_iter_and_into_iters(block, id);
 +                if let [iter_call] = &*iter_calls;
 +                then {
 +                    let mut used_count_visitor = UsedCountVisitor {
 +                        cx,
 +                        id,
 +                        count: 0,
 +                    };
 +                    walk_block(&mut used_count_visitor, block);
 +                    if used_count_visitor.count > 1 {
 +                        return;
 +                    }
 +
 +                    // Suggest replacing iter_call with iter_replacement, and removing stmt
 +                    let mut span = MultiSpan::from_span(collect_span);
 +                    span.push_span_label(iter_call.span, "the iterator could be used here instead".into());
 +                    span_lint_hir_and_then(
 +                        cx,
 +                        super::NEEDLESS_COLLECT,
 +                        init_expr.hir_id,
 +                        span,
 +                        NEEDLESS_COLLECT_MSG,
 +                        |diag| {
 +                            let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx));
 +                            diag.multipart_suggestion(
 +                                iter_call.get_suggestion_text(),
 +                                vec![
 +                                    (stmt.span, String::new()),
 +                                    (iter_call.span, iter_replacement)
 +                                ],
++                                Applicability::MaybeIncorrect,
 +                            );
 +                        },
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +struct IterFunction {
 +    func: IterFunctionKind,
 +    span: Span,
 +}
 +impl IterFunction {
 +    fn get_iter_method(&self, cx: &LateContext<'_>) -> String {
 +        match &self.func {
 +            IterFunctionKind::IntoIter => String::new(),
 +            IterFunctionKind::Len => String::from(".count()"),
 +            IterFunctionKind::IsEmpty => String::from(".next().is_none()"),
 +            IterFunctionKind::Contains(span) => {
 +                let s = snippet(cx, *span, "..");
 +                if let Some(stripped) = s.strip_prefix('&') {
 +                    format!(".any(|x| x == {})", stripped)
 +                } else {
 +                    format!(".any(|x| x == *{})", s)
 +                }
 +            },
 +        }
 +    }
 +    fn get_suggestion_text(&self) -> &'static str {
 +        match &self.func {
 +            IterFunctionKind::IntoIter => {
 +                "use the original Iterator instead of collecting it and then producing a new one"
 +            },
 +            IterFunctionKind::Len => {
 +                "take the original Iterator's count instead of collecting it and finding the length"
 +            },
 +            IterFunctionKind::IsEmpty => {
 +                "check if the original Iterator has anything instead of collecting it and seeing if it's empty"
 +            },
 +            IterFunctionKind::Contains(_) => {
 +                "check if the original Iterator contains an element instead of collecting then checking"
 +            },
 +        }
 +    }
 +}
 +enum IterFunctionKind {
 +    IntoIter,
 +    Len,
 +    IsEmpty,
 +    Contains(Span),
 +}
 +
 +struct IterFunctionVisitor {
 +    uses: Vec<IterFunction>,
 +    seen_other: bool,
 +    target: HirId,
 +}
 +impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 +        // Check function calls on our collection
 +        if let ExprKind::MethodCall(method_name, _, [recv, args @ ..], _) = &expr.kind {
 +            if path_to_local_id(recv, self.target) {
 +                match &*method_name.ident.name.as_str() {
 +                    "into_iter" => self.uses.push(IterFunction {
 +                        func: IterFunctionKind::IntoIter,
 +                        span: expr.span,
 +                    }),
 +                    "len" => self.uses.push(IterFunction {
 +                        func: IterFunctionKind::Len,
 +                        span: expr.span,
 +                    }),
 +                    "is_empty" => self.uses.push(IterFunction {
 +                        func: IterFunctionKind::IsEmpty,
 +                        span: expr.span,
 +                    }),
 +                    "contains" => self.uses.push(IterFunction {
 +                        func: IterFunctionKind::Contains(args[0].span),
 +                        span: expr.span,
 +                    }),
 +                    _ => self.seen_other = true,
 +                }
 +                return;
 +            }
 +        }
 +        // Check if the collection is used for anything else
 +        if path_to_local_id(expr, self.target) {
 +            self.seen_other = true;
 +        } else {
 +            walk_expr(self, expr);
 +        }
 +    }
 +
 +    type Map = Map<'tcx>;
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
 +struct UsedCountVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    id: HirId,
 +    count: usize,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if path_to_local_id(expr, self.id) {
 +            self.count += 1;
 +        } else {
 +            walk_expr(self, expr);
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
 +    }
 +}
 +
 +/// Detect the occurrences of calls to `iter` or `into_iter` for the
 +/// given identifier
 +fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, id: HirId) -> Option<Vec<IterFunction>> {
 +    let mut visitor = IterFunctionVisitor {
 +        uses: Vec::new(),
 +        target: id,
 +        seen_other: false,
 +    };
 +    visitor.visit_block(block);
 +    if visitor.seen_other { None } else { Some(visitor.uses) }
 +}
index 3f77e7af927ad67c79dcf3ec92dda54335a048b9,0000000000000000000000000000000000000000..e8f3550283a49c85e1411ed9106619dda5e1d8d8
mode 100644,000000..100644
--- /dev/null
@@@ -1,391 -1,0 +1,382 @@@
- use clippy_utils::visitors::LocalUsedVisitor;
- use clippy_utils::{
-     contains_name, higher, is_integer_const, match_trait_method, path_to_local_id, paths, sugg, SpanlessEq,
- };
 +use super::NEEDLESS_RANGE_LOOP;
 +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::has_iter_method;
-             let index_used_directly = path_to_local_id(idx, self.var);
-             let indexed_indirectly = {
-                 let mut used_visitor = LocalUsedVisitor::new(self.cx, self.var);
-                 walk_expr(&mut used_visitor, idx);
-                 used_visitor.used
-             };
-             if indexed_indirectly || index_used_directly;
++use clippy_utils::visitors::is_local_used;
++use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq};
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, QPath};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::middle::region;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_span::symbol::{sym, Symbol};
 +use std::iter::{self, Iterator};
 +use std::mem;
 +
 +/// Checks for looping over a range and then indexing a sequence with it.
 +/// The iteratee must be a range literal.
 +#[allow(clippy::too_many_lines)]
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    pat: &'tcx Pat<'_>,
 +    arg: &'tcx Expr<'_>,
 +    body: &'tcx Expr<'_>,
 +    expr: &'tcx Expr<'_>,
 +) {
 +    if let Some(higher::Range {
 +        start: Some(start),
 +        ref end,
 +        limits,
 +    }) = higher::Range::hir(arg)
 +    {
 +        // the var must be a single name
 +        if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind {
 +            let mut visitor = VarVisitor {
 +                cx,
 +                var: canonical_id,
 +                indexed_mut: FxHashSet::default(),
 +                indexed_indirectly: FxHashMap::default(),
 +                indexed_directly: FxHashMap::default(),
 +                referenced: FxHashSet::default(),
 +                nonindex: false,
 +                prefer_mutable: false,
 +            };
 +            walk_expr(&mut visitor, body);
 +
 +            // linting condition: we only indexed one variable, and indexed it directly
 +            if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 {
 +                let (indexed, (indexed_extent, indexed_ty)) = visitor
 +                    .indexed_directly
 +                    .into_iter()
 +                    .next()
 +                    .expect("already checked that we have exactly 1 element");
 +
 +                // ensure that the indexed variable was declared before the loop, see #601
 +                if let Some(indexed_extent) = indexed_extent {
 +                    let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
 +                    let parent_def_id = cx.tcx.hir().local_def_id(parent_id);
 +                    let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id);
 +                    let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id);
 +                    if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) {
 +                        return;
 +                    }
 +                }
 +
 +                // don't lint if the container that is indexed does not have .iter() method
 +                let has_iter = has_iter_method(cx, indexed_ty);
 +                if has_iter.is_none() {
 +                    return;
 +                }
 +
 +                // don't lint if the container that is indexed into is also used without
 +                // indexing
 +                if visitor.referenced.contains(&indexed) {
 +                    return;
 +                }
 +
 +                let starts_at_zero = is_integer_const(cx, start, 0);
 +
 +                let skip = if starts_at_zero {
 +                    String::new()
 +                } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) {
 +                    return;
 +                } else {
 +                    format!(".skip({})", snippet(cx, start.span, ".."))
 +                };
 +
 +                let mut end_is_start_plus_val = false;
 +
 +                let take = if let Some(end) = *end {
 +                    let mut take_expr = end;
 +
 +                    if let ExprKind::Binary(ref op, left, right) = end.kind {
 +                        if let BinOpKind::Add = op.node {
 +                            let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left);
 +                            let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right);
 +
 +                            if start_equal_left {
 +                                take_expr = right;
 +                            } else if start_equal_right {
 +                                take_expr = left;
 +                            }
 +
 +                            end_is_start_plus_val = start_equal_left | start_equal_right;
 +                        }
 +                    }
 +
 +                    if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) {
 +                        String::new()
 +                    } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) {
 +                        return;
 +                    } else {
 +                        match limits {
 +                            ast::RangeLimits::Closed => {
 +                                let take_expr = sugg::Sugg::hir(cx, take_expr, "<count>");
 +                                format!(".take({})", take_expr + sugg::ONE)
 +                            },
 +                            ast::RangeLimits::HalfOpen => format!(".take({})", snippet(cx, take_expr.span, "..")),
 +                        }
 +                    }
 +                } else {
 +                    String::new()
 +                };
 +
 +                let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) {
 +                    ("mut ", "iter_mut")
 +                } else {
 +                    ("", "iter")
 +                };
 +
 +                let take_is_empty = take.is_empty();
 +                let mut method_1 = take;
 +                let mut method_2 = skip;
 +
 +                if end_is_start_plus_val {
 +                    mem::swap(&mut method_1, &mut method_2);
 +                }
 +
 +                if visitor.nonindex {
 +                    span_lint_and_then(
 +                        cx,
 +                        NEEDLESS_RANGE_LOOP,
 +                        expr.span,
 +                        &format!("the loop variable `{}` is used to index `{}`", ident.name, indexed),
 +                        |diag| {
 +                            multispan_sugg(
 +                                diag,
 +                                "consider using an iterator",
 +                                vec![
 +                                    (pat.span, format!("({}, <item>)", ident.name)),
 +                                    (
 +                                        arg.span,
 +                                        format!("{}.{}().enumerate(){}{}", indexed, method, method_1, method_2),
 +                                    ),
 +                                ],
 +                            );
 +                        },
 +                    );
 +                } else {
 +                    let repl = if starts_at_zero && take_is_empty {
 +                        format!("&{}{}", ref_mut, indexed)
 +                    } else {
 +                        format!("{}.{}(){}{}", indexed, method, method_1, method_2)
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        NEEDLESS_RANGE_LOOP,
 +                        expr.span,
 +                        &format!("the loop variable `{}` is only used to index `{}`", ident.name, indexed),
 +                        |diag| {
 +                            multispan_sugg(
 +                                diag,
 +                                "consider using an iterator",
 +                                vec![(pat.span, "<item>".to_string()), (arg.span, repl)],
 +                            );
 +                        },
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool {
 +    if_chain! {
 +        if let ExprKind::MethodCall(method, _, len_args, _) = expr.kind;
 +        if len_args.len() == 1;
 +        if method.ident.name == sym::len;
 +        if let ExprKind::Path(QPath::Resolved(_, path)) = len_args[0].kind;
 +        if path.segments.len() == 1;
 +        if path.segments[0].ident.name == var;
 +        then {
 +            return true;
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn is_end_eq_array_len<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    end: &Expr<'_>,
 +    limits: ast::RangeLimits,
 +    indexed_ty: Ty<'tcx>,
 +) -> bool {
 +    if_chain! {
 +        if let ExprKind::Lit(ref lit) = end.kind;
 +        if let ast::LitKind::Int(end_int, _) = lit.node;
 +        if let ty::Array(_, arr_len_const) = indexed_ty.kind();
 +        if let Some(arr_len) = arr_len_const.try_eval_usize(cx.tcx, cx.param_env);
 +        then {
 +            return match limits {
 +                ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(),
 +                ast::RangeLimits::HalfOpen => end_int >= arr_len.into(),
 +            };
 +        }
 +    }
 +
 +    false
 +}
 +
 +struct VarVisitor<'a, 'tcx> {
 +    /// context reference
 +    cx: &'a LateContext<'tcx>,
 +    /// var name to look for as index
 +    var: HirId,
 +    /// indexed variables that are used mutably
 +    indexed_mut: FxHashSet<Symbol>,
 +    /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global
 +    indexed_indirectly: FxHashMap<Symbol, Option<region::Scope>>,
 +    /// subset of `indexed` of vars that are indexed directly: `v[i]`
 +    /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
 +    indexed_directly: FxHashMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>,
 +    /// Any names that are used outside an index operation.
 +    /// Used to detect things like `&mut vec` used together with `vec[i]`
 +    referenced: FxHashSet<Symbol>,
 +    /// has the loop variable been used in expressions other than the index of
 +    /// an index op?
 +    nonindex: bool,
 +    /// Whether we are inside the `$` in `&mut $` or `$ = foo` or `$.bar`, where bar
 +    /// takes `&mut self`
 +    prefer_mutable: bool,
 +}
 +
 +impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
 +    fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
 +        if_chain! {
 +            // the indexed container is referenced by a name
 +            if let ExprKind::Path(ref seqpath) = seqexpr.kind;
 +            if let QPath::Resolved(None, seqvar) = *seqpath;
 +            if seqvar.segments.len() == 1;
-                         if indexed_indirectly {
-                             self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
-                         }
++            if is_local_used(self.cx, idx, self.var);
 +            then {
 +                if self.prefer_mutable {
 +                    self.indexed_mut.insert(seqvar.segments[0].ident.name);
 +                }
++                let index_used_directly = matches!(idx.kind, ExprKind::Path(_));
 +                let res = self.cx.qpath_res(seqpath, seqexpr.hir_id);
 +                match res {
 +                    Res::Local(hir_id) => {
 +                        let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id);
 +                        let parent_def_id = self.cx.tcx.hir().local_def_id(parent_id);
 +                        let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id);
-                         if indexed_indirectly {
-                             self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
-                         }
 +                        if index_used_directly {
 +                            self.indexed_directly.insert(
 +                                seqvar.segments[0].ident.name,
 +                                (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)),
 +                            );
++                        } else {
++                            self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
 +                        }
 +                        return false;  // no need to walk further *on the variable*
 +                    }
 +                    Res::Def(DefKind::Static | DefKind::Const, ..) => {
-             if let ExprKind::MethodCall(meth, _, args, _) = expr.kind;
 +                        if index_used_directly {
 +                            self.indexed_directly.insert(
 +                                seqvar.segments[0].ident.name,
 +                                (None, self.cx.typeck_results().node_type(seqexpr.hir_id)),
 +                            );
++                        } else {
++                            self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
 +                        }
 +                        return false;  // no need to walk further *on the variable*
 +                    }
 +                    _ => (),
 +                }
 +            }
 +        }
 +        true
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            // a range index op
-             if !self.check(&args[1], &args[0], expr);
++            if let ExprKind::MethodCall(meth, _, [args_0, args_1, ..], _) = &expr.kind;
 +            if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX))
 +                || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT));
++            if !self.check(args_1, args_0, expr);
 +            then { return }
 +        }
 +
 +        if_chain! {
 +            // an index op
 +            if let ExprKind::Index(seqexpr, idx) = expr.kind;
 +            if !self.check(idx, seqexpr, expr);
 +            then { return }
 +        }
 +
 +        if_chain! {
 +            // directly using a variable
 +            if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind;
 +            if let Res::Local(local_id) = path.res;
 +            then {
 +                if local_id == self.var {
 +                    self.nonindex = true;
 +                } else {
 +                    // not the correct variable, but still a variable
 +                    self.referenced.insert(path.segments[0].ident.name);
 +                }
 +            }
 +        }
 +
 +        let old = self.prefer_mutable;
 +        match expr.kind {
 +            ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => {
 +                self.prefer_mutable = true;
 +                self.visit_expr(lhs);
 +                self.prefer_mutable = false;
 +                self.visit_expr(rhs);
 +            },
 +            ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => {
 +                if mutbl == Mutability::Mut {
 +                    self.prefer_mutable = true;
 +                }
 +                self.visit_expr(expr);
 +            },
 +            ExprKind::Call(f, args) => {
 +                self.visit_expr(f);
 +                for expr in args {
 +                    let ty = self.cx.typeck_results().expr_ty_adjusted(expr);
 +                    self.prefer_mutable = false;
 +                    if let ty::Ref(_, _, mutbl) = *ty.kind() {
 +                        if mutbl == Mutability::Mut {
 +                            self.prefer_mutable = true;
 +                        }
 +                    }
 +                    self.visit_expr(expr);
 +                }
 +            },
 +            ExprKind::MethodCall(_, _, args, _) => {
 +                let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
 +                for (ty, expr) in iter::zip(self.cx.tcx.fn_sig(def_id).inputs().skip_binder(), args) {
 +                    self.prefer_mutable = false;
 +                    if let ty::Ref(_, _, mutbl) = *ty.kind() {
 +                        if mutbl == Mutability::Mut {
 +                            self.prefer_mutable = true;
 +                        }
 +                    }
 +                    self.visit_expr(expr);
 +                }
 +            },
 +            ExprKind::Closure(_, _, body_id, ..) => {
 +                let body = self.cx.tcx.hir().body(body_id);
 +                self.visit_expr(&body.value);
 +            },
 +            _ => walk_expr(self, expr),
 +        }
 +        self.prefer_mutable = old;
 +    }
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
index 2c46971d5f741358eba4646be0997efb601c0aa5,0000000000000000000000000000000000000000..41956650c9f4e7c8a000926f8aa8d7d2b0894207
mode 100644,000000..100644
--- /dev/null
@@@ -1,207 -1,0 +1,207 @@@
-     let expr = once(block.expr.as_deref());
 +use super::utils::make_iterator_snippet;
 +use super::NEVER_LOOP;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::higher::ForLoop;
 +use clippy_utils::source::snippet;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, LoopSource, Node, Pat, Stmt, StmtKind};
 +use rustc_lint::LateContext;
 +use std::iter::{once, Iterator};
 +
 +pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +    if let ExprKind::Loop(block, _, source, _) = expr.kind {
 +        match never_loop_block(block, expr.hir_id) {
 +            NeverLoopResult::AlwaysBreak => {
 +                span_lint_and_then(cx, NEVER_LOOP, expr.span, "this loop never actually loops", |diag| {
 +                    if_chain! {
 +                        if let LoopSource::ForLoop = source;
 +                        if let Some((_, Node::Expr(parent_match))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1);
 +                        if let Some(ForLoop { arg: iterator, pat, span: for_span, .. }) = ForLoop::hir(parent_match);
 +                        then {
 +                            // Suggests using an `if let` instead. This is `Unspecified` because the
 +                            // loop may (probably) contain `break` statements which would be invalid
 +                            // in an `if let`.
 +                            diag.span_suggestion_verbose(
 +                                for_span.with_hi(iterator.span.hi()),
 +                                "if you need the first element of the iterator, try writing",
 +                                for_to_if_let_sugg(cx, iterator, pat),
 +                                Applicability::Unspecified,
 +                            );
 +                        }
 +                    };
 +                });
 +            },
 +            NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (),
 +        }
 +    }
 +}
 +
 +enum NeverLoopResult {
 +    // A break/return always get triggered but not necessarily for the main loop.
 +    AlwaysBreak,
 +    // A continue may occur for the main loop.
 +    MayContinueMainLoop,
 +    Otherwise,
 +}
 +
 +#[must_use]
 +fn absorb_break(arg: &NeverLoopResult) -> NeverLoopResult {
 +    match *arg {
 +        NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise,
 +        NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop,
 +    }
 +}
 +
 +// Combine two results for parts that are called in order.
 +#[must_use]
 +fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult {
 +    match first {
 +        NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop => first,
 +        NeverLoopResult::Otherwise => second,
 +    }
 +}
 +
 +// Combine two results where both parts are called but not necessarily in order.
 +#[must_use]
 +fn combine_both(left: NeverLoopResult, right: NeverLoopResult) -> NeverLoopResult {
 +    match (left, right) {
 +        (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
 +            NeverLoopResult::MayContinueMainLoop
 +        },
 +        (NeverLoopResult::AlwaysBreak, _) | (_, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
 +        (NeverLoopResult::Otherwise, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
 +    }
 +}
 +
 +// Combine two results where only one of the part may have been executed.
 +#[must_use]
 +fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult {
 +    match (b1, b2) {
 +        (NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
 +        (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
 +            NeverLoopResult::MayContinueMainLoop
 +        },
 +        (NeverLoopResult::Otherwise, _) | (_, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
 +    }
 +}
 +
 +fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult {
 +    let stmts = block.stmts.iter().map(stmt_to_expr);
-         StmtKind::Local(local) => local.init.as_deref(),
++    let expr = once(block.expr);
 +    let mut iter = stmts.chain(expr).flatten();
 +    never_loop_expr_seq(&mut iter, main_loop_id)
 +}
 +
 +fn never_loop_expr_seq<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult {
 +    es.map(|e| never_loop_expr(e, main_loop_id))
 +        .fold(NeverLoopResult::Otherwise, combine_seq)
 +}
 +
 +fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +    match stmt.kind {
 +        StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some(e),
++        StmtKind::Local(local) => local.init,
 +        StmtKind::Item(..) => None,
 +    }
 +}
 +
 +fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
 +    match expr.kind {
 +        ExprKind::Box(e)
 +        | ExprKind::Unary(_, e)
 +        | ExprKind::Cast(e, _)
 +        | ExprKind::Type(e, _)
 +        | ExprKind::Let(_, e, _)
 +        | ExprKind::Field(e, _)
 +        | ExprKind::AddrOf(_, _, e)
 +        | ExprKind::Struct(_, _, Some(e))
 +        | ExprKind::Repeat(e, _)
 +        | ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id),
 +        ExprKind::Array(es) | ExprKind::MethodCall(_, _, es, _) | ExprKind::Tup(es) => {
 +            never_loop_expr_all(&mut es.iter(), main_loop_id)
 +        },
 +        ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id),
 +        ExprKind::Binary(_, e1, e2)
 +        | ExprKind::Assign(e1, e2, _)
 +        | ExprKind::AssignOp(_, e1, e2)
 +        | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id),
 +        ExprKind::Loop(b, _, _, _) => {
 +            // Break can come from the inner loop so remove them.
 +            absorb_break(&never_loop_block(b, main_loop_id))
 +        },
 +        ExprKind::If(e, e2, e3) => {
 +            let e1 = never_loop_expr(e, main_loop_id);
 +            let e2 = never_loop_expr(e2, main_loop_id);
 +            let e3 = e3
 +                .as_ref()
 +                .map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id));
 +            combine_seq(e1, combine_branches(e2, e3))
 +        },
 +        ExprKind::Match(e, arms, _) => {
 +            let e = never_loop_expr(e, main_loop_id);
 +            if arms.is_empty() {
 +                e
 +            } else {
 +                let arms = never_loop_expr_branch(&mut arms.iter().map(|a| &*a.body), main_loop_id);
 +                combine_seq(e, arms)
 +            }
 +        },
 +        ExprKind::Block(b, _) => never_loop_block(b, main_loop_id),
 +        ExprKind::Continue(d) => {
 +            let id = d
 +                .target_id
 +                .expect("target ID can only be missing in the presence of compilation errors");
 +            if id == main_loop_id {
 +                NeverLoopResult::MayContinueMainLoop
 +            } else {
 +                NeverLoopResult::AlwaysBreak
 +            }
 +        },
 +        ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
 +            combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
 +        }),
 +        ExprKind::InlineAsm(asm) => asm
 +            .operands
 +            .iter()
 +            .map(|(o, _)| match o {
 +                InlineAsmOperand::In { expr, .. }
 +                | InlineAsmOperand::InOut { expr, .. }
 +                | InlineAsmOperand::Sym { expr } => never_loop_expr(expr, main_loop_id),
 +                InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter(), main_loop_id),
 +                InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
 +                    never_loop_expr_all(&mut once(in_expr).chain(out_expr.iter()), main_loop_id)
 +                },
 +                InlineAsmOperand::Const { .. } => NeverLoopResult::Otherwise,
 +            })
 +            .fold(NeverLoopResult::Otherwise, combine_both),
 +        ExprKind::Struct(_, _, None)
 +        | ExprKind::Yield(_, _)
 +        | ExprKind::Closure(_, _, _, _, _)
 +        | ExprKind::LlvmInlineAsm(_)
 +        | ExprKind::Path(_)
 +        | ExprKind::ConstBlock(_)
 +        | ExprKind::Lit(_)
 +        | ExprKind::Err => NeverLoopResult::Otherwise,
 +    }
 +}
 +
 +fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult {
 +    es.map(|e| never_loop_expr(e, main_loop_id))
 +        .fold(NeverLoopResult::Otherwise, combine_both)
 +}
 +
 +fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(e: &mut T, main_loop_id: HirId) -> NeverLoopResult {
 +    e.map(|e| never_loop_expr(e, main_loop_id))
 +        .fold(NeverLoopResult::AlwaysBreak, combine_branches)
 +}
 +
 +fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) -> String {
 +    let pat_snippet = snippet(cx, pat.span, "_");
 +    let iter_snippet = make_iterator_snippet(cx, iterator, &mut Applicability::Unspecified);
 +
 +    format!(
 +        "if let Some({pat}) = {iter}.next()",
 +        pat = pat_snippet,
 +        iter = iter_snippet
 +    )
 +}
index d6d3315e0a83ed8c2ec69afb27dc6feb5826e45e,0000000000000000000000000000000000000000..1848f5b5de2f2fca48c3f2c1d1d2ce00ce6a9d3a
mode 100644,000000..100644
--- /dev/null
@@@ -1,102 -1,0 +1,102 @@@
-         if let ExprKind::Match(ref matchexpr, ref arms, MatchSource::Normal) = inner.kind {
 +use super::WHILE_LET_LOOP;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::source::snippet_with_applicability;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Block, Expr, ExprKind, MatchSource, Pat, StmtKind};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +
 +pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
 +    // extract the expression from the first statement (if any) in a block
 +    let inner_stmt_expr = extract_expr_from_first_stmt(loop_block);
 +    // or extract the first expression (if any) from the block
 +    if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(loop_block)) {
 +        if let Some(higher::IfLet {
 +            let_pat,
 +            let_expr,
 +            if_else: Some(if_else),
 +            ..
 +        }) = higher::IfLet::hir(cx, inner)
 +        {
 +            if is_simple_break_expr(if_else) {
 +                could_be_while_let(cx, expr, let_pat, let_expr);
 +            }
 +        }
 +
-                 && is_simple_break_expr(&arms[1].body)
++        if let ExprKind::Match(matchexpr, arms, MatchSource::Normal) = inner.kind {
 +            if arms.len() == 2
 +                && arms[0].guard.is_none()
 +                && arms[1].guard.is_none()
-                 could_be_while_let(cx, expr, &arms[0].pat, matchexpr);
++                && is_simple_break_expr(arms[1].body)
 +            {
++                could_be_while_let(cx, expr, arms[0].pat, matchexpr);
 +            }
 +        }
 +    }
 +}
 +
 +/// If a block begins with a statement (possibly a `let` binding) and has an
 +/// expression, return it.
 +fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +    if let Some(first_stmt) = block.stmts.get(0) {
 +        if let StmtKind::Local(local) = first_stmt.kind {
 +            return local.init;
 +        }
 +    }
 +    None
 +}
 +
 +/// If a block begins with an expression (with or without semicolon), return it.
 +fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +    match block.expr {
 +        Some(expr) if block.stmts.is_empty() => Some(expr),
 +        None if !block.stmts.is_empty() => match block.stmts[0].kind {
 +            StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some(expr),
 +            StmtKind::Local(..) | StmtKind::Item(..) => None,
 +        },
 +        _ => None,
 +    }
 +}
 +
 +/// Returns `true` if expr contains a single break expr without destination label
 +/// and
 +/// passed expression. The expression may be within a block.
 +fn is_simple_break_expr(expr: &Expr<'_>) -> bool {
 +    match expr.kind {
 +        ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true,
 +        ExprKind::Block(b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)),
 +        _ => false,
 +    }
 +}
 +
 +fn could_be_while_let<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    let_pat: &'tcx Pat<'_>,
 +    let_expr: &'tcx Expr<'_>,
 +) {
 +    if in_external_macro(cx.sess(), expr.span) {
 +        return;
 +    }
 +
 +    // NOTE: we used to build a body here instead of using
 +    // ellipsis, this was removed because:
 +    // 1) it was ugly with big bodies;
 +    // 2) it was not indented properly;
 +    // 3) it wasn’t very smart (see #675).
 +    let mut applicability = Applicability::HasPlaceholders;
 +    span_lint_and_sugg(
 +        cx,
 +        WHILE_LET_LOOP,
 +        expr.span,
 +        "this loop could be written as a `while let` loop",
 +        "try",
 +        format!(
 +            "while let {} = {} {{ .. }}",
 +            snippet_with_applicability(cx, let_pat.span, "..", &mut applicability),
 +            snippet_with_applicability(cx, let_expr.span, "..", &mut applicability),
 +        ),
 +        applicability,
 +    );
 +}
index 0757d329125cb12a1680ad90feef70e29c64e9fb,0000000000000000000000000000000000000000..79527e3bfa92f1b4b790d88319de759db0d8de5d
mode 100644,000000..100644
--- /dev/null
@@@ -1,366 -1,0 +1,361 @@@
-         if let Some(higher::WhileLet {
-             if_then,
-             let_pat,
-             let_expr,
-             ..
-         }) = higher::WhileLet::hir(expr);
 +use super::WHILE_LET_ON_ITERATOR;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{
 +    get_enclosing_loop_or_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, ErasedMap, NestedVisitorMap, Visitor};
 +use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
 +use rustc_lint::LateContext;
 +use rustc_span::{symbol::sym, Span, Symbol};
 +
 +pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +    let (scrutinee_expr, 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, 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 ref_mut = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) {
 +        if cx.typeck_results().node_type(iter_expr.hir_id).ref_mutability() == Some(Mutability::Mut) {
 +            // Reborrow for mutable references. It may not be possible to get a mutable reference here.
 +            "&mut *"
 +        } else {
 +            "&mut "
 +        }
 +    } 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, ref_mut, iterator),
 +        applicability,
 +    );
 +}
 +
 +#[derive(Debug)]
 +struct IterExpr {
 +    /// The span of the whole expression, not just the path and fields stored here.
 +    span: Span,
 +    /// The HIR id of the whole expression, not just the path and fields stored here.
 +    hir_id: HirId,
 +    /// The fields used, in order of child to parent.
 +    fields: Vec<Symbol>,
 +    /// The path being used.
 +    path: Res,
 +}
 +
 +/// 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 span = e.span;
 +    let hir_id = e.hir_id;
 +    let mut fields = Vec::new();
 +    loop {
 +        match e.kind {
 +            ExprKind::Path(ref path) => {
 +                break Some(IterExpr {
 +                    span,
 +                    hir_id,
 +                    fields,
 +                    path: cx.qpath_res(path, e.hir_id),
 +                });
 +            },
 +            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() => {
 +                fields.clear();
 +                e = base;
 +            },
 +            ExprKind::Unary(UnOp::Deref, base) => {
 +                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(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(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 Visitor<'tcx> for V<'_, '_, 'tcx> {
 +        type Map = ErasedMap<'tcx>;
 +        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +            NestedVisitorMap::None
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.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(_, _, 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
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: &'tcx 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 Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> {
 +        type Map = ErasedMap<'tcx>;
 +        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +            NestedVisitorMap::None
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.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(_, _, 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 Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
 +        type Map = ErasedMap<'tcx>;
 +
 +        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +            NestedVisitorMap::None
 +        }
 +
 +        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(_, _, 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_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.
 +        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 39f7ade3f81f6cbbf8c824e9c58f9820b1d40d64,0000000000000000000000000000000000000000..7627e0fb28956f3a61a0c8f513d03dad7c29033b
mode 100644,000000..100644
--- /dev/null
@@@ -1,235 -1,0 +1,234 @@@
-         let mut path = sm.filename_for_diagnostics(&sm.span_to_filename(callee))
-             .to_string();
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::in_macro;
 +use clippy_utils::source::snippet;
 +use hir::def::{DefKind, Res};
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{edition::Edition, sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `#[macro_use] use...`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Since the Rust 2018 edition you can import
 +    /// macro's directly, this is considered idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// #[macro_use]
 +    /// use some_macro;
 +    /// ```
 +    pub MACRO_USE_IMPORTS,
 +    pedantic,
 +    "#[macro_use] is no longer needed"
 +}
 +
 +const BRACKETS: &[char] = &['<', '>'];
 +
 +#[derive(Clone, Debug, PartialEq, Eq)]
 +struct PathAndSpan {
 +    path: String,
 +    span: Span,
 +}
 +
 +/// `MacroRefData` includes the name of the macro
 +/// and the path from `SourceMap::span_to_filename`.
 +#[derive(Debug, Clone)]
 +pub struct MacroRefData {
 +    name: String,
 +    path: String,
 +}
 +
 +impl MacroRefData {
 +    pub fn new(name: String, callee: Span, cx: &LateContext<'_>) -> Self {
 +        let sm = cx.sess().source_map();
++        let mut path = sm.filename_for_diagnostics(&sm.span_to_filename(callee)).to_string();
 +
 +        // std lib paths are <::std::module::file type>
 +        // so remove brackets, space and type.
 +        if path.contains('<') {
 +            path = path.replace(BRACKETS, "");
 +        }
 +        if path.contains(' ') {
 +            path = path.split(' ').next().unwrap().to_string();
 +        }
 +        Self { name, path }
 +    }
 +}
 +
 +#[derive(Default)]
 +#[allow(clippy::module_name_repetitions)]
 +pub struct MacroUseImports {
 +    /// the actual import path used and the span of the attribute above it.
 +    imports: Vec<(String, Span)>,
 +    /// the span of the macro reference, kept to ensure only one reference is used per macro call.
 +    collected: FxHashSet<Span>,
 +    mac_refs: Vec<MacroRefData>,
 +}
 +
 +impl_lint_pass!(MacroUseImports => [MACRO_USE_IMPORTS]);
 +
 +impl MacroUseImports {
 +    fn push_unique_macro(&mut self, cx: &LateContext<'_>, span: Span) {
 +        let call_site = span.source_callsite();
 +        let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
 +        if let Some(callee) = span.source_callee() {
 +            if !self.collected.contains(&call_site) {
 +                let name = if name.contains("::") {
 +                    name.split("::").last().unwrap().to_string()
 +                } else {
 +                    name.to_string()
 +                };
 +
 +                self.mac_refs.push(MacroRefData::new(name, callee.def_site, cx));
 +                self.collected.insert(call_site);
 +            }
 +        }
 +    }
 +
 +    fn push_unique_macro_pat_ty(&mut self, cx: &LateContext<'_>, span: Span) {
 +        let call_site = span.source_callsite();
 +        let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
 +        if let Some(callee) = span.source_callee() {
 +            if !self.collected.contains(&call_site) {
 +                self.mac_refs
 +                    .push(MacroRefData::new(name.to_string(), callee.def_site, cx));
 +                self.collected.insert(call_site);
 +            }
 +        }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
 +    fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
 +        if_chain! {
 +            if cx.sess().opts.edition >= Edition::Edition2018;
 +            if let hir::ItemKind::Use(path, _kind) = &item.kind;
 +            let attrs = cx.tcx.hir().attrs(item.hir_id());
 +            if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use));
 +            if let Res::Def(DefKind::Mod, id) = path.res;
 +            if !id.is_local();
 +            then {
 +                for kid in cx.tcx.item_children(id).iter() {
 +                    if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
 +                        let span = mac_attr.span;
 +                        let def_path = cx.tcx.def_path_str(mac_id);
 +                        self.imports.push((def_path, span));
 +                    }
 +                }
 +            } else {
 +                if in_macro(item.span) {
 +                    self.push_unique_macro_pat_ty(cx, item.span);
 +                }
 +            }
 +        }
 +    }
 +    fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
 +        if in_macro(attr.span) {
 +            self.push_unique_macro(cx, attr.span);
 +        }
 +    }
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
 +        if in_macro(expr.span) {
 +            self.push_unique_macro(cx, expr.span);
 +        }
 +    }
 +    fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) {
 +        if in_macro(stmt.span) {
 +            self.push_unique_macro(cx, stmt.span);
 +        }
 +    }
 +    fn check_pat(&mut self, cx: &LateContext<'_>, pat: &hir::Pat<'_>) {
 +        if in_macro(pat.span) {
 +            self.push_unique_macro_pat_ty(cx, pat.span);
 +        }
 +    }
 +    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &hir::Ty<'_>) {
 +        if in_macro(ty.span) {
 +            self.push_unique_macro_pat_ty(cx, ty.span);
 +        }
 +    }
 +    #[allow(clippy::too_many_lines)]
 +    fn check_crate_post(&mut self, cx: &LateContext<'_>, _krate: &hir::Crate<'_>) {
 +        let mut used = FxHashMap::default();
 +        let mut check_dup = vec![];
 +        for (import, span) in &self.imports {
 +            let found_idx = self.mac_refs.iter().position(|mac| import.ends_with(&mac.name));
 +
 +            if let Some(idx) = found_idx {
 +                self.mac_refs.remove(idx);
 +                let seg = import.split("::").collect::<Vec<_>>();
 +
 +                match seg.as_slice() {
 +                    // an empty path is impossible
 +                    // a path should always consist of 2 or more segments
 +                    [] | [_] => return,
 +                    [root, item] => {
 +                        if !check_dup.contains(&(*item).to_string()) {
 +                            used.entry(((*root).to_string(), span))
 +                                .or_insert_with(Vec::new)
 +                                .push((*item).to_string());
 +                            check_dup.push((*item).to_string());
 +                        }
 +                    },
 +                    [root, rest @ ..] => {
 +                        if rest.iter().all(|item| !check_dup.contains(&(*item).to_string())) {
 +                            let filtered = rest
 +                                .iter()
 +                                .filter_map(|item| {
 +                                    if check_dup.contains(&(*item).to_string()) {
 +                                        None
 +                                    } else {
 +                                        Some((*item).to_string())
 +                                    }
 +                                })
 +                                .collect::<Vec<_>>();
 +                            used.entry(((*root).to_string(), span))
 +                                .or_insert_with(Vec::new)
 +                                .push(filtered.join("::"));
 +                            check_dup.extend(filtered);
 +                        } else {
 +                            let rest = rest.to_vec();
 +                            used.entry(((*root).to_string(), span))
 +                                .or_insert_with(Vec::new)
 +                                .push(rest.join("::"));
 +                            check_dup.extend(rest.iter().map(ToString::to_string));
 +                        }
 +                    },
 +                }
 +            }
 +        }
 +
 +        let mut suggestions = vec![];
 +        for ((root, span), path) in used {
 +            if path.len() == 1 {
 +                suggestions.push((span, format!("{}::{}", root, path[0])));
 +            } else {
 +                suggestions.push((span, format!("{}::{{{}}}", root, path.join(", "))));
 +            }
 +        }
 +
 +        // If mac_refs is not empty we have encountered an import we could not handle
 +        // such as `std::prelude::v1::foo` or some other macro that expands to an import.
 +        if self.mac_refs.is_empty() {
 +            for (span, import) in suggestions {
 +                let help = format!("use {};", import);
 +                span_lint_and_sugg(
 +                    cx,
 +                    MACRO_USE_IMPORTS,
 +                    *span,
 +                    "`macro_use` attributes are no longer needed in the Rust 2018 edition",
 +                    "remove the attribute and import the macro directly, try",
 +                    help,
 +                    Applicability::MaybeIncorrect,
 +                );
 +            }
 +        }
 +    }
 +}
index 161d884149075012d3cb7a300056910de428096e,0000000000000000000000000000000000000000..b5f573cb104e914a6cde3eab296f7da9b7842b0c
mode 100644,000000..100644
--- /dev/null
@@@ -1,299 -1,0 +1,295 @@@
- use clippy_utils::higher;
 +use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
-     peel_hir_expr_refs,
++use clippy_utils::higher::IfLetOrMatch;
 +use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 +use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
 +use clippy_utils::{
 +    can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id,
- use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Pat, PatKind};
++    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};
-         if let Some(higher::IfLet {
-             let_pat,
-             let_expr,
-             if_then,
-             if_else: Some(if_else),
-         }) = higher::IfLet::hir(cx, expr)
-         {
-             manage_lint(cx, expr, (&let_pat.kind, if_then), (&PatKind::Wild, if_else), let_expr);
++use rustc_hir::{
++    def::Res, Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath,
++};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{sym, SyntaxContext};
 +
 +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);
 +    /// ```
 +    pub MANUAL_MAP,
 +    style,
 +    "reimplementation of `map`"
 +}
 +
 +declare_lint_pass!(ManualMap => [MANUAL_MAP]);
 +
 +impl LateLintPass<'_> for ManualMap {
 +    #[allow(clippy::too_many_lines)]
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-         if let ExprKind::Match(scrutinee, [then @ Arm { guard: None, .. }, r#else @ Arm { guard: None, .. }], _) =
-             expr.kind
++        let (scrutinee, then_pat, then_body, else_pat, else_body) = match IfLetOrMatch::parse(cx, expr) {
++            Some(IfLetOrMatch::IfLet(scrutinee, pat, body, Some(r#else))) => (scrutinee, pat, body, None, r#else),
++            Some(IfLetOrMatch::Match(
++                scrutinee,
++                [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }],
++                _,
++            )) => (scrutinee, arm1.pat, arm1.body, Some(arm2.pat), arm2.body),
++            _ => return,
++        };
++        if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) {
++            return;
 +        }
 +
-             manage_lint(
-                 cx,
-                 expr,
-                 (&then.pat.kind, then.body),
-                 (&r#else.pat.kind, r#else.body),
-                 scrutinee,
-             );
++        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_type)
++            && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type))
 +        {
-     }
- }
- fn manage_lint<'tcx>(
-     cx: &LateContext<'tcx>,
-     expr: &'tcx Expr<'_>,
-     then: (&'tcx PatKind<'_>, &'tcx Expr<'_>),
-     r#else: (&'tcx PatKind<'_>, &'tcx Expr<'_>),
-     scrut: &'tcx Expr<'_>,
- ) {
-     if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) {
-         return;
-     }
++            return;
 +        }
-     let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrut));
-     if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type)
-         && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type))
-     {
-         return;
-     }
 +
-     let (then_pat, then_expr) = then;
-     let (else_pat, else_expr) = r#else;
++        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,
++        };
 +
-     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),
-         try_parse_pattern(cx, else_pat, expr_ctxt),
-     ) {
-         (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_expr) => {
-             (else_expr, pattern, ref_count, true)
-         },
-         (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_expr) => {
-             (else_expr, pattern, ref_count, false)
-         },
-         (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_expr) => {
-             (then_expr, pattern, ref_count, true)
-         },
-         (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_expr) => {
-             (then_expr, pattern, ref_count, false)
-         },
-         _ => return,
-     };
++        // Top level or patterns aren't allowed in closures.
++        if matches!(some_pat.kind, PatKind::Or(_)) {
++            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, expr_ctxt) {
++            Some(expr) => expr,
++            None => return,
++        };
 +
-     let some_expr = match get_some_expr(cx, some_expr, 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) == cx.tcx.types.unit
++            && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
++        {
++            return;
++        }
 +
-     if cx.typeck_results().expr_ty(some_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).is_empty() {
++            return;
++        }
 +
-     // `map` won't perform any adjustments.
-     if !cx.typeck_results().expr_adjustments(some_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(|| ty_mutability));
 +
-     if !can_move_expr_to_closure(cx, some_expr) {
-         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(|| ty_mutability));
++        let as_ref_str = match binding_ref {
++            Some(Mutability::Mut) => ".as_mut()",
++            Some(Mutability::Not) => ".as_ref()",
++            None => "",
++        };
 +
-     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) {
++            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;
++        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(scrut).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()
-     };
++        // 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 {
-         match can_pass_as_func(cx, id, some_expr) {
-             Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
-                 snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
-             },
-             _ => {
-                 if path_to_local_id(some_expr, id)
-                     && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id)
-                     && binding_ref.is_some()
-                 {
-                     return;
-                 }
++        let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
++            match can_pass_as_func(cx, id, some_expr) {
++                Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
++                    snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
++                },
++                _ => {
++                    if path_to_local_id(some_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 {
-                     ""
-                 };
-                 format!(
-                     "|{}{}| {}",
-                     annotation,
-                     some_binding,
-                     snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
-                 )
++                    // `ref` and `ref mut` annotations were handled earlier.
++                    let annotation = if matches!(annotation, BindingAnnotation::Mutable) {
++                        "mut "
++                    } else {
++                        ""
++                    };
++                    format!(
++                        "|{}{}| {}",
++                        annotation,
++                        some_binding,
++                        snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
++                    )
++                },
++            }
++        } else if !is_wild_none && explicit_ref.is_none() {
++            // TODO: handle explicit reference annotations.
++            format!(
++                "|{}| {}",
++                snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0,
++                snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
++            )
++        } else {
++            // Refutable bindings and mixed reference annotations can't be handled by `map`.
++            return;
++        };
 +
-         }
-     } else if !is_wild_none && explicit_ref.is_none() {
-         // TODO: handle explicit reference annotations.
-         format!(
-             "|{}| {}",
-             snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0,
-             snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
-         )
-     } 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 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,
-     );
++        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)
 +            },
-             if path_to_local_id (arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
++            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(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    match expr.kind {
 +        ExprKind::Call(func, [arg])
- fn try_parse_pattern(
-     cx: &LateContext<'tcx>,
-     pat_kind: &'tcx PatKind<'_>,
-     ctxt: SyntaxContext,
- ) -> Option<OptionPat<'tcx>> {
-     fn f(
-         cx: &LateContext<'tcx>,
-         pat_kind: &'tcx PatKind<'_>,
-         ref_count: usize,
-         ctxt: SyntaxContext,
-     ) -> Option<OptionPat<'tcx>> {
-         match pat_kind {
++            if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
 +        {
 +            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,
 +    },
 +}
 +
 +// Try to parse into a recognized `Option` pattern.
 +// i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
-             PatKind::Ref(ref_pat, _) => f(cx, &ref_pat.kind, ref_count + 1, ctxt),
++fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
++    fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
++        match pat.kind {
 +            PatKind::Wild => Some(OptionPat::Wild),
-             PatKind::TupleStruct(ref qpath, [pattern], _) if is_lang_ctor(cx, qpath, OptionSome) => {
++            PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
 +            PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None),
-     f(cx, pat_kind, 0, ctxt)
++            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(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxContext) -> Option<&'tcx Expr<'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(arg),
 +        ExprKind::Block(
 +            Block {
 +                stmts: [],
 +                expr: Some(expr),
 +                ..
 +            },
 +            _,
 +        ) => get_some_expr(cx, expr, ctxt),
 +        _ => None,
 +    }
 +}
 +
 +// Checks for the `None` value.
 +fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
 +    match expr.kind {
 +        ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
 +        ExprKind::Block(
 +            Block {
 +                stmts: [],
 +                expr: Some(expr),
 +                ..
 +            },
 +            _,
 +        ) => is_none_expr(cx, expr),
 +        _ => false,
 +    }
 +}
index 3d0da472ddcbff8f2fe61560c94e490d2587d7b1,0000000000000000000000000000000000000000..2f1ff567e844f803c7e4f8930aefd1a5f1187eda
mode 100644,000000..100644
--- /dev/null
@@@ -1,2376 -1,0 +1,2374 @@@
- use clippy_utils::visitors::LocalUsedVisitor;
 +use clippy_utils::consts::{constant, miri_to_const, Constant};
 +use clippy_utils::diagnostics::{
 +    multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
 +};
 +use clippy_utils::higher;
 +use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
-         if let ExprKind::Match(ref ex, ref arms, _) = expr.kind {
++use clippy_utils::visitors::is_local_used;
 +use clippy_utils::{
 +    get_parent_expr, in_macro, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild,
 +    meets_msrv, msrvs, path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns,
 +    remove_blocks, strip_pat_refs,
 +};
 +use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
 +use core::array;
 +use core::iter::{once, ExactSizeIterator};
 +use if_chain::if_chain;
 +use rustc_ast::ast::{Attribute, LitKind};
 +use rustc_errors::Applicability;
 +use rustc_hir::def::{CtorKind, DefKind, Res};
 +use rustc_hir::LangItem::{OptionNone, OptionSome};
 +use rustc_hir::{
 +    self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource,
 +    Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind,
 +};
 +use rustc_hir::{HirIdMap, HirIdSet};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{self, Ty, TyS, VariantDef};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::{Span, Spanned};
 +use rustc_span::sym;
 +use std::cmp::Ordering;
 +use std::collections::hash_map::Entry;
 +use std::iter;
 +use std::ops::Bound;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches with a single arm where an `if let`
 +    /// will usually suffice.
 +    ///
 +    /// ### Why is this bad?
 +    /// Just readability – `if let` nests less than a `match`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn bar(stool: &str) {}
 +    /// # let x = Some("abc");
 +    /// // Bad
 +    /// match x {
 +    ///     Some(ref foo) => bar(foo),
 +    ///     _ => (),
 +    /// }
 +    ///
 +    /// // Good
 +    /// if let Some(ref foo) = x {
 +    ///     bar(foo);
 +    /// }
 +    /// ```
 +    pub SINGLE_MATCH,
 +    style,
 +    "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches with two arms where an `if let else` will
 +    /// usually suffice.
 +    ///
 +    /// ### Why is this bad?
 +    /// Just readability – `if let` nests less than a `match`.
 +    ///
 +    /// ### Known problems
 +    /// Personal style preferences may differ.
 +    ///
 +    /// ### Example
 +    /// Using `match`:
 +    ///
 +    /// ```rust
 +    /// # fn bar(foo: &usize) {}
 +    /// # let other_ref: usize = 1;
 +    /// # let x: Option<&usize> = Some(&1);
 +    /// match x {
 +    ///     Some(ref foo) => bar(foo),
 +    ///     _ => bar(&other_ref),
 +    /// }
 +    /// ```
 +    ///
 +    /// Using `if let` with `else`:
 +    ///
 +    /// ```rust
 +    /// # fn bar(foo: &usize) {}
 +    /// # let other_ref: usize = 1;
 +    /// # let x: Option<&usize> = Some(&1);
 +    /// if let Some(ref foo) = x {
 +    ///     bar(foo);
 +    /// } else {
 +    ///     bar(&other_ref);
 +    /// }
 +    /// ```
 +    pub SINGLE_MATCH_ELSE,
 +    pedantic,
 +    "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches where all arms match a reference,
 +    /// suggesting to remove the reference and deref the matched expression
 +    /// instead. It also checks for `if let &foo = bar` blocks.
 +    ///
 +    /// ### Why is this bad?
 +    /// It just makes the code less readable. That reference
 +    /// destructuring adds nothing to the code.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// match x {
 +    ///     &A(ref y) => foo(y),
 +    ///     &B => bar(),
 +    ///     _ => frob(&x),
 +    /// }
 +    ///
 +    /// // Good
 +    /// match *x {
 +    ///     A(ref y) => foo(y),
 +    ///     B => bar(),
 +    ///     _ => frob(x),
 +    /// }
 +    /// ```
 +    pub MATCH_REF_PATS,
 +    style,
 +    "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches where match expression is a `bool`. It
 +    /// suggests to replace the expression with an `if...else` block.
 +    ///
 +    /// ### Why is this bad?
 +    /// It makes the code less readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn foo() {}
 +    /// # fn bar() {}
 +    /// let condition: bool = true;
 +    /// match condition {
 +    ///     true => foo(),
 +    ///     false => bar(),
 +    /// }
 +    /// ```
 +    /// Use if/else instead:
 +    /// ```rust
 +    /// # fn foo() {}
 +    /// # fn bar() {}
 +    /// let condition: bool = true;
 +    /// if condition {
 +    ///     foo();
 +    /// } else {
 +    ///     bar();
 +    /// }
 +    /// ```
 +    pub MATCH_BOOL,
 +    pedantic,
 +    "a `match` on a boolean expression instead of an `if..else` block"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for overlapping match arms.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is likely to be an error and if not, makes the code
 +    /// less obvious.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 5;
 +    /// match x {
 +    ///     1...10 => println!("1 ... 10"),
 +    ///     5...15 => println!("5 ... 15"),
 +    ///     _ => (),
 +    /// }
 +    /// ```
 +    pub MATCH_OVERLAPPING_ARM,
 +    style,
 +    "a `match` with overlapping arms"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for arm which matches all errors with `Err(_)`
 +    /// and take drastic actions like `panic!`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is generally a bad practice, similar to
 +    /// catching all exceptions in java with `catch(Exception)`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: Result<i32, &str> = Ok(3);
 +    /// match x {
 +    ///     Ok(_) => println!("ok"),
 +    ///     Err(_) => panic!("err"),
 +    /// }
 +    /// ```
 +    pub MATCH_WILD_ERR_ARM,
 +    pedantic,
 +    "a `match` with `Err(_)` arm and take drastic actions"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for match which is used to add a reference to an
 +    /// `Option` value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `as_ref()` or `as_mut()` instead is shorter.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: Option<()> = None;
 +    ///
 +    /// // Bad
 +    /// let r: Option<&()> = match x {
 +    ///     None => None,
 +    ///     Some(ref v) => Some(v),
 +    /// };
 +    ///
 +    /// // Good
 +    /// let r: Option<&()> = x.as_ref();
 +    /// ```
 +    pub MATCH_AS_REF,
 +    complexity,
 +    "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for wildcard enum matches using `_`.
 +    ///
 +    /// ### Why is this bad?
 +    /// New enum variants added by library updates can be missed.
 +    ///
 +    /// ### Known problems
 +    /// Suggested replacements may be incorrect if guards exhaustively cover some
 +    /// variants, and also may not use correct path to enum if it's not present in the current scope.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # enum Foo { A(usize), B(usize) }
 +    /// # let x = Foo::B(1);
 +    /// // Bad
 +    /// match x {
 +    ///     Foo::A(_) => {},
 +    ///     _ => {},
 +    /// }
 +    ///
 +    /// // Good
 +    /// match x {
 +    ///     Foo::A(_) => {},
 +    ///     Foo::B(_) => {},
 +    /// }
 +    /// ```
 +    pub WILDCARD_ENUM_MATCH_ARM,
 +    restriction,
 +    "a wildcard enum match arm using `_`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for wildcard enum matches for a single variant.
 +    ///
 +    /// ### Why is this bad?
 +    /// New enum variants added by library updates can be missed.
 +    ///
 +    /// ### Known problems
 +    /// Suggested replacements may not use correct path to enum
 +    /// if it's not present in the current scope.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # enum Foo { A, B, C }
 +    /// # let x = Foo::B;
 +    /// // Bad
 +    /// match x {
 +    ///     Foo::A => {},
 +    ///     Foo::B => {},
 +    ///     _ => {},
 +    /// }
 +    ///
 +    /// // Good
 +    /// match x {
 +    ///     Foo::A => {},
 +    ///     Foo::B => {},
 +    ///     Foo::C => {},
 +    /// }
 +    /// ```
 +    pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +    pedantic,
 +    "a wildcard enum match for a single variant"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for wildcard pattern used with others patterns in same match arm.
 +    ///
 +    /// ### Why is this bad?
 +    /// Wildcard pattern already covers any other pattern as it will match anyway.
 +    /// It makes the code less readable, especially to spot wildcard pattern use in match arm.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// match "foo" {
 +    ///     "a" => {},
 +    ///     "bar" | _ => {},
 +    /// }
 +    ///
 +    /// // Good
 +    /// match "foo" {
 +    ///     "a" => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    pub WILDCARD_IN_OR_PATTERNS,
 +    complexity,
 +    "a wildcard pattern used with others patterns in same match arm"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches being used to destructure a single-variant enum
 +    /// or tuple struct where a `let` will suffice.
 +    ///
 +    /// ### Why is this bad?
 +    /// Just readability – `let` doesn't nest, whereas a `match` does.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// enum Wrapper {
 +    ///     Data(i32),
 +    /// }
 +    ///
 +    /// let wrapper = Wrapper::Data(42);
 +    ///
 +    /// let data = match wrapper {
 +    ///     Wrapper::Data(i) => i,
 +    /// };
 +    /// ```
 +    ///
 +    /// The correct use would be:
 +    /// ```rust
 +    /// enum Wrapper {
 +    ///     Data(i32),
 +    /// }
 +    ///
 +    /// let wrapper = Wrapper::Data(42);
 +    /// let Wrapper::Data(data) = wrapper;
 +    /// ```
 +    pub INFALLIBLE_DESTRUCTURING_MATCH,
 +    style,
 +    "a `match` statement with a single infallible arm instead of a `let`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for useless match that binds to only one value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability and needless complexity.
 +    ///
 +    /// ### Known problems
 +    ///  Suggested replacements may be incorrect when `match`
 +    /// is actually binding temporary value, bringing a 'dropped while borrowed' error.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let a = 1;
 +    /// # let b = 2;
 +    ///
 +    /// // Bad
 +    /// match (a, b) {
 +    ///     (c, d) => {
 +    ///         // useless match
 +    ///     }
 +    /// }
 +    ///
 +    /// // Good
 +    /// let (c, d) = (a, b);
 +    /// ```
 +    pub MATCH_SINGLE_BINDING,
 +    complexity,
 +    "a match with a single binding instead of using `let` statement"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
 +    ///
 +    /// ### Why is this bad?
 +    /// Correctness and readability. It's like having a wildcard pattern after
 +    /// matching all enum variants explicitly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct A { a: i32 }
 +    /// let a = A { a: 5 };
 +    ///
 +    /// // Bad
 +    /// match a {
 +    ///     A { a: 5, .. } => {},
 +    ///     _ => {},
 +    /// }
 +    ///
 +    /// // Good
 +    /// match a {
 +    ///     A { a: 5 } => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +    restriction,
 +    "a match on a struct that binds all fields but still uses the wildcard pattern"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Lint for redundant pattern matching over `Result`, `Option`,
 +    /// `std::task::Poll` or `std::net::IpAddr`
 +    ///
 +    /// ### Why is this bad?
 +    /// It's more concise and clear to just use the proper
 +    /// utility function
 +    ///
 +    /// ### Known problems
 +    /// This will change the drop order for the matched type. Both `if let` and
 +    /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
 +    /// value before entering the block. For most types this change will not matter, but for a few
 +    /// types this will not be an acceptable change (e.g. locks). See the
 +    /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
 +    /// drop order.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::task::Poll;
 +    /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
 +    /// if let Ok(_) = Ok::<i32, i32>(42) {}
 +    /// if let Err(_) = Err::<i32, i32>(42) {}
 +    /// if let None = None::<()> {}
 +    /// if let Some(_) = Some(42) {}
 +    /// if let Poll::Pending = Poll::Pending::<()> {}
 +    /// if let Poll::Ready(_) = Poll::Ready(42) {}
 +    /// if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}
 +    /// if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}
 +    /// match Ok::<i32, i32>(42) {
 +    ///     Ok(_) => true,
 +    ///     Err(_) => false,
 +    /// };
 +    /// ```
 +    ///
 +    /// The more idiomatic use would be:
 +    ///
 +    /// ```rust
 +    /// # use std::task::Poll;
 +    /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
 +    /// if Ok::<i32, i32>(42).is_ok() {}
 +    /// if Err::<i32, i32>(42).is_err() {}
 +    /// if None::<()>.is_none() {}
 +    /// if Some(42).is_some() {}
 +    /// if Poll::Pending::<()>.is_pending() {}
 +    /// if Poll::Ready(42).is_ready() {}
 +    /// if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
 +    /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
 +    /// Ok::<i32, i32>(42).is_ok();
 +    /// ```
 +    pub REDUNDANT_PATTERN_MATCHING,
 +    style,
 +    "use the proper utility function avoiding an `if let`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `match`  or `if let` expressions producing a
 +    /// `bool` that could be written using `matches!`
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability and needless complexity.
 +    ///
 +    /// ### Known problems
 +    /// This lint falsely triggers, if there are arms with
 +    /// `cfg` attributes that remove an arm evaluating to `false`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = Some(5);
 +    ///
 +    /// // Bad
 +    /// let a = match x {
 +    ///     Some(0) => true,
 +    ///     _ => false,
 +    /// };
 +    ///
 +    /// let a = if let Some(0) = x {
 +    ///     true
 +    /// } else {
 +    ///     false
 +    /// };
 +    ///
 +    /// // Good
 +    /// let a = matches!(x, Some(0));
 +    /// ```
 +    pub MATCH_LIKE_MATCHES_MACRO,
 +    style,
 +    "a match that could be written with the matches! macro"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `match` with identical arm bodies.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a copy & paste error. If arm bodies
 +    /// are the same on purpose, you can factor them
 +    /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
 +    ///
 +    /// ### Known problems
 +    /// False positive possible with order dependent `match`
 +    /// (see issue
 +    /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// match foo {
 +    ///     Bar => bar(),
 +    ///     Quz => quz(),
 +    ///     Baz => bar(), // <= oops
 +    /// }
 +    /// ```
 +    ///
 +    /// This should probably be
 +    /// ```rust,ignore
 +    /// match foo {
 +    ///     Bar => bar(),
 +    ///     Quz => quz(),
 +    ///     Baz => baz(), // <= fixed
 +    /// }
 +    /// ```
 +    ///
 +    /// or if the original code was not a typo:
 +    /// ```rust,ignore
 +    /// match foo {
 +    ///     Bar | Baz => bar(), // <= shows the intent better
 +    ///     Quz => quz(),
 +    /// }
 +    /// ```
 +    pub MATCH_SAME_ARMS,
 +    pedantic,
 +    "`match` with identical arm bodies"
 +}
 +
 +#[derive(Default)]
 +pub struct Matches {
 +    msrv: Option<RustcVersion>,
 +    infallible_destructuring_match_linted: bool,
 +}
 +
 +impl Matches {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self {
 +            msrv,
 +            ..Matches::default()
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(Matches => [
 +    SINGLE_MATCH,
 +    MATCH_REF_PATS,
 +    MATCH_BOOL,
 +    SINGLE_MATCH_ELSE,
 +    MATCH_OVERLAPPING_ARM,
 +    MATCH_WILD_ERR_ARM,
 +    MATCH_AS_REF,
 +    WILDCARD_ENUM_MATCH_ARM,
 +    MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +    WILDCARD_IN_OR_PATTERNS,
 +    MATCH_SINGLE_BINDING,
 +    INFALLIBLE_DESTRUCTURING_MATCH,
 +    REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +    REDUNDANT_PATTERN_MATCHING,
 +    MATCH_LIKE_MATCHES_MACRO,
 +    MATCH_SAME_ARMS,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Matches {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
 +            return;
 +        }
 +
 +        redundant_pattern_match::check(cx, expr);
 +
 +        if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
 +            if !check_match_like_matches(cx, expr) {
 +                lint_match_arms(cx, expr);
 +            }
 +        } else {
 +            lint_match_arms(cx, expr);
 +        }
 +
 +        if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
 +            check_single_match(cx, ex, arms, expr);
 +            check_match_bool(cx, ex, arms, expr);
 +            check_overlapping_arms(cx, ex, arms);
 +            check_wild_err_arm(cx, ex, arms);
 +            check_wild_enum_match(cx, ex, arms);
 +            check_match_as_ref(cx, ex, arms, expr);
 +            check_wild_in_or_pats(cx, arms);
 +
 +            if self.infallible_destructuring_match_linted {
 +                self.infallible_destructuring_match_linted = false;
 +            } else {
 +                check_match_single_binding(cx, ex, arms, expr);
 +            }
 +        }
-                                 if ident.as_str().starts_with('_')
-                                     && !LocalUsedVisitor::new(cx, id).check_expr(arm.body)
-                                 {
++        if let ExprKind::Match(ex, arms, _) = expr.kind {
 +            check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr);
 +        }
 +        if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) {
 +            check_match_ref_pats(cx, let_expr, once(let_pat), expr);
 +        }
 +    }
 +
 +    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
 +        if_chain! {
 +            if !in_external_macro(cx.sess(), local.span);
 +            if !in_macro(local.span);
 +            if let Some(expr) = local.init;
 +            if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
 +            if arms.len() == 1 && arms[0].guard.is_none();
 +            if let PatKind::TupleStruct(
 +                QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
 +            if args.len() == 1;
 +            if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
 +            let body = remove_blocks(arms[0].body);
 +            if path_to_local_id(body, arg);
 +
 +            then {
 +                let mut applicability = Applicability::MachineApplicable;
 +                self.infallible_destructuring_match_linted = true;
 +                span_lint_and_sugg(
 +                    cx,
 +                    INFALLIBLE_DESTRUCTURING_MATCH,
 +                    local.span,
 +                    "you seem to be trying to use `match` to destructure a single infallible pattern. \
 +                    Consider using `let`",
 +                    "try this",
 +                    format!(
 +                        "let {}({}) = {};",
 +                        snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
 +                        snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
 +                        snippet_with_applicability(cx, target.span, "..", &mut applicability),
 +                    ),
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
 +        if_chain! {
 +            if !in_external_macro(cx.sess(), pat.span);
 +            if !in_macro(pat.span);
 +            if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind;
 +            if let Some(def_id) = path.res.opt_def_id();
 +            let ty = cx.tcx.type_of(def_id);
 +            if let ty::Adt(def, _) = ty.kind();
 +            if def.is_struct() || def.is_union();
 +            if fields.len() == def.non_enum_variant().fields.len();
 +
 +            then {
 +                span_lint_and_help(
 +                    cx,
 +                    REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +                    pat.span,
 +                    "unnecessary use of `..` pattern in struct binding. All fields were already bound",
 +                    None,
 +                    "consider removing `..` from this binding",
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +#[rustfmt::skip]
 +fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
 +    if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
 +        if in_macro(expr.span) {
 +            // Don't lint match expressions present in
 +            // macro_rules! block
 +            return;
 +        }
 +        if let PatKind::Or(..) = arms[0].pat.kind {
 +            // don't lint for or patterns for now, this makes
 +            // the lint noisy in unnecessary situations
 +            return;
 +        }
 +        let els = arms[1].body;
 +        let els = if is_unit_expr(remove_blocks(els)) {
 +            None
 +        } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
 +            if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
 +                // single statement/expr "else" block, don't lint
 +                return;
 +            }
 +            // block with 2+ statements or 1 expr and 1+ statement
 +            Some(els)
 +        } else {
 +            // not a block, don't lint
 +            return;
 +        };
 +
 +        let ty = cx.typeck_results().expr_ty(ex);
 +        if *ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) {
 +            check_single_match_single_pattern(cx, ex, arms, expr, els);
 +            check_single_match_opt_like(cx, ex, arms, expr, ty, els);
 +        }
 +    }
 +}
 +
 +fn check_single_match_single_pattern(
 +    cx: &LateContext<'_>,
 +    ex: &Expr<'_>,
 +    arms: &[Arm<'_>],
 +    expr: &Expr<'_>,
 +    els: Option<&Expr<'_>>,
 +) {
 +    if is_wild(arms[1].pat) {
 +        report_single_match_single_pattern(cx, ex, arms, expr, els);
 +    }
 +}
 +
 +fn report_single_match_single_pattern(
 +    cx: &LateContext<'_>,
 +    ex: &Expr<'_>,
 +    arms: &[Arm<'_>],
 +    expr: &Expr<'_>,
 +    els: Option<&Expr<'_>>,
 +) {
 +    let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
 +    let els_str = els.map_or(String::new(), |els| {
 +        format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
 +    });
 +
 +    let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
 +    let (msg, sugg) = if_chain! {
 +        if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
 +        let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
 +        if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait();
 +        if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait();
 +        if ty.is_integral() || ty.is_char() || ty.is_str()
 +            || (implements_trait(cx, ty, spe_trait_id, &[])
 +                && implements_trait(cx, ty, pe_trait_id, &[ty.into()]));
 +        then {
 +            // scrutinee derives PartialEq and the pattern is a constant.
 +            let pat_ref_count = match pat.kind {
 +                // string literals are already a reference.
 +                PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1,
 +                _ => pat_ref_count,
 +            };
 +            // References are only implicitly added to the pattern, so no overflow here.
 +            // e.g. will work: match &Some(_) { Some(_) => () }
 +            // will not: match Some(_) { &Some(_) => () }
 +            let ref_count_diff = ty_ref_count - pat_ref_count;
 +
 +            // Try to remove address of expressions first.
 +            let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff);
 +            let ref_count_diff = ref_count_diff - removed;
 +
 +            let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`";
 +            let sugg = format!(
 +                "if {} == {}{} {}{}",
 +                snippet(cx, ex.span, ".."),
 +                // PartialEq for different reference counts may not exist.
 +                "&".repeat(ref_count_diff),
 +                snippet(cx, arms[0].pat.span, ".."),
 +                expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
 +                els_str,
 +            );
 +            (msg, sugg)
 +        } else {
 +            let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
 +            let sugg = format!(
 +                "if let {} = {} {}{}",
 +                snippet(cx, arms[0].pat.span, ".."),
 +                snippet(cx, ex.span, ".."),
 +                expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
 +                els_str,
 +            );
 +            (msg, sugg)
 +        }
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        lint,
 +        expr.span,
 +        msg,
 +        "try this",
 +        sugg,
 +        Applicability::HasPlaceholders,
 +    );
 +}
 +
 +fn check_single_match_opt_like(
 +    cx: &LateContext<'_>,
 +    ex: &Expr<'_>,
 +    arms: &[Arm<'_>],
 +    expr: &Expr<'_>,
 +    ty: Ty<'_>,
 +    els: Option<&Expr<'_>>,
 +) {
 +    // list of candidate `Enum`s we know will never get any more members
 +    let candidates = &[
 +        (&paths::COW, "Borrowed"),
 +        (&paths::COW, "Cow::Borrowed"),
 +        (&paths::COW, "Cow::Owned"),
 +        (&paths::COW, "Owned"),
 +        (&paths::OPTION, "None"),
 +        (&paths::RESULT, "Err"),
 +        (&paths::RESULT, "Ok"),
 +    ];
 +
 +    let path = match arms[1].pat.kind {
 +        PatKind::TupleStruct(ref path, inner, _) => {
 +            // Contains any non wildcard patterns (e.g., `Err(err)`)?
 +            if !inner.iter().all(is_wild) {
 +                return;
 +            }
 +            rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
 +        },
 +        PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None) => ident.to_string(),
 +        PatKind::Path(ref path) => {
 +            rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
 +        },
 +        _ => return,
 +    };
 +
 +    for &(ty_path, pat_path) in candidates {
 +        if path == *pat_path && match_type(cx, ty, ty_path) {
 +            report_single_match_single_pattern(cx, ex, arms, expr, els);
 +        }
 +    }
 +}
 +
 +fn check_match_bool(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
 +    // Type of expression is `bool`.
 +    if *cx.typeck_results().expr_ty(ex).kind() == ty::Bool {
 +        span_lint_and_then(
 +            cx,
 +            MATCH_BOOL,
 +            expr.span,
 +            "you seem to be trying to match on a boolean expression",
 +            move |diag| {
 +                if arms.len() == 2 {
 +                    // no guards
 +                    let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind {
 +                        if let ExprKind::Lit(ref lit) = arm_bool.kind {
 +                            match lit.node {
 +                                LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
 +                                LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
 +                                _ => None,
 +                            }
 +                        } else {
 +                            None
 +                        }
 +                    } else {
 +                        None
 +                    };
 +
 +                    if let Some((true_expr, false_expr)) = exprs {
 +                        let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
 +                            (false, false) => Some(format!(
 +                                "if {} {} else {}",
 +                                snippet(cx, ex.span, "b"),
 +                                expr_block(cx, true_expr, None, "..", Some(expr.span)),
 +                                expr_block(cx, false_expr, None, "..", Some(expr.span))
 +                            )),
 +                            (false, true) => Some(format!(
 +                                "if {} {}",
 +                                snippet(cx, ex.span, "b"),
 +                                expr_block(cx, true_expr, None, "..", Some(expr.span))
 +                            )),
 +                            (true, false) => {
 +                                let test = Sugg::hir(cx, ex, "..");
 +                                Some(format!(
 +                                    "if {} {}",
 +                                    !test,
 +                                    expr_block(cx, false_expr, None, "..", Some(expr.span))
 +                                ))
 +                            },
 +                            (true, true) => None,
 +                        };
 +
 +                        if let Some(sugg) = sugg {
 +                            diag.span_suggestion(
 +                                expr.span,
 +                                "consider using an `if`/`else` expression",
 +                                sugg,
 +                                Applicability::HasPlaceholders,
 +                            );
 +                        }
 +                    }
 +                }
 +            },
 +        );
 +    }
 +}
 +
 +fn check_overlapping_arms<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
 +    if arms.len() >= 2 && cx.typeck_results().expr_ty(ex).is_integral() {
 +        let ranges = all_ranges(cx, arms, cx.typeck_results().expr_ty(ex));
 +        let type_ranges = type_ranges(&ranges);
 +        if !type_ranges.is_empty() {
 +            if let Some((start, end)) = overlapping(&type_ranges) {
 +                span_lint_and_note(
 +                    cx,
 +                    MATCH_OVERLAPPING_ARM,
 +                    start.span,
 +                    "some ranges overlap",
 +                    Some(end.span),
 +                    "overlaps with this",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) {
 +    let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
 +    if is_type_diagnostic_item(cx, ex_ty, sym::result_type) {
 +        for arm in arms {
 +            if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind {
 +                let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
 +                if path_str == "Err" {
 +                    let mut matching_wild = inner.iter().any(is_wild);
 +                    let mut ident_bind_name = String::from("_");
 +                    if !matching_wild {
 +                        // Looking for unused bindings (i.e.: `_e`)
 +                        for pat in inner.iter() {
 +                            if let PatKind::Binding(_, id, ident, None) = pat.kind {
-     if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = ex.kind {
++                                if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
 +                                    ident_bind_name = (&ident.name.as_str()).to_string();
 +                                    matching_wild = true;
 +                                }
 +                            }
 +                        }
 +                    }
 +                    if_chain! {
 +                        if matching_wild;
 +                        if let ExprKind::Block(block, _) = arm.body.kind;
 +                        if is_panic_block(block);
 +                        then {
 +                            // `Err(_)` or `Err(_e)` arm with `panic!` found
 +                            span_lint_and_note(cx,
 +                                MATCH_WILD_ERR_ARM,
 +                                arm.pat.span,
 +                                &format!("`Err({})` matches all errors", &ident_bind_name),
 +                                None,
 +                                "match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable",
 +                            );
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +enum CommonPrefixSearcher<'a> {
 +    None,
 +    Path(&'a [PathSegment<'a>]),
 +    Mixed,
 +}
 +impl CommonPrefixSearcher<'a> {
 +    fn with_path(&mut self, path: &'a [PathSegment<'a>]) {
 +        match path {
 +            [path @ .., _] => self.with_prefix(path),
 +            [] => (),
 +        }
 +    }
 +
 +    fn with_prefix(&mut self, path: &'a [PathSegment<'a>]) {
 +        match self {
 +            Self::None => *self = Self::Path(path),
 +            Self::Path(self_path)
 +                if path
 +                    .iter()
 +                    .map(|p| p.ident.name)
 +                    .eq(self_path.iter().map(|p| p.ident.name)) => {},
 +            Self::Path(_) => *self = Self::Mixed,
 +            Self::Mixed => (),
 +        }
 +    }
 +}
 +
 +fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool {
 +    let attrs = cx.tcx.get_attrs(variant_def.def_id);
 +    clippy_utils::attrs::is_doc_hidden(attrs) || clippy_utils::attrs::is_unstable(attrs)
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
 +    let ty = cx.typeck_results().expr_ty(ex).peel_refs();
 +    let adt_def = match ty.kind() {
 +        ty::Adt(adt_def, _)
 +            if adt_def.is_enum()
 +                && !(is_type_diagnostic_item(cx, ty, sym::option_type)
 +                    || is_type_diagnostic_item(cx, ty, sym::result_type)) =>
 +        {
 +            adt_def
 +        },
 +        _ => return,
 +    };
 +
 +    // First pass - check for violation, but don't do much book-keeping because this is hopefully
 +    // the uncommon case, and the book-keeping is slightly expensive.
 +    let mut wildcard_span = None;
 +    let mut wildcard_ident = None;
 +    let mut has_non_wild = false;
 +    for arm in arms {
 +        match peel_hir_pat_refs(arm.pat).0.kind {
 +            PatKind::Wild => wildcard_span = Some(arm.pat.span),
 +            PatKind::Binding(_, _, ident, None) => {
 +                wildcard_span = Some(arm.pat.span);
 +                wildcard_ident = Some(ident);
 +            },
 +            _ => has_non_wild = true,
 +        }
 +    }
 +    let wildcard_span = match wildcard_span {
 +        Some(x) if has_non_wild => x,
 +        _ => return,
 +    };
 +
 +    // Accumulate the variants which should be put in place of the wildcard because they're not
 +    // already covered.
 +    let has_hidden = adt_def.variants.iter().any(|x| is_hidden(cx, x));
 +    let mut missing_variants: Vec<_> = adt_def.variants.iter().filter(|x| !is_hidden(cx, x)).collect();
 +
 +    let mut path_prefix = CommonPrefixSearcher::None;
 +    for arm in arms {
 +        // Guards mean that this case probably isn't exhaustively covered. Technically
 +        // this is incorrect, as we should really check whether each variant is exhaustively
 +        // covered by the set of guards that cover it, but that's really hard to do.
 +        recurse_or_patterns(arm.pat, |pat| {
 +            let path = match &peel_hir_pat_refs(pat).0.kind {
 +                PatKind::Path(path) => {
 +                    #[allow(clippy::match_same_arms)]
 +                    let id = match cx.qpath_res(path, pat.hir_id) {
 +                        Res::Def(DefKind::Const | DefKind::ConstParam | DefKind::AnonConst, _) => return,
 +                        Res::Def(_, id) => id,
 +                        _ => return,
 +                    };
 +                    if arm.guard.is_none() {
 +                        missing_variants.retain(|e| e.ctor_def_id != Some(id));
 +                    }
 +                    path
 +                },
 +                PatKind::TupleStruct(path, patterns, ..) => {
 +                    if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
 +                        if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) {
 +                            missing_variants.retain(|e| e.ctor_def_id != Some(id));
 +                        }
 +                    }
 +                    path
 +                },
 +                PatKind::Struct(path, patterns, ..) => {
 +                    if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
 +                        if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) {
 +                            missing_variants.retain(|e| e.def_id != id);
 +                        }
 +                    }
 +                    path
 +                },
 +                _ => return,
 +            };
 +            match path {
 +                QPath::Resolved(_, path) => path_prefix.with_path(path.segments),
 +                QPath::TypeRelative(
 +                    hir::Ty {
 +                        kind: TyKind::Path(QPath::Resolved(_, path)),
 +                        ..
 +                    },
 +                    _,
 +                ) => path_prefix.with_prefix(path.segments),
 +                _ => (),
 +            }
 +        });
 +    }
 +
 +    let format_suggestion = |variant: &VariantDef| {
 +        format!(
 +            "{}{}{}{}",
 +            if let Some(ident) = wildcard_ident {
 +                format!("{} @ ", ident.name)
 +            } else {
 +                String::new()
 +            },
 +            if let CommonPrefixSearcher::Path(path_prefix) = path_prefix {
 +                let mut s = String::new();
 +                for seg in path_prefix {
 +                    s.push_str(&seg.ident.as_str());
 +                    s.push_str("::");
 +                }
 +                s
 +            } else {
 +                let mut s = cx.tcx.def_path_str(adt_def.did);
 +                s.push_str("::");
 +                s
 +            },
 +            variant.ident.name,
 +            match variant.ctor_kind {
 +                CtorKind::Fn if variant.fields.len() == 1 => "(_)",
 +                CtorKind::Fn => "(..)",
 +                CtorKind::Const => "",
 +                CtorKind::Fictive => "{ .. }",
 +            }
 +        )
 +    };
 +
 +    match missing_variants.as_slice() {
 +        [] => (),
 +        [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg(
 +            cx,
 +            MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +            wildcard_span,
 +            "wildcard matches only a single variant and will also match any future added variants",
 +            "try this",
 +            format_suggestion(x),
 +            Applicability::MaybeIncorrect,
 +        ),
 +        variants => {
 +            let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
 +            let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden {
 +                suggestions.push("_".into());
 +                "wildcard matches known variants and will also match future added variants"
 +            } else {
 +                "wildcard match will also match any future added variants"
 +            };
 +
 +            span_lint_and_sugg(
 +                cx,
 +                WILDCARD_ENUM_MATCH_ARM,
 +                wildcard_span,
 +                message,
 +                "try this",
 +                suggestions.join(" | "),
 +                Applicability::MaybeIncorrect,
 +            );
 +        },
 +    };
 +}
 +
 +// If the block contains only a `panic!` macro (as expression or statement)
 +fn is_panic_block(block: &Block<'_>) -> bool {
 +    match (&block.expr, block.stmts.len(), block.stmts.first()) {
 +        (&Some(exp), 0, _) => is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none(),
 +        (&None, 1, Some(stmt)) => {
 +            is_expn_of(stmt.span, "panic").is_some() && is_expn_of(stmt.span, "unreachable").is_none()
 +        },
 +        _ => false,
 +    }
 +}
 +
 +fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>)
 +where
 +    'b: 'a,
 +    I: Clone + Iterator<Item = &'a Pat<'b>>,
 +{
 +    if !has_only_ref_pats(pats.clone()) {
 +        return;
 +    }
 +
 +    let (first_sugg, msg, title);
 +    let span = ex.span.source_callsite();
-         if let PatKind::Ref(ref refp, _) = pat.kind {
++    if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind {
 +        first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
 +        msg = "try";
 +        title = "you don't need to add `&` to both the expression and the patterns";
 +    } else {
 +        first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
 +        msg = "instead of prefixing all patterns with `&`, you can dereference the expression";
 +        title = "you don't need to add `&` to all patterns";
 +    }
 +
 +    let remaining_suggs = pats.filter_map(|pat| {
-             if let Some(ref last_pat) = last_pat_opt {
++        if let PatKind::Ref(refp, _) = pat.kind {
 +            Some((pat.span, snippet(cx, refp.span, "..").to_string()))
 +        } else {
 +            None
 +        }
 +    });
 +
 +    span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| {
 +        if !expr.span.from_expansion() {
 +            multispan_sugg(diag, msg, first_sugg.chain(remaining_suggs));
 +        }
 +    });
 +}
 +
 +fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
 +    if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
 +        let arm_ref: Option<BindingAnnotation> = if is_none_arm(cx, &arms[0]) {
 +            is_ref_some_arm(cx, &arms[1])
 +        } else if is_none_arm(cx, &arms[1]) {
 +            is_ref_some_arm(cx, &arms[0])
 +        } else {
 +            None
 +        };
 +        if let Some(rb) = arm_ref {
 +            let suggestion = if rb == BindingAnnotation::Ref {
 +                "as_ref"
 +            } else {
 +                "as_mut"
 +            };
 +
 +            let output_ty = cx.typeck_results().expr_ty(expr);
 +            let input_ty = cx.typeck_results().expr_ty(ex);
 +
 +            let cast = if_chain! {
 +                if let ty::Adt(_, substs) = input_ty.kind();
 +                let input_ty = substs.type_at(0);
 +                if let ty::Adt(_, substs) = output_ty.kind();
 +                let output_ty = substs.type_at(0);
 +                if let ty::Ref(_, output_ty, _) = *output_ty.kind();
 +                if input_ty != output_ty;
 +                then {
 +                    ".map(|x| x as _)"
 +                } else {
 +                    ""
 +                }
 +            };
 +
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_sugg(
 +                cx,
 +                MATCH_AS_REF,
 +                expr.span,
 +                &format!("use `{}()` instead", suggestion),
 +                "try this",
 +                format!(
 +                    "{}.{}(){}",
 +                    snippet_with_applicability(cx, ex.span, "_", &mut applicability),
 +                    suggestion,
 +                    cast,
 +                ),
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
 +    for arm in arms {
 +        if let PatKind::Or(fields) = arm.pat.kind {
 +            // look for multiple fields in this arm that contains at least one Wild pattern
 +            if fields.len() > 1 && fields.iter().any(is_wild) {
 +                span_lint_and_help(
 +                    cx,
 +                    WILDCARD_IN_OR_PATTERNS,
 +                    arm.pat.span,
 +                    "wildcard pattern covers any other pattern as it will match anyway",
 +                    None,
 +                    "consider handling `_` separately",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
 +fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
 +    if let Some(higher::IfLet {
 +        let_pat,
 +        let_expr,
 +        if_then,
 +        if_else: Some(if_else),
 +    }) = higher::IfLet::hir(cx, expr)
 +    {
 +        return find_matches_sugg(
 +            cx,
 +            let_expr,
 +            array::IntoIter::new([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, None)]),
 +            expr,
 +            true,
 +        );
 +    }
 +
 +    if let ExprKind::Match(scrut, arms, MatchSource::Normal) = expr.kind {
 +        return find_matches_sugg(
 +            cx,
 +            scrut,
 +            arms.iter().map(|arm| {
 +                (
 +                    cx.tcx.hir().attrs(arm.hir_id),
 +                    Some(arm.pat),
 +                    arm.body,
 +                    arm.guard.as_ref(),
 +                )
 +            }),
 +            expr,
 +            false,
 +        );
 +    }
 +
 +    false
 +}
 +
 +/// Lint a `match` or `if let` for replacement by `matches!`
 +fn find_matches_sugg<'a, 'b, I>(
 +    cx: &LateContext<'_>,
 +    ex: &Expr<'_>,
 +    mut iter: I,
 +    expr: &Expr<'_>,
 +    is_if_let: bool,
 +) -> bool
 +where
 +    'b: 'a,
 +    I: Clone
 +        + DoubleEndedIterator
 +        + ExactSizeIterator
 +        + Iterator<
 +            Item = (
 +                &'a [Attribute],
 +                Option<&'a Pat<'b>>,
 +                &'a Expr<'b>,
 +                Option<&'a Guard<'b>>,
 +            ),
 +        >,
 +{
 +    if_chain! {
 +        if iter.len() >= 2;
 +        if cx.typeck_results().expr_ty(expr).is_bool();
 +        if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
 +        let iter_without_last = iter.clone();
 +        if let Some((first_attrs, _, first_expr, first_guard)) = iter.next();
 +        if let Some(b0) = find_bool_lit(&first_expr.kind, is_if_let);
 +        if let Some(b1) = find_bool_lit(&last_expr.kind, is_if_let);
 +        if b0 != b1;
 +        if first_guard.is_none() || iter.len() == 0;
 +        if first_attrs.is_empty();
 +        if iter
 +            .all(|arm| {
 +                find_bool_lit(&arm.2.kind, is_if_let).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
 +            });
 +        then {
-             find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some())
++            if let Some(last_pat) = last_pat_opt {
 +                if !is_wild(last_pat) {
 +                    return false;
 +                }
 +            }
 +
 +            // The suggestion may be incorrect, because some arms can have `cfg` attributes
 +            // evaluated into `false` and so such arms will be stripped before.
 +            let mut applicability = Applicability::MaybeIncorrect;
 +            let pat = {
 +                use itertools::Itertools as _;
 +                iter_without_last
 +                    .filter_map(|arm| {
 +                        let pat_span = arm.1?.span;
 +                        Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability))
 +                    })
 +                    .join(" | ")
 +            };
 +            let pat_and_guard = if let Some(Guard::If(g)) = first_guard {
 +                format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability))
 +            } else {
 +                pat
 +            };
 +
 +            // strip potential borrows (#6503), but only if the type is a reference
 +            let mut ex_new = ex;
 +            if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind {
 +                if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() {
 +                    ex_new = ex_inner;
 +                }
 +            };
 +            span_lint_and_sugg(
 +                cx,
 +                MATCH_LIKE_MATCHES_MACRO,
 +                expr.span,
 +                &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }),
 +                "try this",
 +                format!(
 +                    "{}matches!({}, {})",
 +                    if b0 { "" } else { "!" },
 +                    snippet_with_applicability(cx, ex_new.span, "..", &mut applicability),
 +                    pat_and_guard,
 +                ),
 +                applicability,
 +            );
 +            true
 +        } else {
 +            false
 +        }
 +    }
 +}
 +
 +/// Extract a `bool` or `{ bool }`
 +fn find_bool_lit(ex: &ExprKind<'_>, is_if_let: bool) -> Option<bool> {
 +    match ex {
 +        ExprKind::Lit(Spanned {
 +            node: LitKind::Bool(b), ..
 +        }) => Some(*b),
 +        ExprKind::Block(
 +            rustc_hir::Block {
 +                stmts: &[],
 +                expr: Some(exp),
 +                ..
 +            },
 +            _,
 +        ) if is_if_let => {
 +            if let ExprKind::Lit(Spanned {
 +                node: LitKind::Bool(b), ..
 +            }) = exp.kind
 +            {
 +                Some(b)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
 +    if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
 +        return;
 +    }
 +
 +    // HACK:
 +    // This is a hack to deal with arms that are excluded by macros like `#[cfg]`. It is only used here
 +    // to prevent false positives as there is currently no better way to detect if code was excluded by
 +    // a macro. See PR #6435
 +    if_chain! {
 +        if let Some(match_snippet) = snippet_opt(cx, expr.span);
 +        if let Some(arm_snippet) = snippet_opt(cx, arms[0].span);
 +        if let Some(ex_snippet) = snippet_opt(cx, ex.span);
 +        let rest_snippet = match_snippet.replace(&arm_snippet, "").replace(&ex_snippet, "");
 +        if rest_snippet.contains("=>");
 +        then {
 +            // The code it self contains another thick arrow "=>"
 +            // -> Either another arm or a comment
 +            return;
 +        }
 +    }
 +
 +    let matched_vars = ex.span;
 +    let bind_names = arms[0].pat.span;
 +    let match_body = remove_blocks(arms[0].body);
 +    let mut snippet_body = if match_body.span.from_expansion() {
 +        Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string()
 +    } else {
 +        snippet_block(cx, match_body.span, "..", Some(expr.span)).to_string()
 +    };
 +
 +    // Do we need to add ';' to suggestion ?
 +    match match_body.kind {
 +        ExprKind::Block(block, _) => {
 +            // macro + expr_ty(body) == ()
 +            if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() {
 +                snippet_body.push(';');
 +            }
 +        },
 +        _ => {
 +            // expr_ty(body) == ()
 +            if cx.typeck_results().expr_ty(match_body).is_unit() {
 +                snippet_body.push(';');
 +            }
 +        },
 +    }
 +
 +    let mut applicability = Applicability::MaybeIncorrect;
 +    match arms[0].pat.kind {
 +        PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => {
 +            // If this match is in a local (`let`) stmt
 +            let (target_span, sugg) = if let Some(parent_let_node) = opt_parent_let(cx, ex) {
 +                (
 +                    parent_let_node.span,
 +                    format!(
 +                        "let {} = {};\n{}let {} = {};",
 +                        snippet_with_applicability(cx, bind_names, "..", &mut applicability),
 +                        snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
 +                        " ".repeat(indent_of(cx, expr.span).unwrap_or(0)),
 +                        snippet_with_applicability(cx, parent_let_node.pat.span, "..", &mut applicability),
 +                        snippet_body
 +                    ),
 +                )
 +            } else {
 +                // If we are in closure, we need curly braces around suggestion
 +                let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0));
 +                let (mut cbrace_start, mut cbrace_end) = ("".to_string(), "".to_string());
 +                if let Some(parent_expr) = get_parent_expr(cx, expr) {
 +                    if let ExprKind::Closure(..) = parent_expr.kind {
 +                        cbrace_end = format!("\n{}}}", indent);
 +                        // Fix body indent due to the closure
 +                        indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
 +                        cbrace_start = format!("{{\n{}", indent);
 +                    }
 +                }
 +                // If the parent is already an arm, and the body is another match statement,
 +                // we need curly braces around suggestion
 +                let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id);
 +                if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) {
 +                    if let ExprKind::Match(..) = arm.body.kind {
 +                        cbrace_end = format!("\n{}}}", indent);
 +                        // Fix body indent due to the match
 +                        indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
 +                        cbrace_start = format!("{{\n{}", indent);
 +                    }
 +                }
 +                (
 +                    expr.span,
 +                    format!(
 +                        "{}let {} = {};\n{}{}{}",
 +                        cbrace_start,
 +                        snippet_with_applicability(cx, bind_names, "..", &mut applicability),
 +                        snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
 +                        indent,
 +                        snippet_body,
 +                        cbrace_end
 +                    ),
 +                )
 +            };
 +            span_lint_and_sugg(
 +                cx,
 +                MATCH_SINGLE_BINDING,
 +                target_span,
 +                "this match could be written as a `let` statement",
 +                "consider using `let` statement",
 +                sugg,
 +                applicability,
 +            );
 +        },
 +        PatKind::Wild => {
 +            if ex.can_have_side_effects() {
 +                let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0));
 +                let sugg = format!(
 +                    "{};\n{}{}",
 +                    snippet_with_applicability(cx, ex.span, "..", &mut applicability),
 +                    indent,
 +                    snippet_body
 +                );
 +                span_lint_and_sugg(
 +                    cx,
 +                    MATCH_SINGLE_BINDING,
 +                    expr.span,
 +                    "this match could be replaced by its scrutinee and body",
 +                    "consider using the scrutinee and body instead",
 +                    sugg,
 +                    applicability,
 +                );
 +            } else {
 +                span_lint_and_sugg(
 +                    cx,
 +                    MATCH_SINGLE_BINDING,
 +                    expr.span,
 +                    "this match could be replaced by its body itself",
 +                    "consider using the match body instead",
 +                    snippet_body,
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        },
 +        _ => (),
 +    }
 +}
 +
 +/// Returns true if the `ex` match expression is in a local (`let`) statement
 +fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> {
 +    let map = &cx.tcx.hir();
 +    if_chain! {
 +        if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id));
 +        if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id));
 +        then {
 +            return Some(parent_let_expr);
 +        }
 +    }
 +    None
 +}
 +
 +/// Gets all arms that are unbounded `PatRange`s.
 +fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<Constant>> {
 +    arms.iter()
 +        .filter_map(|arm| {
 +            if let Arm { pat, guard: None, .. } = *arm {
 +                if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
 +                    let lhs = match lhs {
 +                        Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0,
 +                        None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
 +                    };
 +                    let rhs = match rhs {
 +                        Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0,
 +                        None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
 +                    };
 +                    let rhs = match range_end {
 +                        RangeEnd::Included => Bound::Included(rhs),
 +                        RangeEnd::Excluded => Bound::Excluded(rhs),
 +                    };
 +                    return Some(SpannedRange {
 +                        span: pat.span,
 +                        node: (lhs, rhs),
 +                    });
 +                }
 +
 +                if let PatKind::Lit(value) = pat.kind {
 +                    let value = constant(cx, cx.typeck_results(), value)?.0;
 +                    return Some(SpannedRange {
 +                        span: pat.span,
 +                        node: (value.clone(), Bound::Included(value)),
 +                    });
 +                }
 +            }
 +            None
 +        })
 +        .collect()
 +}
 +
 +#[derive(Debug, Eq, PartialEq)]
 +pub struct SpannedRange<T> {
 +    pub span: Span,
 +    pub node: (T, Bound<T>),
 +}
 +
 +type TypedRanges = Vec<SpannedRange<u128>>;
 +
 +/// Gets all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
 +/// and other types than
 +/// `Uint` and `Int` probably don't make sense.
 +fn type_ranges(ranges: &[SpannedRange<Constant>]) -> TypedRanges {
 +    ranges
 +        .iter()
 +        .filter_map(|range| match range.node {
 +            (Constant::Int(start), Bound::Included(Constant::Int(end))) => Some(SpannedRange {
 +                span: range.span,
 +                node: (start, Bound::Included(end)),
 +            }),
 +            (Constant::Int(start), Bound::Excluded(Constant::Int(end))) => Some(SpannedRange {
 +                span: range.span,
 +                node: (start, Bound::Excluded(end)),
 +            }),
 +            (Constant::Int(start), Bound::Unbounded) => Some(SpannedRange {
 +                span: range.span,
 +                node: (start, Bound::Unbounded),
 +            }),
 +            _ => None,
 +        })
 +        .collect()
 +}
 +
 +// Checks if arm has the form `None => None`
 +fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +    matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
 +}
 +
 +// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
 +fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotation> {
 +    if_chain! {
 +        if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind;
 +        if is_lang_ctor(cx, qpath, OptionSome);
 +        if let PatKind::Binding(rb, .., ident, _) = first_pat.kind;
 +        if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
 +        if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind;
 +        if let ExprKind::Path(ref some_path) = e.kind;
 +        if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1;
 +        if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
 +        if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
 +        then {
 +            return Some(rb)
 +        }
 +    }
 +    None
 +}
 +
 +fn has_only_ref_pats<'a, 'b, I>(pats: I) -> bool
 +where
 +    'b: 'a,
 +    I: Iterator<Item = &'a Pat<'b>>,
 +{
 +    let mut at_least_one_is_true = false;
 +    for opt in pats.map(|pat| match pat.kind {
 +        PatKind::Ref(..) => Some(true), // &-patterns
 +        PatKind::Wild => Some(false),   // an "anything" wildcard is also fine
 +        _ => None,                      // any other pattern is not fine
 +    }) {
 +        if let Some(inner) = opt {
 +            if inner {
 +                at_least_one_is_true = true;
 +            }
 +        } else {
 +            return false;
 +        }
 +    }
 +    at_least_one_is_true
 +}
 +
 +pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
 +where
 +    T: Copy + Ord,
 +{
 +    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
 +    enum Kind<'a, T> {
 +        Start(T, &'a SpannedRange<T>),
 +        End(Bound<T>, &'a SpannedRange<T>),
 +    }
 +
 +    impl<'a, T: Copy> Kind<'a, T> {
 +        fn range(&self) -> &'a SpannedRange<T> {
 +            match *self {
 +                Kind::Start(_, r) | Kind::End(_, r) => r,
 +            }
 +        }
 +
 +        fn value(self) -> Bound<T> {
 +            match self {
 +                Kind::Start(t, _) => Bound::Included(t),
 +                Kind::End(t, _) => t,
 +            }
 +        }
 +    }
 +
 +    impl<'a, T: Copy + Ord> PartialOrd for Kind<'a, T> {
 +        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
 +            Some(self.cmp(other))
 +        }
 +    }
 +
 +    impl<'a, T: Copy + Ord> Ord for Kind<'a, T> {
 +        fn cmp(&self, other: &Self) -> Ordering {
 +            match (self.value(), other.value()) {
 +                (Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
 +                // Range patterns cannot be unbounded (yet)
 +                (Bound::Unbounded, _) | (_, Bound::Unbounded) => unimplemented!(),
 +                (Bound::Included(a), Bound::Excluded(b)) => match a.cmp(&b) {
 +                    Ordering::Equal => Ordering::Greater,
 +                    other => other,
 +                },
 +                (Bound::Excluded(a), Bound::Included(b)) => match a.cmp(&b) {
 +                    Ordering::Equal => Ordering::Less,
 +                    other => other,
 +                },
 +            }
 +        }
 +    }
 +
 +    let mut values = Vec::with_capacity(2 * ranges.len());
 +
 +    for r in ranges {
 +        values.push(Kind::Start(r.node.0, r));
 +        values.push(Kind::End(r.node.1, r));
 +    }
 +
 +    values.sort();
 +
 +    for (a, b) in iter::zip(&values, values.iter().skip(1)) {
 +        match (a, b) {
 +            (&Kind::Start(_, ra), &Kind::End(_, rb)) => {
 +                if ra.node != rb.node {
 +                    return Some((ra, rb));
 +                }
 +            },
 +            (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
 +            _ => {
 +                // skip if the range `a` is completely included into the range `b`
 +                if let Ordering::Equal | Ordering::Less = a.cmp(b) {
 +                    let kind_a = Kind::End(a.range().node.1, a.range());
 +                    let kind_b = Kind::End(b.range().node.1, b.range());
 +                    if let Ordering::Equal | Ordering::Greater = kind_a.cmp(&kind_b) {
 +                        return None;
 +                    }
 +                }
 +                return Some((a.range(), b.range()));
 +            },
 +        }
 +    }
 +
 +    None
 +}
 +
 +mod redundant_pattern_match {
 +    use super::REDUNDANT_PATTERN_MATCHING;
 +    use clippy_utils::diagnostics::span_lint_and_then;
 +    use clippy_utils::higher;
 +    use clippy_utils::source::{snippet, snippet_with_applicability};
 +    use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
 +    use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths};
 +    use if_chain::if_chain;
 +    use rustc_ast::ast::LitKind;
 +    use rustc_data_structures::fx::FxHashSet;
 +    use rustc_errors::Applicability;
 +    use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
 +    use rustc_hir::{
 +        intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
 +        Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath,
 +    };
 +    use rustc_lint::LateContext;
 +    use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
 +    use rustc_span::sym;
 +
 +    pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let Some(higher::IfLet {
 +            if_else,
 +            let_pat,
 +            let_expr,
 +            ..
 +        }) = higher::IfLet::hir(cx, expr)
 +        {
-             find_sugg_for_match(cx, expr, op, arms)
++            find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some());
 +        }
 +        if let ExprKind::Match(op, arms, MatchSource::Normal) = &expr.kind {
-             find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false)
++            find_sugg_for_match(cx, expr, op, arms);
 +        }
 +        if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
++            find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false);
 +        }
 +    }
 +
 +    /// Checks if the drop order for a type matters. Some std types implement drop solely to
 +    /// deallocate memory. For these types, and composites containing them, changing the drop order
 +    /// won't result in any observable side effects.
 +    fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +        type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
 +    }
 +
 +    fn type_needs_ordered_drop_inner(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
 +        if !seen.insert(ty) {
 +            return false;
 +        }
 +        if !ty.needs_drop(cx.tcx, cx.param_env) {
 +            false
 +        } else if !cx
 +            .tcx
 +            .lang_items()
 +            .drop_trait()
 +            .map_or(false, |id| implements_trait(cx, ty, id, &[]))
 +        {
 +            // This type doesn't implement drop, so no side effects here.
 +            // Check if any component type has any.
 +            match ty.kind() {
 +                ty::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
 +                ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, ty, seen),
 +                ty::Adt(adt, subs) => adt
 +                    .all_fields()
 +                    .map(|f| f.ty(cx.tcx, subs))
 +                    .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
 +                _ => true,
 +            }
 +        }
 +        // Check for std types which implement drop, but only for memory allocation.
 +        else if is_type_diagnostic_item(cx, ty, sym::vec_type)
 +            || is_type_lang_item(cx, ty, LangItem::OwnedBox)
 +            || is_type_diagnostic_item(cx, ty, sym::Rc)
 +            || is_type_diagnostic_item(cx, ty, sym::Arc)
 +            || is_type_diagnostic_item(cx, ty, sym::cstring_type)
 +            || is_type_diagnostic_item(cx, ty, sym::BTreeMap)
 +            || is_type_diagnostic_item(cx, ty, sym::LinkedList)
 +            || match_type(cx, ty, &paths::WEAK_RC)
 +            || match_type(cx, ty, &paths::WEAK_ARC)
 +        {
 +            // Check all of the generic arguments.
 +            if let ty::Adt(_, subs) = ty.kind() {
 +                subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen))
 +            } else {
 +                true
 +            }
 +        } else {
 +            true
 +        }
 +    }
 +
 +    // Extract the generic arguments out of a type
 +    fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
 +        if_chain! {
 +            if let ty::Adt(_, subs) = ty.kind();
 +            if let Some(sub) = subs.get(index);
 +            if let GenericArgKind::Type(sub_ty) = sub.unpack();
 +            then {
 +                Some(sub_ty)
 +            } else {
 +                None
 +            }
 +        }
 +    }
 +
 +    // Checks if there are any temporaries created in the given expression for which drop order
 +    // matters.
 +    fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
 +        struct V<'a, 'tcx> {
 +            cx: &'a LateContext<'tcx>,
 +            res: bool,
 +        }
 +        impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
 +            type Map = ErasedMap<'tcx>;
 +            fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +                NestedVisitorMap::None
 +            }
 +
 +            fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 +                match expr.kind {
 +                    // Taking the reference of a value leaves a temporary
 +                    // e.g. In `&String::new()` the string is a temporary value.
 +                    // Remaining fields are temporary values
 +                    // e.g. In `(String::new(), 0).1` the string is a temporary value.
 +                    ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
 +                        if !matches!(expr.kind, ExprKind::Path(_)) {
 +                            if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
 +                                self.res = true;
 +                            } else {
 +                                self.visit_expr(expr);
 +                            }
 +                        }
 +                    },
 +                    // the base type is alway taken by reference.
 +                    // e.g. In `(vec![0])[0]` the vector is a temporary value.
 +                    ExprKind::Index(base, index) => {
 +                        if !matches!(base.kind, ExprKind::Path(_)) {
 +                            if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
 +                                self.res = true;
 +                            } else {
 +                                self.visit_expr(base);
 +                            }
 +                        }
 +                        self.visit_expr(index);
 +                    },
 +                    // Method calls can take self by reference.
 +                    // e.g. In `String::new().len()` the string is a temporary value.
 +                    ExprKind::MethodCall(_, _, [self_arg, args @ ..], _) => {
 +                        if !matches!(self_arg.kind, ExprKind::Path(_)) {
 +                            let self_by_ref = self
 +                                .cx
 +                                .typeck_results()
 +                                .type_dependent_def_id(expr.hir_id)
 +                                .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
 +                            if self_by_ref
 +                                && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg))
 +                            {
 +                                self.res = true;
 +                            } else {
 +                                self.visit_expr(self_arg);
 +                            }
 +                        }
 +                        args.iter().for_each(|arg| self.visit_expr(arg));
 +                    },
 +                    // Either explicitly drops values, or changes control flow.
 +                    ExprKind::DropTemps(_)
 +                    | ExprKind::Ret(_)
 +                    | ExprKind::Break(..)
 +                    | ExprKind::Yield(..)
 +                    | ExprKind::Block(Block { expr: None, .. }, _)
 +                    | ExprKind::Loop(..) => (),
 +
 +                    // Only consider the final expression.
 +                    ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr),
 +
 +                    _ => walk_expr(self, expr),
 +                }
 +            }
 +        }
 +
 +        let mut v = V { cx, res: false };
 +        v.visit_expr(expr);
 +        v.res
 +    }
 +
 +    fn find_sugg_for_if_let<'tcx>(
 +        cx: &LateContext<'tcx>,
 +        expr: &'tcx Expr<'_>,
 +        let_pat: &Pat<'_>,
 +        let_expr: &'tcx Expr<'_>,
 +        keyword: &'static str,
 +        has_else: bool,
 +    ) {
 +        // also look inside refs
 +        let mut kind = &let_pat.kind;
 +        // if we have &None for example, peel it so we can detect "if let None = x"
 +        if let PatKind::Ref(inner, _mutability) = kind {
 +            kind = &inner.kind;
 +        }
 +        let op_ty = cx.typeck_results().expr_ty(let_expr);
 +        // Determine which function should be used, and the type contained by the corresponding
 +        // variant.
 +        let (good_method, inner_ty) = match kind {
 +            PatKind::TupleStruct(ref path, [sub_pat], _) => {
 +                if let PatKind::Wild = sub_pat.kind {
 +                    if is_lang_ctor(cx, path, ResultOk) {
 +                        ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
 +                    } else if is_lang_ctor(cx, path, ResultErr) {
 +                        ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))
 +                    } else if is_lang_ctor(cx, path, OptionSome) {
 +                        ("is_some()", op_ty)
 +                    } else if is_lang_ctor(cx, path, PollReady) {
 +                        ("is_ready()", op_ty)
 +                    } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V4) {
 +                        ("is_ipv4()", op_ty)
 +                    } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V6) {
 +                        ("is_ipv6()", op_ty)
 +                    } else {
 +                        return;
 +                    }
 +                } else {
 +                    return;
 +                }
 +            },
 +            PatKind::Path(ref path) => {
 +                let method = if is_lang_ctor(cx, path, OptionNone) {
 +                    "is_none()"
 +                } else if is_lang_ctor(cx, path, PollPending) {
 +                    "is_pending()"
 +                } else {
 +                    return;
 +                };
 +                // `None` and `Pending` don't have an inner type.
 +                (method, cx.tcx.types.unit)
 +            },
 +            _ => return,
 +        };
 +
 +        // If this is the last expression in a block or there is an else clause then the whole
 +        // type needs to be considered, not just the inner type of the branch being matched on.
 +        // Note the last expression in a block is dropped after all local bindings.
 +        let check_ty = if has_else
 +            || (keyword == "if" && matches!(cx.tcx.hir().parent_iter(expr.hir_id).next(), Some((_, Node::Block(..)))))
 +        {
 +            op_ty
 +        } else {
 +            inner_ty
 +        };
 +
 +        // All temporaries created in the scrutinee expression are dropped at the same time as the
 +        // scrutinee would be, so they have to be considered as well.
 +        // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
 +        // for the duration if body.
 +        let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
 +
 +        // check that `while_let_on_iterator` lint does not trigger
 +        if_chain! {
 +            if keyword == "while";
 +            if let ExprKind::MethodCall(method_path, _, _, _) = let_expr.kind;
 +            if method_path.ident.name == sym::next;
 +            if is_trait_method(cx, let_expr, sym::Iterator);
 +            then {
 +                return;
 +            }
 +        }
 +
 +        let result_expr = match &let_expr.kind {
 +            ExprKind::AddrOf(_, _, borrowed) => borrowed,
 +            _ => let_expr,
 +        };
 +        span_lint_and_then(
 +            cx,
 +            REDUNDANT_PATTERN_MATCHING,
 +            let_pat.span,
 +            &format!("redundant pattern matching, consider using `{}`", good_method),
 +            |diag| {
 +                // if/while let ... = ... { ... }
 +                // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +                let expr_span = expr.span;
 +
 +                // if/while let ... = ... { ... }
 +                //                 ^^^
 +                let op_span = result_expr.span.source_callsite();
 +
 +                // if/while let ... = ... { ... }
 +                // ^^^^^^^^^^^^^^^^^^^
 +                let span = expr_span.until(op_span.shrink_to_hi());
 +
 +                let mut app = if needs_drop {
 +                    Applicability::MaybeIncorrect
 +                } else {
 +                    Applicability::MachineApplicable
 +                };
 +                let sugg = snippet_with_applicability(cx, op_span, "_", &mut app);
 +
 +                diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app);
 +
 +                if needs_drop {
 +                    diag.note("this will change drop order of the result, as well as all temporaries");
 +                    diag.note("add `#[allow(clippy::redundant_pattern_matching)]` if this is important");
 +                }
 +            },
 +        );
 +    }
 +
 +    fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
 +        if arms.len() == 2 {
 +            let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
 +
 +            let found_good_method = match node_pair {
 +                (
 +                    PatKind::TupleStruct(ref path_left, patterns_left, _),
 +                    PatKind::TupleStruct(ref path_right, patterns_right, _),
 +                ) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
 +                    if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
 +                        find_good_method_for_match(
 +                            cx,
 +                            arms,
 +                            path_left,
 +                            path_right,
 +                            &paths::RESULT_OK,
 +                            &paths::RESULT_ERR,
 +                            "is_ok()",
 +                            "is_err()",
 +                        )
 +                        .or_else(|| {
 +                            find_good_method_for_match(
 +                                cx,
 +                                arms,
 +                                path_left,
 +                                path_right,
 +                                &paths::IPADDR_V4,
 +                                &paths::IPADDR_V6,
 +                                "is_ipv4()",
 +                                "is_ipv6()",
 +                            )
 +                        })
 +                    } else {
 +                        None
 +                    }
 +                },
 +                (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right))
 +                | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _))
 +                    if patterns.len() == 1 =>
 +                {
 +                    if let PatKind::Wild = patterns[0].kind {
 +                        find_good_method_for_match(
 +                            cx,
 +                            arms,
 +                            path_left,
 +                            path_right,
 +                            &paths::OPTION_SOME,
 +                            &paths::OPTION_NONE,
 +                            "is_some()",
 +                            "is_none()",
 +                        )
 +                        .or_else(|| {
 +                            find_good_method_for_match(
 +                                cx,
 +                                arms,
 +                                path_left,
 +                                path_right,
 +                                &paths::POLL_READY,
 +                                &paths::POLL_PENDING,
 +                                "is_ready()",
 +                                "is_pending()",
 +                            )
 +                        })
 +                    } else {
 +                        None
 +                    }
 +                },
 +                _ => None,
 +            };
 +
 +            if let Some(good_method) = found_good_method {
 +                let span = expr.span.to(op.span);
 +                let result_expr = match &op.kind {
 +                    ExprKind::AddrOf(_, _, borrowed) => borrowed,
 +                    _ => op,
 +                };
 +                span_lint_and_then(
 +                    cx,
 +                    REDUNDANT_PATTERN_MATCHING,
 +                    expr.span,
 +                    &format!("redundant pattern matching, consider using `{}`", good_method),
 +                    |diag| {
 +                        diag.span_suggestion(
 +                            span,
 +                            "try this",
 +                            format!("{}.{}", snippet(cx, result_expr.span, "_"), good_method),
 +                            Applicability::MaybeIncorrect, // snippet
 +                        );
 +                    },
 +                );
 +            }
 +        }
 +    }
 +
 +    #[allow(clippy::too_many_arguments)]
 +    fn find_good_method_for_match<'a>(
 +        cx: &LateContext<'_>,
 +        arms: &[Arm<'_>],
 +        path_left: &QPath<'_>,
 +        path_right: &QPath<'_>,
 +        expected_left: &[&str],
 +        expected_right: &[&str],
 +        should_be_left: &'a str,
 +        should_be_right: &'a str,
 +    ) -> Option<&'a str> {
 +        let body_node_pair = if is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_left)
 +            && is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_right)
 +        {
 +            (&(*arms[0].body).kind, &(*arms[1].body).kind)
 +        } else if is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_left)
 +            && is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_right)
 +        {
 +            (&(*arms[1].body).kind, &(*arms[0].body).kind)
 +        } else {
 +            return None;
 +        };
 +
 +        match body_node_pair {
 +            (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
 +                (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left),
 +                (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right),
 +                _ => None,
 +            },
 +            _ => None,
 +        }
 +    }
 +}
 +
 +#[test]
 +fn test_overlapping() {
 +    use rustc_span::source_map::DUMMY_SP;
 +
 +    let sp = |s, e| SpannedRange {
 +        span: DUMMY_SP,
 +        node: (s, e),
 +    };
 +
 +    assert_eq!(None, overlapping::<u8>(&[]));
 +    assert_eq!(None, overlapping(&[sp(1, Bound::Included(4))]));
 +    assert_eq!(
 +        None,
 +        overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6))])
 +    );
 +    assert_eq!(
 +        None,
 +        overlapping(&[
 +            sp(1, Bound::Included(4)),
 +            sp(5, Bound::Included(6)),
 +            sp(10, Bound::Included(11))
 +        ],)
 +    );
 +    assert_eq!(
 +        Some((&sp(1, Bound::Included(4)), &sp(3, Bound::Included(6)))),
 +        overlapping(&[sp(1, Bound::Included(4)), sp(3, Bound::Included(6))])
 +    );
 +    assert_eq!(
 +        Some((&sp(5, Bound::Included(6)), &sp(6, Bound::Included(11)))),
 +        overlapping(&[
 +            sp(1, Bound::Included(4)),
 +            sp(5, Bound::Included(6)),
 +            sp(6, Bound::Included(11))
 +        ],)
 +    );
 +}
 +
 +/// Implementation of `MATCH_SAME_ARMS`.
 +fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
 +    if let ExprKind::Match(_, arms, MatchSource::Normal) = expr.kind {
 +        let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
 +            let mut h = SpanlessHash::new(cx);
 +            h.hash_expr(arm.body);
 +            h.finish()
 +        };
 +
 +        let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
 +            let min_index = usize::min(lindex, rindex);
 +            let max_index = usize::max(lindex, rindex);
 +
 +            let mut local_map: HirIdMap<HirId> = HirIdMap::default();
 +            let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
 +                if_chain! {
 +                    if let Some(a_id) = path_to_local(a);
 +                    if let Some(b_id) = path_to_local(b);
 +                    let entry = match local_map.entry(a_id) {
 +                        Entry::Vacant(entry) => entry,
 +                        // check if using the same bindings as before
 +                        Entry::Occupied(entry) => return *entry.get() == b_id,
 +                    };
 +                    // the names technically don't have to match; this makes the lint more conservative
 +                    if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id);
 +                    if TyS::same_type(cx.typeck_results().expr_ty(a), cx.typeck_results().expr_ty(b));
 +                    if pat_contains_local(lhs.pat, a_id);
 +                    if pat_contains_local(rhs.pat, b_id);
 +                    then {
 +                        entry.insert(b_id);
 +                        true
 +                    } else {
 +                        false
 +                    }
 +                }
 +            };
 +            // Arms with a guard are ignored, those can’t always be merged together
 +            // This is also the case for arms in-between each there is an arm with a guard
 +            (min_index..=max_index).all(|index| arms[index].guard.is_none())
 +                && SpanlessEq::new(cx)
 +                    .expr_fallback(eq_fallback)
 +                    .eq_expr(lhs.body, rhs.body)
 +                // these checks could be removed to allow unused bindings
 +                && bindings_eq(lhs.pat, local_map.keys().copied().collect())
 +                && bindings_eq(rhs.pat, local_map.values().copied().collect())
 +        };
 +
 +        let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
 +        for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
 +            span_lint_and_then(
 +                cx,
 +                MATCH_SAME_ARMS,
 +                j.body.span,
 +                "this `match` has identical arm bodies",
 +                |diag| {
 +                    diag.span_note(i.body.span, "same as this");
 +
 +                    // Note: this does not use `span_suggestion` on purpose:
 +                    // there is no clean way
 +                    // to remove the other arm. Building a span and suggest to replace it to ""
 +                    // makes an even more confusing error message. Also in order not to make up a
 +                    // span for the whole pattern, the suggestion is only shown when there is only
 +                    // one pattern. The user should know about `|` if they are already using it…
 +
 +                    let lhs = snippet(cx, i.pat.span, "<pat1>");
 +                    let rhs = snippet(cx, j.pat.span, "<pat2>");
 +
 +                    if let PatKind::Wild = j.pat.kind {
 +                        // if the last arm is _, then i could be integrated into _
 +                        // note that i.pat cannot be _, because that would mean that we're
 +                        // hiding all the subsequent arms, and rust won't compile
 +                        diag.span_note(
 +                            i.body.span,
 +                            &format!(
 +                                "`{}` has the same arm body as the `_` wildcard, consider removing it",
 +                                lhs
 +                            ),
 +                        );
 +                    } else {
 +                        diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,))
 +                            .help("...or consider changing the match arm bodies");
 +                    }
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +fn pat_contains_local(pat: &Pat<'_>, id: HirId) -> bool {
 +    let mut result = false;
 +    pat.walk_short(|p| {
 +        result |= matches!(p.kind, PatKind::Binding(_, binding_id, ..) if binding_id == id);
 +        !result
 +    });
 +    result
 +}
 +
 +/// Returns true if all the bindings in the `Pat` are in `ids` and vice versa
 +fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool {
 +    let mut result = true;
 +    pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.remove(&id));
 +    result && ids.is_empty()
 +}
index 07202a59c4b96be954e8eb67aacd6f7f1a01b30f,0000000000000000000000000000000000000000..eb437dc47afb496e63b0bfc37e28a3d209a29f7d
mode 100644,000000..100644
--- /dev/null
@@@ -1,45 -1,0 +1,45 @@@
-         if let ExprKind::Call(path_expr, args) = e.kind {
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::{match_def_path, paths};
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `std::mem::forget(t)` where `t` is
 +    /// `Drop`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `std::mem::forget(t)` prevents `t` from running its
 +    /// destructor, possibly causing leaks.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::mem;
 +    /// # use std::rc::Rc;
 +    /// mem::forget(Rc::new(55))
 +    /// ```
 +    pub MEM_FORGET,
 +    restriction,
 +    "`mem::forget` usage on `Drop` types, likely to cause memory leaks"
 +}
 +
 +declare_lint_pass!(MemForget => [MEM_FORGET]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MemForget {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-                         let forgot_ty = cx.typeck_results().expr_ty(&args[0]);
++        if let ExprKind::Call(path_expr, [ref first_arg, ..]) = e.kind {
 +            if let ExprKind::Path(ref qpath) = path_expr.kind {
 +                if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id() {
 +                    if match_def_path(cx, def_id, &paths::MEM_FORGET) {
++                        let forgot_ty = cx.typeck_results().expr_ty(first_arg);
 +
 +                        if forgot_ty.ty_adt_def().map_or(false, |def| def.has_dtor(cx.tcx)) {
 +                            span_lint(cx, MEM_FORGET, e.span, "usage of `mem::forget` on `Drop` type");
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
index 3d071c9081be184f347bc47f82cb73bdde49a940,0000000000000000000000000000000000000000..1e6057a8fe969eb177c0d3252175799bf3ba8b73
mode 100644,000000..100644
--- /dev/null
@@@ -1,288 -1,0 +1,261 @@@
- use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths};
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::{snippet, snippet_with_applicability};
- use rustc_hir::def_id::DefId;
++use clippy_utils::ty::is_non_aggregate_primitive_type;
++use clippy_utils::{in_macro, is_default_equivalent, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
- /// 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_type,
-         sym::vec_type,
-         sym::vecdeque_type,
-         sym::LinkedList,
-         sym::hashmap_type,
-         sym::BTreeMap,
-         sym::hashset_type,
-         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));
-                 }
-             }
 +use rustc_hir::LangItem::OptionNone;
 +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
 +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::source_map::Span;
 +use rustc_span::symbol::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `mem::replace()` on an `Option` with
 +    /// `None`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `Option` already has the method `take()` for
 +    /// taking its current value (Some(..) or None) and replacing it with
 +    /// `None`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::mem;
 +    ///
 +    /// let mut an_option = Some(0);
 +    /// let replaced = mem::replace(&mut an_option, None);
 +    /// ```
 +    /// Is better expressed with:
 +    /// ```rust
 +    /// let mut an_option = Some(0);
 +    /// let taken = an_option.take();
 +    /// ```
 +    pub MEM_REPLACE_OPTION_WITH_NONE,
 +    style,
 +    "replacing an `Option` with `None` instead of `take()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `mem::replace(&mut _, mem::uninitialized())`
 +    /// and `mem::replace(&mut _, mem::zeroed())`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This will lead to undefined behavior even if the
 +    /// value is overwritten later, because the uninitialized value may be
 +    /// observed in the case of a panic.
 +    ///
 +    /// ### Example
 +    /// ```
 +    /// use std::mem;
 +    ///# fn may_panic(v: Vec<i32>) -> Vec<i32> { v }
 +    ///
 +    /// #[allow(deprecated, invalid_value)]
 +    /// fn myfunc (v: &mut Vec<i32>) {
 +    ///     let taken_v = unsafe { mem::replace(v, mem::uninitialized()) };
 +    ///     let new_v = may_panic(taken_v); // undefined behavior on panic
 +    ///     mem::forget(mem::replace(v, new_v));
 +    /// }
 +    /// ```
 +    ///
 +    /// The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,
 +    /// at the cost of either lazily creating a replacement value or aborting
 +    /// on panic, to ensure that the uninitialized value cannot be observed.
 +    pub MEM_REPLACE_WITH_UNINIT,
 +    correctness,
 +    "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `std::mem::replace` on a value of type
 +    /// `T` with `T::default()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `std::mem` module already has the method `take` to
 +    /// take the current value and replace it with the default value of that type.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut text = String::from("foo");
 +    /// let replaced = std::mem::replace(&mut text, String::default());
 +    /// ```
 +    /// Is better expressed with:
 +    /// ```rust
 +    /// let mut text = String::from("foo");
 +    /// let taken = std::mem::take(&mut text);
 +    /// ```
 +    pub MEM_REPLACE_WITH_DEFAULT,
 +    style,
 +    "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
 +}
 +
 +impl_lint_pass!(MemReplace =>
 +    [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
 +
 +fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
 +    if let ExprKind::Path(ref replacement_qpath) = src.kind {
 +        // Check that second argument is `Option::None`
 +        if is_lang_ctor(cx, replacement_qpath, OptionNone) {
 +            // Since this is a late pass (already type-checked),
 +            // and we already know that the second argument is an
 +            // `Option`, we do not need to check the first
 +            // argument's type. All that's left is to get
 +            // replacee's path.
 +            let replaced_path = match dest.kind {
 +                ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, replaced) => {
 +                    if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind {
 +                        replaced_path
 +                    } else {
 +                        return;
 +                    }
 +                },
 +                ExprKind::Path(QPath::Resolved(None, replaced_path)) => replaced_path,
 +                _ => return,
 +            };
 +
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_sugg(
 +                cx,
 +                MEM_REPLACE_OPTION_WITH_NONE,
 +                expr_span,
 +                "replacing an `Option` with `None`",
 +                "consider `Option::take()` instead",
 +                format!(
 +                    "{}.take()",
 +                    snippet_with_applicability(cx, replaced_path.span, "", &mut applicability)
 +                ),
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
 +    if_chain! {
 +        // check if replacement is mem::MaybeUninit::uninit().assume_init()
 +        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(src.hir_id);
 +        if cx.tcx.is_diagnostic_item(sym::assume_init, method_def_id);
 +        then {
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_sugg(
 +                cx,
 +                MEM_REPLACE_WITH_UNINIT,
 +                expr_span,
 +                "replacing with `mem::MaybeUninit::uninit().assume_init()`",
 +                "consider using",
 +                format!(
 +                    "std::ptr::read({})",
 +                    snippet_with_applicability(cx, dest.span, "", &mut applicability)
 +                ),
 +                applicability,
 +            );
 +            return;
 +        }
 +    }
 +
 +    if_chain! {
 +        if let ExprKind::Call(repl_func, repl_args) = src.kind;
 +        if repl_args.is_empty();
 +        if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
 +        if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
 +        then {
 +            if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) {
 +                let mut applicability = Applicability::MachineApplicable;
 +                span_lint_and_sugg(
 +                    cx,
 +                    MEM_REPLACE_WITH_UNINIT,
 +                    expr_span,
 +                    "replacing with `mem::uninitialized()`",
 +                    "consider using",
 +                    format!(
 +                        "std::ptr::read({})",
 +                        snippet_with_applicability(cx, dest.span, "", &mut applicability)
 +                    ),
 +                    applicability,
 +                );
 +            } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) &&
 +                    !cx.typeck_results().expr_ty(src).is_primitive() {
 +                span_lint_and_help(
 +                    cx,
 +                    MEM_REPLACE_WITH_UNINIT,
 +                    expr_span,
 +                    "replacing with `mem::zeroed()`",
 +                    None,
 +                    "consider using a default value or the `take_mut` crate instead",
 +                );
 +            }
 +        }
 +    }
 +}
 +
-     false
- }
++fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
++    // disable lint for primitives
++    let expr_type = cx.typeck_results().expr_ty_adjusted(src);
++    if is_non_aggregate_primitive_type(expr_type) {
++        return;
++    }
++    // disable lint for Option since it is covered in another lint
++    if let ExprKind::Path(q) = &src.kind {
++        if is_lang_ctor(cx, q, OptionNone) {
++            return;
 +        }
 +    }
- fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
-     if_chain! {
-         if let ExprKind::Call(repl_func, _) = src.kind;
-         if !in_external_macro(cx.tcx.sess, expr_span);
-         if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
-         if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
-         if is_diag_trait_item(cx, repl_def_id, sym::Default)
-             || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
-         then {
-             span_lint_and_then(
-                 cx,
-                 MEM_REPLACE_WITH_DEFAULT,
-                 expr_span,
-                 "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
-                 |diag| {
-                     if !in_macro(expr_span) {
-                         let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, ""));
-                         diag.span_suggestion(
-                             expr_span,
-                             "consider using",
-                             suggestion,
-                             Applicability::MachineApplicable
-                         );
-                     }
++    if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) {
++        span_lint_and_then(
++            cx,
++            MEM_REPLACE_WITH_DEFAULT,
++            expr_span,
++            "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
++            |diag| {
++                if !in_macro(expr_span) {
++                    let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, ""));
 +
-             );
-         }
++                    diag.span_suggestion(
++                        expr_span,
++                        "consider using",
++                        suggestion,
++                        Applicability::MachineApplicable,
++                    );
 +                }
++            },
++        );
 +    }
 +}
 +
 +pub struct MemReplace {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl MemReplace {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for MemReplace {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            // Check that `expr` is a call to `mem::replace()`
 +            if let ExprKind::Call(func, func_args) = expr.kind;
 +            if let ExprKind::Path(ref func_qpath) = func.kind;
 +            if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
 +            if match_def_path(cx, def_id, &paths::MEM_REPLACE);
 +            if let [dest, src] = func_args;
 +            then {
 +                check_replace_option_with_none(cx, src, dest, expr.span);
 +                check_replace_with_uninit(cx, src, dest, expr.span);
 +                if meets_msrv(self.msrv.as_ref(), &msrvs::MEM_TAKE) {
 +                    check_replace_with_default(cx, src, dest, expr.span);
 +                }
 +            }
 +        }
 +    }
 +    extract_msrv_attr!(LateContext);
 +}
index 172714f6b01c61d97b31eac24d6a7d3bbc8f013b,0000000000000000000000000000000000000000..bcf8d93b602ef2a7d03703f7864723af5806fca0
mode 100644,000000..100644
--- /dev/null
@@@ -1,39 -1,0 +1,42 @@@
- use clippy_utils::is_trait_method;
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-     if is_trait_method(cx, expr, sym::Iterator) {
 +use clippy_utils::source::snippet;
++use clippy_utils::ty::implements_trait;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_span::sym;
 +
 +use super::FILTER_NEXT;
 +
 +/// lint use of `filter().next()` for `Iterators`
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx hir::Expr<'_>,
 +    recv: &'tcx hir::Expr<'_>,
 +    filter_arg: &'tcx hir::Expr<'_>,
 +) {
 +    // lint if caller of `.filter().next()` is an Iterator
++    let recv_impls_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| {
++        implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[])
++    });
++    if recv_impls_iterator {
 +        let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
 +                   `.find(..)` instead";
 +        let filter_snippet = snippet(cx, filter_arg.span, "..");
 +        if filter_snippet.lines().count() <= 1 {
 +            let iter_snippet = snippet(cx, recv.span, "..");
 +            // add note if not multi-line
 +            span_lint_and_sugg(
 +                cx,
 +                FILTER_NEXT,
 +                expr.span,
 +                msg,
 +                "try this",
 +                format!("{}.find({})", iter_snippet, filter_snippet),
 +                Applicability::MachineApplicable,
 +            );
 +        } else {
 +            span_lint(cx, FILTER_NEXT, expr.span, msg);
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e273186d0519022d5a7c1a9219cf1aa9625fd4a7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,213 @@@
++use clippy_utils::consts::{constant, Constant};
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet_with_context;
++use clippy_utils::{is_diag_item_method, match_def_path, paths};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath};
++use rustc_lint::LateContext;
++use rustc_middle::ty::{self, adjustment::Adjust};
++use rustc_span::{symbol::sym, Span, SyntaxContext};
++
++use super::MANUAL_SPLIT_ONCE;
++
++pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) {
++    if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
++        return;
++    }
++
++    let ctxt = expr.span.ctxt();
++    let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id)) {
++        Some(x) => x,
++        None => return,
++    };
++    let (method_name, msg) = if method_name == "splitn" {
++        ("split_once", "manual implementation of `split_once`")
++    } else {
++        ("rsplit_once", "manual implementation of `rsplit_once`")
++    };
++
++    let mut app = Applicability::MachineApplicable;
++    let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
++    let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
++
++    match usage.kind {
++        IterUsageKind::NextTuple => {
++            span_lint_and_sugg(
++                cx,
++                MANUAL_SPLIT_ONCE,
++                usage.span,
++                msg,
++                "try this",
++                format!("{}.{}({})", self_snip, method_name, pat_snip),
++                app,
++            );
++        },
++        IterUsageKind::Next => {
++            let self_deref = {
++                let adjust = cx.typeck_results().expr_adjustments(self_arg);
++                if adjust.is_empty() {
++                    String::new()
++                } else if cx.typeck_results().expr_ty(self_arg).is_box()
++                    || adjust
++                        .iter()
++                        .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box())
++                {
++                    format!("&{}", "*".repeat(adjust.len() - 1))
++                } else {
++                    "*".repeat(adjust.len() - 2)
++                }
++            };
++            let sugg = if usage.unwrap_kind.is_some() {
++                format!(
++                    "{}.{}({}).map_or({}{}, |x| x.0)",
++                    &self_snip, method_name, pat_snip, self_deref, &self_snip
++                )
++            } else {
++                format!(
++                    "Some({}.{}({}).map_or({}{}, |x| x.0))",
++                    &self_snip, method_name, pat_snip, self_deref, &self_snip
++                )
++            };
++
++            span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
++        },
++        IterUsageKind::Second => {
++            let access_str = match usage.unwrap_kind {
++                Some(UnwrapKind::Unwrap) => ".unwrap().1",
++                Some(UnwrapKind::QuestionMark) => "?.1",
++                None => ".map(|x| x.1)",
++            };
++            span_lint_and_sugg(
++                cx,
++                MANUAL_SPLIT_ONCE,
++                usage.span,
++                msg,
++                "try this",
++                format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str),
++                app,
++            );
++        },
++    }
++}
++
++enum IterUsageKind {
++    Next,
++    Second,
++    NextTuple,
++}
++
++enum UnwrapKind {
++    Unwrap,
++    QuestionMark,
++}
++
++struct IterUsage {
++    kind: IterUsageKind,
++    unwrap_kind: Option<UnwrapKind>,
++    span: Span,
++}
++
++fn parse_iter_usage(
++    cx: &LateContext<'tcx>,
++    ctxt: SyntaxContext,
++    mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
++) -> Option<IterUsage> {
++    let (kind, span) = match iter.next() {
++        Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
++            let (name, args) = if let ExprKind::MethodCall(name, _, [_, args @ ..], _) = e.kind {
++                (name, args)
++            } else {
++                return None;
++            };
++            let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
++            let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
++
++            match (&*name.ident.as_str(), args) {
++                ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Next, e.span),
++                ("next_tuple", []) => {
++                    if_chain! {
++                        if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
++                        if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind();
++                        if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did);
++                        if let ty::Tuple(subs) = subs.type_at(0).kind();
++                        if subs.len() == 2;
++                        then {
++                            return Some(IterUsage { kind: IterUsageKind::NextTuple, span: e.span, unwrap_kind: None });
++                        } else {
++                            return None;
++                        }
++                    }
++                },
++                ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
++                    if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
++                        let span = if name.ident.as_str() == "nth" {
++                            e.span
++                        } else {
++                            if_chain! {
++                                if let Some((_, Node::Expr(next_expr))) = iter.next();
++                                if let ExprKind::MethodCall(next_name, _, [_], _) = next_expr.kind;
++                                if next_name.ident.name == sym::next;
++                                if next_expr.span.ctxt() == ctxt;
++                                if let Some(next_id) = cx.typeck_results().type_dependent_def_id(next_expr.hir_id);
++                                if cx.tcx.trait_of_item(next_id) == Some(iter_id);
++                                then {
++                                    next_expr.span
++                                } else {
++                                    return None;
++                                }
++                            }
++                        };
++                        match idx {
++                            0 => (IterUsageKind::Next, span),
++                            1 => (IterUsageKind::Second, span),
++                            _ => return None,
++                        }
++                    } else {
++                        return None;
++                    }
++                },
++                _ => return None,
++            }
++        },
++        _ => return None,
++    };
++
++    let (unwrap_kind, span) = if let Some((_, Node::Expr(e))) = iter.next() {
++        match e.kind {
++            ExprKind::Call(
++                Expr {
++                    kind: ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, _)),
++                    ..
++                },
++                _,
++            ) => {
++                let parent_span = e.span.parent().unwrap();
++                if parent_span.ctxt() == ctxt {
++                    (Some(UnwrapKind::QuestionMark), parent_span)
++                } else {
++                    (None, span)
++                }
++            },
++            _ if e.span.ctxt() != ctxt => (None, span),
++            ExprKind::MethodCall(name, _, [_], _)
++                if name.ident.name == sym::unwrap
++                    && cx
++                        .typeck_results()
++                        .type_dependent_def_id(e.hir_id)
++                        .map_or(false, |id| is_diag_item_method(cx, id, sym::option_type)) =>
++            {
++                (Some(UnwrapKind::Unwrap), e.span)
++            },
++            _ => (None, span),
++        }
++    } else {
++        (None, span)
++    };
++
++    Some(IterUsage {
++        kind,
++        unwrap_kind,
++        span,
++    })
++}
index 9626cf79dc129c4ba253907830fa8674c0c8e722,0000000000000000000000000000000000000000..e89b2d295b92345f4f1173e0e19911c559cc3bca
mode 100644,000000..100644
--- /dev/null
@@@ -1,2430 -1,0 +1,2466 @@@
-     EXTEND_WITH_DRAIN
 +mod bind_instead_of_map;
 +mod bytes_nth;
 +mod chars_cmp;
 +mod chars_cmp_with_unwrap;
 +mod chars_last_cmp;
 +mod chars_last_cmp_with_unwrap;
 +mod chars_next_cmp;
 +mod chars_next_cmp_with_unwrap;
 +mod clone_on_copy;
 +mod clone_on_ref_ptr;
 +mod cloned_instead_of_copied;
 +mod expect_fun_call;
 +mod expect_used;
 +mod extend_with_drain;
 +mod filetype_is_file;
 +mod filter_map;
 +mod filter_map_identity;
 +mod filter_map_next;
 +mod filter_next;
 +mod flat_map_identity;
 +mod flat_map_option;
 +mod from_iter_instead_of_collect;
 +mod get_unwrap;
 +mod implicit_clone;
 +mod inefficient_to_string;
 +mod inspect_for_each;
 +mod into_iter_on_ref;
 +mod iter_cloned_collect;
 +mod iter_count;
 +mod iter_next_slice;
 +mod iter_nth;
 +mod iter_nth_zero;
 +mod iter_skip_next;
 +mod iterator_step_by_zero;
 +mod manual_saturating_arithmetic;
++mod manual_split_once;
 +mod manual_str_repeat;
 +mod map_collect_result_unit;
 +mod map_flatten;
 +mod map_identity;
 +mod map_unwrap_or;
 +mod ok_expect;
 +mod option_as_ref_deref;
 +mod option_map_or_none;
 +mod option_map_unwrap_or;
 +mod or_fun_call;
 +mod search_is_some;
 +mod single_char_add_str;
 +mod single_char_insert_string;
 +mod single_char_pattern;
 +mod single_char_push_string;
 +mod skip_while_next;
 +mod string_extend_chars;
 +mod suspicious_map;
 +mod suspicious_splitn;
 +mod uninit_assumed_init;
 +mod unnecessary_filter_map;
 +mod unnecessary_fold;
 +mod unnecessary_lazy_eval;
 +mod unwrap_or_else_default;
 +mod unwrap_used;
 +mod useless_asref;
 +mod utils;
 +mod wrong_self_convention;
 +mod zst_offset;
 +
 +use bind_instead_of_map::BindInsteadOfMap;
++use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 +use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
 +use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_hir::def::Res;
 +use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{self, TraitRef, Ty, TyS};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::SymbolStr;
 +use rustc_span::{sym, Span};
 +use rustc_typeck::hir_ty_to_ty;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `cloned()` on an `Iterator` or `Option` where
 +    /// `copied()` could be used instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// `copied()` is better because it guarantees that the type being cloned
 +    /// implements `Copy`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// [1, 2, 3].iter().cloned();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// [1, 2, 3].iter().copied();
 +    /// ```
 +    pub CLONED_INSTEAD_OF_COPIED,
 +    pedantic,
 +    "used `cloned` where `copied` could be used instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
 +    /// used instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// When applicable, `filter_map()` is more clear since it shows that
 +    /// `Option` is used to produce 0 or 1 items.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
 +    /// ```
 +    pub FLAT_MAP_OPTION,
 +    pedantic,
 +    "used `flat_map` where `filter_map` could be used instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.unwrap()` calls on `Option`s and on `Result`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is better to handle the `None` or `Err` case,
 +    /// or at least call `.expect(_)` with a more helpful message. Still, for a lot of
 +    /// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is
 +    /// `Allow` by default.
 +    ///
 +    /// `result.unwrap()` will let the thread panic on `Err` values.
 +    /// Normally, you want to implement more sophisticated error handling,
 +    /// and propagate errors upwards with `?` operator.
 +    ///
 +    /// Even if you want to panic on errors, not all `Error`s implement good
 +    /// messages on display. Therefore, it may be beneficial to look at the places
 +    /// where they may get displayed. Activate this lint to do just that.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    ///
 +    /// // Bad
 +    /// opt.unwrap();
 +    ///
 +    /// // Good
 +    /// opt.expect("more helpful message");
 +    /// ```
 +    ///
 +    /// // or
 +    ///
 +    /// ```rust
 +    /// # let res: Result<usize, ()> = Ok(1);
 +    ///
 +    /// // Bad
 +    /// res.unwrap();
 +    ///
 +    /// // Good
 +    /// res.expect("more helpful message");
 +    /// ```
 +    pub UNWRAP_USED,
 +    restriction,
 +    "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.expect()` calls on `Option`s and `Result`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// Usually it is better to handle the `None` or `Err` case.
 +    /// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why
 +    /// this lint is `Allow` by default.
 +    ///
 +    /// `result.expect()` will let the thread panic on `Err`
 +    /// values. Normally, you want to implement more sophisticated error handling,
 +    /// and propagate errors upwards with `?` operator.
 +    ///
 +    /// ### Examples
 +    /// ```rust,ignore
 +    /// # let opt = Some(1);
 +    ///
 +    /// // Bad
 +    /// opt.expect("one");
 +    ///
 +    /// // Good
 +    /// let opt = Some(1);
 +    /// opt?;
 +    /// ```
 +    ///
 +    /// // or
 +    ///
 +    /// ```rust
 +    /// # let res: Result<usize, ()> = Ok(1);
 +    ///
 +    /// // Bad
 +    /// res.expect("one");
 +    ///
 +    /// // Good
 +    /// res?;
 +    /// # Ok::<(), ()>(())
 +    /// ```
 +    pub EXPECT_USED,
 +    restriction,
 +    "using `.expect()` on `Result` or `Option`, which might be better handled"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for methods that should live in a trait
 +    /// implementation of a `std` trait (see [llogiq's blog
 +    /// post](http://llogiq.github.io/2015/07/30/traits.html) for further
 +    /// information) instead of an inherent implementation.
 +    ///
 +    /// ### Why is this bad?
 +    /// Implementing the traits improve ergonomics for users of
 +    /// the code, often with very little cost. Also people seeing a `mul(...)`
 +    /// method
 +    /// may expect `*` to work equally, so you should have good reason to disappoint
 +    /// them.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct X;
 +    /// impl X {
 +    ///     fn add(&self, other: &X) -> X {
 +    ///         // ..
 +    /// # X
 +    ///     }
 +    /// }
 +    /// ```
 +    pub SHOULD_IMPLEMENT_TRAIT,
 +    style,
 +    "defining a method that should be implementing a std trait"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for methods with certain name prefixes and which
 +    /// doesn't match how self is taken. The actual rules are:
 +    ///
 +    /// |Prefix |Postfix     |`self` taken           | `self` type  |
 +    /// |-------|------------|-----------------------|--------------|
 +    /// |`as_`  | none       |`&self` or `&mut self` | any          |
 +    /// |`from_`| none       | none                  | any          |
 +    /// |`into_`| none       |`self`                 | any          |
 +    /// |`is_`  | none       |`&self` or none        | any          |
 +    /// |`to_`  | `_mut`     |`&mut self`            | any          |
 +    /// |`to_`  | not `_mut` |`self`                 | `Copy`       |
 +    /// |`to_`  | not `_mut` |`&self`                | not `Copy`   |
 +    ///
 +    /// Note: Clippy doesn't trigger methods with `to_` prefix in:
 +    /// - Traits definition.
 +    /// Clippy can not tell if a type that implements a trait is `Copy` or not.
 +    /// - Traits implementation, when `&self` is taken.
 +    /// The method signature is controlled by the trait and often `&self` is required for all types that implement the trait
 +    /// (see e.g. the `std::string::ToString` trait).
 +    ///
 +    /// Please find more info here:
 +    /// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
 +    ///
 +    /// ### Why is this bad?
 +    /// Consistency breeds readability. If you follow the
 +    /// conventions, your users won't be surprised that they, e.g., need to supply a
 +    /// mutable reference to a `as_..` function.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct X;
 +    /// impl X {
 +    ///     fn as_str(self) -> &'static str {
 +    ///         // ..
 +    /// # ""
 +    ///     }
 +    /// }
 +    /// ```
 +    pub WRONG_SELF_CONVENTION,
 +    style,
 +    "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `ok().expect(..)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Because you usually call `expect()` on the `Result`
 +    /// directly to get a better error message.
 +    ///
 +    /// ### Known problems
 +    /// The error type needs to implement `Debug`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = Ok::<_, ()>(());
 +    ///
 +    /// // Bad
 +    /// x.ok().expect("why did I do this again?");
 +    ///
 +    /// // Good
 +    /// x.expect("why did I do this again?");
 +    /// ```
 +    pub OK_EXPECT,
 +    style,
 +    "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and
 +    /// `Result` values.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, these can be written as `_.unwrap_or_default`, which is
 +    /// simpler and more concise.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let x = Some(1);
 +    ///
 +    /// // Bad
 +    /// x.unwrap_or_else(Default::default);
 +    /// x.unwrap_or_else(u32::default);
 +    ///
 +    /// // Good
 +    /// x.unwrap_or_default();
 +    /// ```
 +    pub UNWRAP_OR_ELSE_DEFAULT,
 +    style,
 +    "using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or
 +    /// `result.map(_).unwrap_or_else(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, these can be written more concisely (resp.) as
 +    /// `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.
 +    ///
 +    /// ### Known problems
 +    /// The order of the arguments is not in execution order
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let x = Some(1);
 +    ///
 +    /// // Bad
 +    /// x.map(|a| a + 1).unwrap_or(0);
 +    ///
 +    /// // Good
 +    /// x.map_or(0, |a| a + 1);
 +    /// ```
 +    ///
 +    /// // or
 +    ///
 +    /// ```rust
 +    /// # let x: Result<usize, ()> = Ok(1);
 +    /// # fn some_function(foo: ()) -> usize { 1 }
 +    ///
 +    /// // Bad
 +    /// x.map(|a| a + 1).unwrap_or_else(some_function);
 +    ///
 +    /// // Good
 +    /// x.map_or_else(some_function, |a| a + 1);
 +    /// ```
 +    pub MAP_UNWRAP_OR,
 +    pedantic,
 +    "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map_or(None, _)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.and_then(_)`.
 +    ///
 +    /// ### Known problems
 +    /// The order of the arguments is not in execution order.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    ///
 +    /// // Bad
 +    /// opt.map_or(None, |a| Some(a + 1));
 +    ///
 +    /// // Good
 +    /// opt.and_then(|a| Some(a + 1));
 +    /// ```
 +    pub OPTION_MAP_OR_NONE,
 +    style,
 +    "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map_or(None, Some)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.ok()`.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust
 +    /// # let r: Result<u32, &str> = Ok(1);
 +    /// assert_eq!(Some(1), r.map_or(None, Some));
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust
 +    /// # let r: Result<u32, &str> = Ok(1);
 +    /// assert_eq!(Some(1), r.ok());
 +    /// ```
 +    pub RESULT_MAP_OR_INTO_OPTION,
 +    style,
 +    "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or
 +    /// `_.or_else(|x| Err(y))`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.map(|x| y)` or `_.map_err(|x| y)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn opt() -> Option<&'static str> { Some("42") }
 +    /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
 +    /// let _ = opt().and_then(|s| Some(s.len()));
 +    /// let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });
 +    /// let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });
 +    /// ```
 +    ///
 +    /// The correct use would be:
 +    ///
 +    /// ```rust
 +    /// # fn opt() -> Option<&'static str> { Some("42") }
 +    /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
 +    /// let _ = opt().map(|s| s.len());
 +    /// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });
 +    /// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });
 +    /// ```
 +    pub BIND_INSTEAD_OF_MAP,
 +    complexity,
 +    "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter(_).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().filter(|x| **x == 0).next();
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().find(|x| **x == 0);
 +    /// ```
 +    pub FILTER_NEXT,
 +    complexity,
 +    "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.skip_while(condition).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find(!condition)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().skip_while(|x| **x == 0).next();
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().find(|x| **x != 0);
 +    /// ```
 +    pub SKIP_WHILE_NEXT,
 +    complexity,
 +    "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map(_).flatten(_)` on `Iterator` and `Option`
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.flat_map(_)`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec = vec![vec![1]];
 +    ///
 +    /// // Bad
 +    /// vec.iter().map(|x| x.iter()).flatten();
 +    ///
 +    /// // Good
 +    /// vec.iter().flat_map(|x| x.iter());
 +    /// ```
 +    pub MAP_FLATTEN,
 +    pedantic,
 +    "using combinations of `flatten` and `map` which can usually be written as a single method call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter(_).map(_)` that can be written more simply
 +    /// as `filter_map(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code in the `filter` and `map` operations is poor style and
 +    /// less performant.
 +    ///
 +     /// ### Example
 +    /// Bad:
 +    /// ```rust
 +    /// (0_i32..10)
 +    ///     .filter(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust
 +    /// (0_i32..10).filter_map(|n| n.checked_add(1));
 +    /// ```
 +    pub MANUAL_FILTER_MAP,
 +    complexity,
 +    "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.find(_).map(_)` that can be written more simply
 +    /// as `find_map(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code in the `find` and `map` operations is poor style and
 +    /// less performant.
 +    ///
 +     /// ### Example
 +    /// Bad:
 +    /// ```rust
 +    /// (0_i32..10)
 +    ///     .find(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust
 +    /// (0_i32..10).find_map(|n| n.checked_add(1));
 +    /// ```
 +    pub MANUAL_FIND_MAP,
 +    complexity,
 +    "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter_map(_).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find_map(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    ///  (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();
 +    /// ```
 +    /// Can be written as
 +    ///
 +    /// ```rust
 +    ///  (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
 +    /// ```
 +    pub FILTER_MAP_NEXT,
 +    pedantic,
 +    "using combination of `filter_map` and `next` which can usually be written as a single method call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `flat_map(|x| x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely by using `flatten`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iter = vec![vec![0]].into_iter();
 +    /// iter.flat_map(|x| x);
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// # let iter = vec![vec![0]].into_iter();
 +    /// iter.flatten();
 +    /// ```
 +    pub FLAT_MAP_IDENTITY,
 +    complexity,
 +    "call to `flat_map` where `flatten` is sufficient"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for an iterator or string search (such as `find()`,
 +    /// `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as:
 +    /// * `_.any(_)`, or `_.contains(_)` for `is_some()`,
 +    /// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec = vec![1];
 +    /// vec.iter().find(|x| **x == 0).is_some();
 +    ///
 +    /// let _ = "hello world".find("world").is_none();
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// let vec = vec![1];
 +    /// vec.iter().any(|x| *x == 0);
 +    ///
 +    /// let _ = !"hello world".contains("world");
 +    /// ```
 +    pub SEARCH_IS_SOME,
 +    complexity,
 +    "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.chars().next()` on a `str` to check
 +    /// if it starts with a given char.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.starts_with(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let name = "foo";
 +    /// if name.chars().next() == Some('_') {};
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// let name = "foo";
 +    /// if name.starts_with('_') {};
 +    /// ```
 +    pub CHARS_NEXT_CMP,
 +    style,
 +    "using `.chars().next()` to check if a string starts with a char"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,
 +    /// etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or
 +    /// `unwrap_or_default` instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// The function will always be called and potentially
 +    /// allocate an object acting as the default.
 +    ///
 +    /// ### Known problems
 +    /// If the function has side-effects, not calling it will
 +    /// change the semantic of the program, but you shouldn't rely on that anyway.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or(String::new());
 +    /// ```
 +    /// this can instead be written:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or_else(String::new);
 +    /// ```
 +    /// or
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or_default();
 +    /// ```
 +    pub OR_FUN_CALL,
 +    perf,
 +    "using any `*or` method with a function call, which suggests `*or_else`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,
 +    /// etc., and suggests to use `unwrap_or_else` instead
 +    ///
 +    /// ### Why is this bad?
 +    /// The function will always be called.
 +    ///
 +    /// ### Known problems
 +    /// If the function has side-effects, not calling it will
 +    /// change the semantics of the program, but you shouldn't rely on that anyway.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # let err_msg = "I'm a teapot";
 +    /// foo.expect(&format!("Err {}: {}", err_code, err_msg));
 +    /// ```
 +    /// or
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # let err_msg = "I'm a teapot";
 +    /// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str());
 +    /// ```
 +    /// this can instead be written:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # let err_msg = "I'm a teapot";
 +    /// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
 +    /// ```
 +    pub EXPECT_FUN_CALL,
 +    perf,
 +    "using any `expect` method with a function call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on a `Copy` type.
 +    ///
 +    /// ### Why is this bad?
 +    /// The only reason `Copy` types implement `Clone` is for
 +    /// generics, not for using the `clone` method on a concrete type.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// 42u64.clone();
 +    /// ```
 +    pub CLONE_ON_COPY,
 +    complexity,
 +    "using `clone` on a `Copy` type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on a ref-counted pointer,
 +    /// (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified
 +    /// function syntax instead (e.g., `Rc::clone(foo)`).
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling '.clone()' on an Rc, Arc, or Weak
 +    /// can obscure the fact that only the pointer is being cloned, not the underlying
 +    /// data.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// let x = Rc::new(1);
 +    ///
 +    /// // Bad
 +    /// x.clone();
 +    ///
 +    /// // Good
 +    /// Rc::clone(&x);
 +    /// ```
 +    pub CLONE_ON_REF_PTR,
 +    restriction,
 +    "using 'clone' on a ref-counted pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on an `&&T`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Cloning an `&&T` copies the inner `&T`, instead of
 +    /// cloning the underlying `T`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn main() {
 +    ///     let x = vec![1];
 +    ///     let y = &&x;
 +    ///     let z = y.clone();
 +    ///     println!("{:p} {:p}", *y, z); // prints out the same pointer
 +    /// }
 +    /// ```
 +    pub CLONE_DOUBLE_REF,
 +    correctness,
 +    "using `clone` on `&&T`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.to_string()` on an `&&T` where
 +    /// `T` implements `ToString` directly (like `&&str` or `&&String`).
 +    ///
 +    /// ### Why is this bad?
 +    /// This bypasses the specialized implementation of
 +    /// `ToString` and instead goes through the more expensive string formatting
 +    /// facilities.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Generic implementation for `T: Display` is used (slow)
 +    /// ["foo", "bar"].iter().map(|s| s.to_string());
 +    ///
 +    /// // OK, the specialized impl is used
 +    /// ["foo", "bar"].iter().map(|&s| s.to_string());
 +    /// ```
 +    pub INEFFICIENT_TO_STRING,
 +    pedantic,
 +    "using `to_string` on `&&T` where `T: ToString`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `new` not returning a type that contains `Self`.
 +    ///
 +    /// ### Why is this bad?
 +    /// As a convention, `new` methods are used to make a new
 +    /// instance of a type.
 +    ///
 +    /// ### Example
 +    /// In an impl block:
 +    /// ```rust
 +    /// # struct Foo;
 +    /// # struct NotAFoo;
 +    /// impl Foo {
 +    ///     fn new() -> NotAFoo {
 +    /// # NotAFoo
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # struct Foo;
 +    /// struct Bar(Foo);
 +    /// impl Foo {
 +    ///     // Bad. The type name must contain `Self`
 +    ///     fn new() -> Bar {
 +    /// # Bar(Foo)
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # struct Foo;
 +    /// # struct FooError;
 +    /// impl Foo {
 +    ///     // Good. Return type contains `Self`
 +    ///     fn new() -> Result<Foo, FooError> {
 +    /// # Ok(Foo)
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Or in a trait definition:
 +    /// ```rust
 +    /// pub trait Trait {
 +    ///     // Bad. The type name must contain `Self`
 +    ///     fn new();
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// pub trait Trait {
 +    ///     // Good. Return type contains `Self`
 +    ///     fn new() -> Self;
 +    /// }
 +    /// ```
 +    pub NEW_RET_NO_SELF,
 +    style,
 +    "not returning type containing `Self` in a `new` method"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for string methods that receive a single-character
 +    /// `str` as an argument, e.g., `_.split("x")`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Performing these methods using a `char` is faster than
 +    /// using a `str`.
 +    ///
 +    /// ### Known problems
 +    /// Does not catch multi-byte unicode characters.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// _.split("x");
 +    ///
 +    /// // Good
 +    /// _.split('x');
 +    pub SINGLE_CHAR_PATTERN,
 +    perf,
 +    "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calling `.step_by(0)` on iterators which panics.
 +    ///
 +    /// ### Why is this bad?
 +    /// This very much looks like an oversight. Use `panic!()` instead if you
 +    /// actually intend to panic.
 +    ///
 +    /// ### Example
 +    /// ```rust,should_panic
 +    /// for x in (0..100).step_by(0) {
 +    ///     //..
 +    /// }
 +    /// ```
 +    pub ITERATOR_STEP_BY_ZERO,
 +    correctness,
 +    "using `Iterator::step_by(0)`, which will panic at runtime"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for indirect collection of populated `Option`
 +    ///
 +    /// ### Why is this bad?
 +    /// `Option` is like a collection of 0-1 things, so `flatten`
 +    /// automatically does this without suspicious-looking `unwrap` calls.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = std::iter::empty::<Option<i32>>().filter(Option::is_some).map(Option::unwrap);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let _ = std::iter::empty::<Option<i32>>().flatten();
 +    /// ```
 +    pub OPTION_FILTER_MAP,
 +    complexity,
 +    "filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `iter.nth(0)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `iter.next()` is equivalent to
 +    /// `iter.nth(0)`, as they both consume the next element,
 +    ///  but is more readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// // Bad
 +    /// # let mut s = HashSet::new();
 +    /// # s.insert(1);
 +    /// let x = s.iter().nth(0);
 +    ///
 +    /// // Good
 +    /// # let mut s = HashSet::new();
 +    /// # s.insert(1);
 +    /// let x = s.iter().next();
 +    /// ```
 +    pub ITER_NTH_ZERO,
 +    style,
 +    "replace `iter.nth(0)` with `iter.next()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.iter().nth()` (and the related
 +    /// `.iter_mut().nth()`) on standard library types with O(1) element access.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.get()` and `.get_mut()` are more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().nth(3);
 +    /// let bad_slice = &some_vec[..].iter().nth(3);
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.get(3);
 +    /// let bad_slice = &some_vec[..].get(3);
 +    /// ```
 +    pub ITER_NTH,
 +    perf,
 +    "using `.iter().nth()` on a standard library type with O(1) element access"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.skip(x).next()` on iterators.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.nth(x)` is cleaner
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().skip(3).next();
 +    /// let bad_slice = &some_vec[..].iter().skip(3).next();
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().nth(3);
 +    /// let bad_slice = &some_vec[..].iter().nth(3);
 +    /// ```
 +    pub ITER_SKIP_NEXT,
 +    style,
 +    "using `.skip(x).next()` on an iterator"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.get().unwrap()` (or
 +    /// `.get_mut().unwrap`) on a standard library type which implements `Index`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the Index trait (`[]`) is more clear and more
 +    /// concise.
 +    ///
 +    /// ### Known problems
 +    /// Not a replacement for error handling: Using either
 +    /// `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`
 +    /// if the value being accessed is `None`. If the use of `.get().unwrap()` is a
 +    /// temporary placeholder for dealing with the `Option` type, then this does
 +    /// not mitigate the need for error handling. If there is a chance that `.get()`
 +    /// will be `None` in your program, then it is advisable that the `None` case
 +    /// is handled in a future refactor instead of using `.unwrap()` or the Index
 +    /// trait.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut some_vec = vec![0, 1, 2, 3];
 +    /// let last = some_vec.get(3).unwrap();
 +    /// *some_vec.get_mut(0).unwrap() = 1;
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let mut some_vec = vec![0, 1, 2, 3];
 +    /// let last = some_vec[3];
 +    /// some_vec[0] = 1;
 +    /// ```
 +    pub GET_UNWRAP,
 +    restriction,
 +    "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for occurrences where one vector gets extended instead of append
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `append` instead of `extend` is more concise and faster
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut a = vec![1, 2, 3];
 +    /// let mut b = vec![4, 5, 6];
 +    ///
 +    /// // Bad
 +    /// a.extend(b.drain(..));
 +    ///
 +    /// // Good
 +    /// a.append(&mut b);
 +    /// ```
 +    pub EXTEND_WITH_DRAIN,
 +    perf,
 +    "using vec.append(&mut vec) to move the full range of a vecor to another"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.extend(s.chars())` where s is a
 +    /// `&str` or `String`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.push_str(s)` is clearer
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let abc = "abc";
 +    /// let def = String::from("def");
 +    /// let mut s = String::new();
 +    /// s.extend(abc.chars());
 +    /// s.extend(def.chars());
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let abc = "abc";
 +    /// let def = String::from("def");
 +    /// let mut s = String::new();
 +    /// s.push_str(abc);
 +    /// s.push_str(&def);
 +    /// ```
 +    pub STRING_EXTEND_CHARS,
 +    style,
 +    "using `x.extend(s.chars())` where s is a `&str` or `String`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.cloned().collect()` on slice to
 +    /// create a `Vec`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.to_vec()` is clearer
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let s = [1, 2, 3, 4, 5];
 +    /// let s2: Vec<isize> = s[..].iter().cloned().collect();
 +    /// ```
 +    /// The better use would be:
 +    /// ```rust
 +    /// let s = [1, 2, 3, 4, 5];
 +    /// let s2: Vec<isize> = s.to_vec();
 +    /// ```
 +    pub ITER_CLONED_COLLECT,
 +    style,
 +    "using `.cloned().collect()` on slice to create a `Vec`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.chars().last()` or
 +    /// `_.chars().next_back()` on a `str` to check if it ends with a given char.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.ends_with(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let name = "_";
 +    ///
 +    /// // Bad
 +    /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-');
 +    ///
 +    /// // Good
 +    /// name.ends_with('_') || name.ends_with('-');
 +    /// ```
 +    pub CHARS_LAST_CMP,
 +    style,
 +    "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.as_ref()` or `.as_mut()` where the
 +    /// types before and after the call are the same.
 +    ///
 +    /// ### Why is this bad?
 +    /// The call is unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn do_stuff(x: &[i32]) {}
 +    /// let x: &[i32] = &[1, 2, 3, 4, 5];
 +    /// do_stuff(x.as_ref());
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// # fn do_stuff(x: &[i32]) {}
 +    /// let x: &[i32] = &[1, 2, 3, 4, 5];
 +    /// do_stuff(x);
 +    /// ```
 +    pub USELESS_ASREF,
 +    complexity,
 +    "using `as_ref` where the types before and after the call are the same"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for using `fold` when a more succinct alternative exists.
 +    /// Specifically, this checks for `fold`s which could be replaced by `any`, `all`,
 +    /// `sum` or `product`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).fold(false, |acc, x| acc || x > 2);
 +    /// ```
 +    /// This could be written as:
 +    /// ```rust
 +    /// let _ = (0..3).any(|x| x > 2);
 +    /// ```
 +    pub UNNECESSARY_FOLD,
 +    style,
 +    "using `fold` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `filter_map` calls which could be replaced by `filter` or `map`.
 +    /// More specifically it checks if the closure provided is only performing one of the
 +    /// filter or map operations and suggests the appropriate option.
 +    ///
 +    /// ### Why is this bad?
 +    /// Complexity. The intent is also clearer if only a single
 +    /// operation is being performed.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });
 +    ///
 +    /// // As there is no transformation of the argument this could be written as:
 +    /// let _ = (0..3).filter(|&x| x > 2);
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// let _ = (0..4).filter_map(|x| Some(x + 1));
 +    ///
 +    /// // As there is no conditional check on the argument this could be written as:
 +    /// let _ = (0..4).map(|x| x + 1);
 +    /// ```
 +    pub UNNECESSARY_FILTER_MAP,
 +    complexity,
 +    "using `filter_map` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `into_iter` calls on references which should be replaced by `iter`
 +    /// or `iter_mut`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. Calling `into_iter` on a reference will not move out its
 +    /// content into the resulting iterator, which is confusing. It is better just call `iter` or
 +    /// `iter_mut` directly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let _ = (&vec![3, 4, 5]).into_iter();
 +    ///
 +    /// // Good
 +    /// let _ = (&vec![3, 4, 5]).iter();
 +    /// ```
 +    pub INTO_ITER_ON_REF,
 +    style,
 +    "using `.into_iter()` on a reference"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `map` followed by a `count`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It looks suspicious. Maybe `map` was confused with `filter`.
 +    /// If the `map` call is intentional, this should be rewritten. Or, if you intend to
 +    /// drive the iterator to completion, you can just use `for_each` instead.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).map(|x| x + 2).count();
 +    /// ```
 +    pub SUSPICIOUS_MAP,
 +    suspicious,
 +    "suspicious usage of map"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `MaybeUninit::uninit().assume_init()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// For most types, this is undefined behavior.
 +    ///
 +    /// ### Known problems
 +    /// For now, we accept empty tuples and tuples / arrays
 +    /// of `MaybeUninit`. There may be other types that allow uninitialized
 +    /// data, but those are not yet rigorously defined.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Beware the UB
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
 +    /// ```
 +    ///
 +    /// Note that the following is OK:
 +    ///
 +    /// ```rust
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: [MaybeUninit<bool>; 5] = unsafe {
 +    ///     MaybeUninit::uninit().assume_init()
 +    /// };
 +    /// ```
 +    pub UNINIT_ASSUMED_INIT,
 +    correctness,
 +    "`MaybeUninit::uninit().assume_init()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be written simply with `saturating_add/sub` methods.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.checked_add(y).unwrap_or(u32::MAX);
 +    /// let sub = x.checked_sub(y).unwrap_or(u32::MIN);
 +    /// ```
 +    ///
 +    /// can be written using dedicated methods for saturating addition/subtraction as:
 +    ///
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.saturating_add(y);
 +    /// let sub = x.saturating_sub(y);
 +    /// ```
 +    pub MANUAL_SATURATING_ARITHMETIC,
 +    style,
 +    "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
 +    /// zero-sized types
 +    ///
 +    /// ### Why is this bad?
 +    /// This is a no-op, and likely unintended
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe { (&() as *const ()).offset(1) };
 +    /// ```
 +    pub ZST_OFFSET,
 +    correctness,
 +    "Check for offset calculations on raw pointers to zero-sized types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `FileType::is_file()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// When people testing a file type with `FileType::is_file`
 +    /// they are testing whether a path is something they can get bytes from. But
 +    /// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover
 +    /// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if filetype.is_file() {
 +    ///     // read file
 +    /// }
 +    /// # Ok::<_, std::io::Error>(())
 +    /// # };
 +    /// ```
 +    ///
 +    /// should be written as:
 +    ///
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if !filetype.is_dir() {
 +    ///     // read file
 +    /// }
 +    /// # Ok::<_, std::io::Error>(())
 +    /// # };
 +    /// ```
 +    pub FILETYPE_IS_FILE,
 +    restriction,
 +    "`FileType::is_file` is not recommended to test for readable file type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.as_deref()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some("".to_string());
 +    /// opt.as_ref().map(String::as_str)
 +    /// # ;
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// # let opt = Some("".to_string());
 +    /// opt.as_deref()
 +    /// # ;
 +    /// ```
 +    pub OPTION_AS_REF_DEREF,
 +    complexity,
 +    "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `iter().next()` on a Slice or an Array
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be shortened into `.get()`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let a = [1, 2, 3];
 +    /// # let b = vec![1, 2, 3];
 +    /// a[2..].iter().next();
 +    /// b.iter().next();
 +    /// ```
 +    /// should be written as:
 +    /// ```rust
 +    /// # let a = [1, 2, 3];
 +    /// # let b = vec![1, 2, 3];
 +    /// a.get(2);
 +    /// b.get(0);
 +    /// ```
 +    pub ITER_NEXT_SLICE,
 +    style,
 +    "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns when using `push_str`/`insert_str` with a single-character string literal
 +    /// where `push`/`insert` with a `char` would work fine.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's less clear that we are pushing a single character.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut string = String::new();
 +    /// string.insert_str(0, "R");
 +    /// string.push_str("R");
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// let mut string = String::new();
 +    /// string.insert(0, 'R');
 +    /// string.push('R');
 +    /// ```
 +    pub SINGLE_CHAR_ADD_STR,
 +    style,
 +    "`push_str()` or `insert_str()` used with a single-character string literal as parameter"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// As the counterpart to `or_fun_call`, this lint looks for unnecessary
 +    /// lazily evaluated closures on `Option` and `Result`.
 +    ///
 +    /// This lint suggests changing the following functions, when eager evaluation results in
 +    /// simpler code:
 +    ///  - `unwrap_or_else` to `unwrap_or`
 +    ///  - `and_then` to `and`
 +    ///  - `or_else` to `or`
 +    ///  - `get_or_insert_with` to `get_or_insert`
 +    ///  - `ok_or_else` to `ok_or`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using eager evaluation is shorter and simpler in some cases.
 +    ///
 +    /// ### Known problems
 +    /// It is possible, but not recommended for `Deref` and `Index` to have
 +    /// side effects. Eagerly evaluating them can change the semantics of the program.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // example code where clippy issues a warning
 +    /// let opt: Option<u32> = None;
 +    ///
 +    /// opt.unwrap_or_else(|| 42);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let opt: Option<u32> = None;
 +    ///
 +    /// opt.unwrap_or(42);
 +    /// ```
 +    pub UNNECESSARY_LAZY_EVALUATIONS,
 +    style,
 +    "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map(_).collect::<Result<(), _>()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `try_for_each` instead is more readable and idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// (0..3).try_for_each(|t| Err(t));
 +    /// ```
 +    pub MAP_COLLECT_RESULT_UNIT,
 +    style,
 +    "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `from_iter()` function calls on types that implement the `FromIterator`
 +    /// trait.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is recommended style to use collect. See
 +    /// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::iter::FromIterator;
 +    ///
 +    /// let five_fives = std::iter::repeat(5).take(5);
 +    ///
 +    /// let v = Vec::from_iter(five_fives);
 +    ///
 +    /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let five_fives = std::iter::repeat(5).take(5);
 +    ///
 +    /// let v: Vec<i32> = five_fives.collect();
 +    ///
 +    /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
 +    /// ```
 +    pub FROM_ITER_INSTEAD_OF_COLLECT,
 +    pedantic,
 +    "use `.collect()` instead of `::from_iter()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `inspect().for_each()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is the same as performing the computation
 +    /// inside `inspect` at the beginning of the closure in `for_each`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// [1,2,3,4,5].iter()
 +    /// .inspect(|&x| println!("inspect the number: {}", x))
 +    /// .for_each(|&x| {
 +    ///     assert!(x >= 0);
 +    /// });
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// [1,2,3,4,5].iter()
 +    /// .for_each(|&x| {
 +    ///     println!("inspect the number: {}", x);
 +    ///     assert!(x >= 0);
 +    /// });
 +    /// ```
 +    pub INSPECT_FOR_EACH,
 +    complexity,
 +    "using `.inspect().for_each()`, which can be replaced with `.for_each()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `filter_map(|x| x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely by using `flatten`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iter = vec![Some(1)].into_iter();
 +    /// iter.filter_map(|x| x);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let iter = vec![Some(1)].into_iter();
 +    /// iter.flatten();
 +    /// ```
 +    pub FILTER_MAP_IDENTITY,
 +    complexity,
 +    "call to `filter_map` where `flatten` is sufficient"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for instances of `map(f)` where `f` is the identity function.
 +    ///
 +    /// ### Why is this bad?
 +    /// It can be written more concisely without the call to `map`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = [1, 2, 3];
 +    /// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = [1, 2, 3];
 +    /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
 +    /// ```
 +    pub MAP_IDENTITY,
 +    complexity,
 +    "using iterator.map(|x| x)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.bytes().nth()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.as_bytes().get()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let _ = "Hello".bytes().nth(3);
 +    ///
 +    /// // Good
 +    /// let _ = "Hello".as_bytes().get(3);
 +    /// ```
 +    pub BYTES_NTH,
 +    style,
 +    "replace `.bytes().nth()` with `.as_bytes().get()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.
 +    ///
 +    /// ### Why is this bad?
 +    /// These methods do the same thing as `_.clone()` but may be confusing as
 +    /// to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = vec![1, 2, 3];
 +    /// let b = a.to_vec();
 +    /// let c = a.to_owned();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = vec![1, 2, 3];
 +    /// let b = a.clone();
 +    /// let c = a.clone();
 +    /// ```
 +    pub IMPLICIT_CLONE,
 +    pedantic,
 +    "implicitly cloning a value by invoking a function on its dereferenced type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.iter().count()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.len()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let _ = some_vec.iter().count();
 +    /// let _ = &some_vec[..].iter().count();
 +    ///
 +    /// // Good
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let _ = some_vec.len();
 +    /// let _ = &some_vec[..].len();
 +    /// ```
 +    pub ITER_COUNT,
 +    complexity,
 +    "replace `.iter().count()` with `.len()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to [`splitn`]
 +    /// (https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and
 +    /// related functions with either zero or one splits.
 +    ///
 +    /// ### Why is this bad?
 +    /// These calls don't actually split the value and are
 +    /// likely to be intended as a different number.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let s = "";
 +    /// for x in s.splitn(1, ":") {
 +    ///     // use x
 +    /// }
 +    ///
 +    /// // Good
 +    /// let s = "";
 +    /// for x in s.splitn(2, ":") {
 +    ///     // use x
 +    /// }
 +    /// ```
 +    pub SUSPICIOUS_SPLITN,
 +    correctness,
 +    "checks for `.splitn(0, ..)` and `.splitn(1, ..)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for manual implementations of `str::repeat`
 +    ///
 +    /// ### Why is this bad?
 +    /// These are both harder to read, as well as less performant.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let x: String = std::iter::repeat('x').take(10).collect();
 +    ///
 +    /// // Good
 +    /// let x: String = "x".repeat(10);
 +    /// ```
 +    pub MANUAL_STR_REPEAT,
 +    perf,
 +    "manual implementation of `str::repeat`"
 +}
 +
++declare_clippy_lint! {
++    /// **What it does:** Checks for usages of `str::splitn(2, _)`
++    ///
++    /// **Why is this bad?** `split_once` is both clearer in intent and slightly more efficient.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust,ignore
++    /// // Bad
++    ///  let (key, value) = _.splitn(2, '=').next_tuple()?;
++    ///  let value = _.splitn(2, '=').nth(1)?;
++    ///
++    /// // Good
++    /// let (key, value) = _.split_once('=')?;
++    /// let value = _.split_once('=')?.1;
++    /// ```
++    pub MANUAL_SPLIT_ONCE,
++    complexity,
++    "replace `.splitn(2, pat)` with `.split_once(pat)`"
++}
++
 +pub struct Methods {
 +    avoid_breaking_exported_api: bool,
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl Methods {
 +    #[must_use]
 +    pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Self {
 +        Self {
 +            avoid_breaking_exported_api,
 +            msrv,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(Methods => [
 +    UNWRAP_USED,
 +    EXPECT_USED,
 +    SHOULD_IMPLEMENT_TRAIT,
 +    WRONG_SELF_CONVENTION,
 +    OK_EXPECT,
 +    UNWRAP_OR_ELSE_DEFAULT,
 +    MAP_UNWRAP_OR,
 +    RESULT_MAP_OR_INTO_OPTION,
 +    OPTION_MAP_OR_NONE,
 +    BIND_INSTEAD_OF_MAP,
 +    OR_FUN_CALL,
 +    EXPECT_FUN_CALL,
 +    CHARS_NEXT_CMP,
 +    CHARS_LAST_CMP,
 +    CLONE_ON_COPY,
 +    CLONE_ON_REF_PTR,
 +    CLONE_DOUBLE_REF,
 +    CLONED_INSTEAD_OF_COPIED,
 +    FLAT_MAP_OPTION,
 +    INEFFICIENT_TO_STRING,
 +    NEW_RET_NO_SELF,
 +    SINGLE_CHAR_PATTERN,
 +    SINGLE_CHAR_ADD_STR,
 +    SEARCH_IS_SOME,
 +    FILTER_NEXT,
 +    SKIP_WHILE_NEXT,
 +    FILTER_MAP_IDENTITY,
 +    MAP_IDENTITY,
 +    MANUAL_FILTER_MAP,
 +    MANUAL_FIND_MAP,
 +    OPTION_FILTER_MAP,
 +    FILTER_MAP_NEXT,
 +    FLAT_MAP_IDENTITY,
 +    MAP_FLATTEN,
 +    ITERATOR_STEP_BY_ZERO,
 +    ITER_NEXT_SLICE,
 +    ITER_COUNT,
 +    ITER_NTH,
 +    ITER_NTH_ZERO,
 +    BYTES_NTH,
 +    ITER_SKIP_NEXT,
 +    GET_UNWRAP,
 +    STRING_EXTEND_CHARS,
 +    ITER_CLONED_COLLECT,
 +    USELESS_ASREF,
 +    UNNECESSARY_FOLD,
 +    UNNECESSARY_FILTER_MAP,
 +    INTO_ITER_ON_REF,
 +    SUSPICIOUS_MAP,
 +    UNINIT_ASSUMED_INIT,
 +    MANUAL_SATURATING_ARITHMETIC,
 +    ZST_OFFSET,
 +    FILETYPE_IS_FILE,
 +    OPTION_AS_REF_DEREF,
 +    UNNECESSARY_LAZY_EVALUATIONS,
 +    MAP_COLLECT_RESULT_UNIT,
 +    FROM_ITER_INSTEAD_OF_COLLECT,
 +    INSPECT_FOR_EACH,
 +    IMPLICIT_CLONE,
 +    SUSPICIOUS_SPLITN,
 +    MANUAL_STR_REPEAT,
-             ("splitn" | "splitn_mut" | "rsplitn" | "rsplitn_mut", [count_arg, _]) => {
-                 suspicious_splitn::check(cx, name, expr, recv, count_arg);
++    EXTEND_WITH_DRAIN,
++    MANUAL_SPLIT_ONCE
 +]);
 +
 +/// Extracts a method call name, args, and `Span` of the method name.
 +fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(SymbolStr, &'tcx [hir::Expr<'tcx>], Span)> {
 +    if let ExprKind::MethodCall(path, span, args, _) = recv.kind {
 +        if !args.iter().any(|e| e.span.from_expansion()) {
 +            return Some((path.ident.name.as_str(), args, span));
 +        }
 +    }
 +    None
 +}
 +
 +/// Same as `method_call` but the `SymbolStr` is dereferenced into a temporary `&str`
 +macro_rules! method_call {
 +    ($expr:expr) => {
 +        method_call($expr)
 +            .as_ref()
 +            .map(|&(ref name, args, span)| (&**name, args, span))
 +    };
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for Methods {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if in_macro(expr.span) {
 +            return;
 +        }
 +
 +        check_methods(cx, expr, self.msrv.as_ref());
 +
 +        match expr.kind {
 +            hir::ExprKind::Call(func, args) => {
 +                from_iter_instead_of_collect::check(cx, expr, args, func);
 +            },
 +            hir::ExprKind::MethodCall(method_call, ref method_span, args, _) => {
 +                or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
 +                expect_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
 +                clone_on_copy::check(cx, expr, method_call.ident.name, args);
 +                clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args);
 +                inefficient_to_string::check(cx, expr, method_call.ident.name, args);
 +                single_char_add_str::check(cx, expr, args);
 +                into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args);
 +                single_char_pattern::check(cx, expr, method_call.ident.name, args);
 +            },
 +            hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
 +                let mut info = BinaryExprInfo {
 +                    expr,
 +                    chain: lhs,
 +                    other: rhs,
 +                    eq: op.node == hir::BinOpKind::Eq,
 +                };
 +                lint_binary_expr_with_method_call(cx, &mut info);
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    #[allow(clippy::too_many_lines)]
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
 +        if in_external_macro(cx.sess(), impl_item.span) {
 +            return;
 +        }
 +        let name = impl_item.ident.name.as_str();
 +        let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id());
 +        let item = cx.tcx.hir().expect_item(parent);
 +        let self_ty = cx.tcx.type_of(item.def_id);
 +
 +        let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }));
 +        if_chain! {
 +            if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind;
 +            if let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next();
 +
 +            let method_sig = cx.tcx.fn_sig(impl_item.def_id);
 +            let method_sig = cx.tcx.erase_late_bound_regions(method_sig);
 +
 +            let first_arg_ty = &method_sig.inputs().iter().next();
 +
 +            // check conventions w.r.t. conversion method names and predicates
 +            if let Some(first_arg_ty) = first_arg_ty;
 +
 +            then {
 +                // if this impl block implements a trait, lint in trait definition instead
 +                if !implements_trait && cx.access_levels.is_exported(impl_item.def_id) {
 +                    // check missing trait implementations
 +                    for method_config in &TRAIT_METHODS {
 +                        if name == method_config.method_name &&
 +                            sig.decl.inputs.len() == method_config.param_count &&
 +                            method_config.output_type.matches(&sig.decl.output) &&
 +                            method_config.self_kind.matches(cx, self_ty, first_arg_ty) &&
 +                            fn_header_equals(method_config.fn_header, sig.header) &&
 +                            method_config.lifetime_param_cond(impl_item)
 +                        {
 +                            span_lint_and_help(
 +                                cx,
 +                                SHOULD_IMPLEMENT_TRAIT,
 +                                impl_item.span,
 +                                &format!(
 +                                    "method `{}` can be confused for the standard trait method `{}::{}`",
 +                                    method_config.method_name,
 +                                    method_config.trait_name,
 +                                    method_config.method_name
 +                                ),
 +                                None,
 +                                &format!(
 +                                    "consider implementing the trait `{}` or choosing a less ambiguous method name",
 +                                    method_config.trait_name
 +                                )
 +                            );
 +                        }
 +                    }
 +                }
 +
 +                if sig.decl.implicit_self.has_implicit_self()
 +                    && !(self.avoid_breaking_exported_api
 +                        && cx.access_levels.is_exported(impl_item.def_id))
 +                {
 +                    wrong_self_convention::check(
 +                        cx,
 +                        &name,
 +                        self_ty,
 +                        first_arg_ty,
 +                        first_arg.pat.span,
 +                        implements_trait,
 +                        false
 +                    );
 +                }
 +            }
 +        }
 +
 +        // if this impl block implements a trait, lint in trait definition instead
 +        if implements_trait {
 +            return;
 +        }
 +
 +        if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
 +            let ret_ty = return_ty(cx, impl_item.hir_id());
 +
 +            // walk the return type and check for Self (this does not check associated types)
 +            if let Some(self_adt) = self_ty.ty_adt_def() {
 +                if contains_adt_constructor(cx.tcx, ret_ty, self_adt) {
 +                    return;
 +                }
 +            } else if contains_ty(cx.tcx, ret_ty, self_ty) {
 +                return;
 +            }
 +
 +            // if return type is impl trait, check the associated types
 +            if let ty::Opaque(def_id, _) = *ret_ty.kind() {
 +                // one of the associated types must be Self
 +                for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
 +                    if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
 +                        // walk the associated type and check for Self
 +                        if let Some(self_adt) = self_ty.ty_adt_def() {
 +                            if contains_adt_constructor(cx.tcx, projection_predicate.ty, self_adt) {
 +                                return;
 +                            }
 +                        } else if contains_ty(cx.tcx, projection_predicate.ty, self_ty) {
 +                            return;
 +                        }
 +                    }
 +                }
 +            }
 +
 +            if name == "new" && !TyS::same_type(ret_ty, self_ty) {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    impl_item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if in_external_macro(cx.tcx.sess, item.span) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let TraitItemKind::Fn(ref sig, _) = item.kind;
 +            if sig.decl.implicit_self.has_implicit_self();
 +            if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
 +
 +            then {
 +                let first_arg_span = first_arg_ty.span;
 +                let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
 +                let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty();
 +                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();
 +            if !contains_ty(cx.tcx, ret_ty, self_ty);
 +
 +            then {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) {
 +    if let Some((name, [recv, args @ ..], span)) = method_call!(expr) {
 +        match (name, args) {
 +            ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
 +                zst_offset::check(cx, expr, recv);
 +            },
 +            ("and_then", [arg]) => {
 +                let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg);
 +                let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg);
 +                if !biom_option_linted && !biom_result_linted {
 +                    unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
 +                }
 +            },
 +            ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
 +            ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
 +            ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
 +            ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv),
 +            ("collect", []) => match method_call!(recv) {
 +                Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2),
 +                Some(("map", [m_recv, m_arg], _)) => {
 +                    map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv);
 +                },
 +                Some(("take", [take_self_arg, take_arg], _)) => {
 +                    if meets_msrv(msrv, &msrvs::STR_REPEAT) {
 +                        manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
 +                    }
 +                },
 +                _ => {},
 +            },
 +            ("count", []) => match method_call!(recv) {
 +                Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
 +                    iter_count::check(cx, expr, recv2, name);
 +                },
 +                Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
 +                _ => {},
 +            },
 +            ("expect", [_]) => match method_call!(recv) {
 +                Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
 +                _ => expect_used::check(cx, expr, recv),
 +            },
 +            ("extend", [arg]) => {
 +                string_extend_chars::check(cx, expr, recv, arg);
 +                extend_with_drain::check(cx, expr, recv, arg);
 +            },
 +            ("filter_map", [arg]) => {
 +                unnecessary_filter_map::check(cx, expr, arg);
 +                filter_map_identity::check(cx, expr, arg, span);
 +            },
 +            ("flat_map", [arg]) => {
 +                flat_map_identity::check(cx, expr, arg, span);
 +                flat_map_option::check(cx, expr, arg, span);
 +            },
 +            ("flatten", []) => {
 +                if let Some(("map", [recv, map_arg], _)) = method_call!(recv) {
 +                    map_flatten::check(cx, expr, recv, map_arg);
 +                }
 +            },
 +            ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
 +            ("for_each", [_]) => {
 +                if let Some(("inspect", [_, _], span2)) = method_call!(recv) {
 +                    inspect_for_each::check(cx, expr, span2);
 +                }
 +            },
 +            ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
 +            ("is_file", []) => filetype_is_file::check(cx, expr, recv),
 +            ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
 +            ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
 +            ("map", [m_arg]) => {
 +                if let Some((name, [recv2, args @ ..], span2)) = method_call!(recv) {
 +                    match (name, args) {
 +                        ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv),
 +                        ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv),
 +                        ("filter", [f_arg]) => {
 +                            filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
 +                        },
 +                        ("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true),
 +                        _ => {},
 +                    }
 +                }
 +                map_identity::check(cx, expr, recv, m_arg, span);
 +            },
 +            ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
 +            ("next", []) => {
 +                if let Some((name, [recv, args @ ..], _)) = method_call!(recv) {
 +                    match (name, args) {
 +                        ("filter", [arg]) => filter_next::check(cx, expr, recv, arg),
 +                        ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv),
 +                        ("iter", []) => iter_next_slice::check(cx, expr, recv),
 +                        ("skip", [arg]) => iter_skip_next::check(cx, expr, recv, arg),
 +                        ("skip_while", [_]) => skip_while_next::check(cx, expr),
 +                        _ => {},
 +                    }
 +                }
 +            },
 +            ("nth", [n_arg]) => match method_call!(recv) {
 +                Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
 +                Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
 +                Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
 +                _ => iter_nth_zero::check(cx, expr, recv, n_arg),
 +            },
 +            ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
 +            ("or_else", [arg]) => {
 +                if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
 +                    unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
 +                }
 +            },
++            ("splitn" | "rsplitn", [count_arg, pat_arg]) => {
++                if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
++                    suspicious_splitn::check(cx, name, expr, recv, count);
++                    if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) {
++                        manual_split_once::check(cx, name, expr, recv, pat_arg);
++                    }
++                }
++            },
++            ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
++                if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
++                    suspicious_splitn::check(cx, name, expr, recv, count);
++                }
 +            },
 +            ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
 +            ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
 +                implicit_clone::check(cx, name, expr, recv, span);
 +            },
 +            ("unwrap", []) => match method_call!(recv) {
 +                Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false),
 +                Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true),
 +                _ => unwrap_used::check(cx, expr, recv),
 +            },
 +            ("unwrap_or", [u_arg]) => match method_call!(recv) {
 +                Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
 +                    manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
 +                },
 +                Some(("map", [m_recv, m_arg], span)) => {
 +                    option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
 +                },
 +                _ => {},
 +            },
 +            ("unwrap_or_else", [u_arg]) => match method_call!(recv) {
 +                Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {},
 +                _ => {
 +                    unwrap_or_else_default::check(cx, expr, recv, u_arg);
 +                    unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
 +                },
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
 +    if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call!(recv) {
 +        search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
 +    }
 +}
 +
 +/// Used for `lint_binary_expr_with_method_call`.
 +#[derive(Copy, Clone)]
 +struct BinaryExprInfo<'a> {
 +    expr: &'a hir::Expr<'a>,
 +    chain: &'a hir::Expr<'a>,
 +    other: &'a hir::Expr<'a>,
 +    eq: bool,
 +}
 +
 +/// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
 +fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) {
 +    macro_rules! lint_with_both_lhs_and_rhs {
 +        ($func:expr, $cx:expr, $info:ident) => {
 +            if !$func($cx, $info) {
 +                ::std::mem::swap(&mut $info.chain, &mut $info.other);
 +                if $func($cx, $info) {
 +                    return;
 +                }
 +            }
 +        };
 +    }
 +
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info);
 +}
 +
 +const FN_HEADER: hir::FnHeader = hir::FnHeader {
 +    unsafety: hir::Unsafety::Normal,
 +    constness: hir::Constness::NotConst,
 +    asyncness: hir::IsAsync::NotAsync,
 +    abi: rustc_target::spec::abi::Abi::Rust,
 +};
 +
 +struct ShouldImplTraitCase {
 +    trait_name: &'static str,
 +    method_name: &'static str,
 +    param_count: usize,
 +    fn_header: hir::FnHeader,
 +    // implicit self kind expected (none, self, &self, ...)
 +    self_kind: SelfKind,
 +    // checks against the output type
 +    output_type: OutType,
 +    // certain methods with explicit lifetimes can't implement the equivalent trait method
 +    lint_explicit_lifetime: bool,
 +}
 +impl ShouldImplTraitCase {
 +    const fn new(
 +        trait_name: &'static str,
 +        method_name: &'static str,
 +        param_count: usize,
 +        fn_header: hir::FnHeader,
 +        self_kind: SelfKind,
 +        output_type: OutType,
 +        lint_explicit_lifetime: bool,
 +    ) -> ShouldImplTraitCase {
 +        ShouldImplTraitCase {
 +            trait_name,
 +            method_name,
 +            param_count,
 +            fn_header,
 +            self_kind,
 +            output_type,
 +            lint_explicit_lifetime,
 +        }
 +    }
 +
 +    fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool {
 +        self.lint_explicit_lifetime
 +            || !impl_item.generics.params.iter().any(|p| {
 +                matches!(
 +                    p.kind,
 +                    hir::GenericParamKind::Lifetime {
 +                        kind: hir::LifetimeParamKind::Explicit
 +                    }
 +                )
 +            })
 +    }
 +}
 +
 +#[rustfmt::skip]
 +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
 +    ShouldImplTraitCase::new("std::ops::Add", "add",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::convert::AsMut", "as_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::convert::AsRef", "as_ref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::BitAnd", "bitand",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitOr", "bitor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitXor", "bitxor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::borrow::Borrow", "borrow",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::clone::Clone", "clone",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::cmp::Ord", "cmp",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    // FIXME: default doesn't work
 +    ShouldImplTraitCase::new("std::default::Default", "default",  0,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Deref", "deref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::Div", "div",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Drop", "drop",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Unit, true),
 +    ShouldImplTraitCase::new("std::cmp::PartialEq", "eq",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Bool, true),
 +    ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::str::FromStr", "from_str",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::hash::Hash", "hash",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Unit, true),
 +    ShouldImplTraitCase::new("std::ops::Index", "index",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut",  2,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Mul", "mul",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Neg", "neg",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::iter::Iterator", "next",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Any, false),
 +    ShouldImplTraitCase::new("std::ops::Not", "not",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Rem", "rem",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Shl", "shl",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Shr", "shr",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Sub", "sub",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +];
 +
 +#[derive(Clone, Copy, PartialEq, Debug)]
 +enum SelfKind {
 +    Value,
 +    Ref,
 +    RefMut,
 +    No,
 +}
 +
 +impl SelfKind {
 +    fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +        fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'_>, ty: Ty<'_>) -> bool {
 +            if ty == parent_ty {
 +                true
 +            } else if ty.is_box() {
 +                ty.boxed_ty() == parent_ty
 +            } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
 +                if let ty::Adt(_, substs) = ty.kind() {
 +                    substs.types().next().map_or(false, |t| t == parent_ty)
 +                } else {
 +                    false
 +                }
 +            } else {
 +                false
 +            }
 +        }
 +
 +        fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +            if let ty::Ref(_, t, m) = *ty.kind() {
 +                return m == mutability && t == parent_ty;
 +            }
 +
 +            let trait_path = match mutability {
 +                hir::Mutability::Not => &paths::ASREF_TRAIT,
 +                hir::Mutability::Mut => &paths::ASMUT_TRAIT,
 +            };
 +
 +            let trait_def_id = match get_trait_def_id(cx, trait_path) {
 +                Some(did) => did,
 +                None => return false,
 +            };
 +            implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
 +        }
 +
 +        match self {
 +            Self::Value => matches_value(cx, parent_ty, ty),
 +            Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty),
 +            Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty),
 +            Self::No => ty != parent_ty,
 +        }
 +    }
 +
 +    #[must_use]
 +    fn description(self) -> &'static str {
 +        match self {
 +            Self::Value => "`self` by value",
 +            Self::Ref => "`self` by reference",
 +            Self::RefMut => "`self` by mutable reference",
 +            Self::No => "no `self`",
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +enum OutType {
 +    Unit,
 +    Bool,
 +    Any,
 +    Ref,
 +}
 +
 +impl OutType {
 +    fn matches(self, ty: &hir::FnRetTy<'_>) -> bool {
 +        let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[]));
 +        match (self, ty) {
 +            (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true,
 +            (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true,
 +            (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true,
 +            (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true,
 +            (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)),
 +            _ => false,
 +        }
 +    }
 +}
 +
 +fn is_bool(ty: &hir::Ty<'_>) -> bool {
 +    if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
 +        matches!(path.res, Res::PrimTy(PrimTy::Bool))
 +    } else {
 +        false
 +    }
 +}
 +
 +fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
 +    expected.constness == actual.constness
 +        && expected.unsafety == actual.unsafety
 +        && expected.asyncness == actual.asyncness
 +}
index c1d22e5d72c138d3a9c216eb571686d3a672e4f0,0000000000000000000000000000000000000000..30ed1d665a907d1cf1bb8d4ea3208a0a48023e5b
mode 100644,000000..100644
--- /dev/null
@@@ -1,193 -1,0 +1,193 @@@
-         if let hir::ExprKind::MethodCall(path, _, args, _) = &arg.kind {
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::eager_or_lazy::is_lazyness_candidate;
 +use clippy_utils::is_trait_item;
 +use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_macro_callsite};
 +use clippy_utils::ty::implements_trait;
 +use clippy_utils::ty::{is_type_diagnostic_item, match_type};
 +use clippy_utils::{contains_return, last_path_segment, paths};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::{BlockCheckMode, UnsafeSource};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +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())`.
 +    fn check_unwrap_or_default(
 +        cx: &LateContext<'_>,
 +        name: &str,
 +        fun: &hir::Expr<'_>,
 +        self_expr: &hir::Expr<'_>,
 +        arg: &hir::Expr<'_>,
 +        or_has_args: bool,
 +        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 {
 +                let mut applicability = Applicability::MachineApplicable;
 +                span_lint_and_sugg(
 +                    cx,
 +                    OR_FUN_CALL,
 +                    span,
 +                    &format!("use of `{}` followed by a call to `{}`", name, path),
 +                    "try this",
 +                    format!(
 +                        "{}.unwrap_or_default()",
 +                        snippet_with_applicability(cx, self_expr.span, "..", &mut applicability)
 +                    ),
 +                    applicability,
 +                );
 +
 +                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"),
 +        ];
 +
-                 let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
++        if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &arg.kind {
 +            if path.ident.name == sym::len {
++                let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
 +
 +                match ty.kind() {
 +                    ty::Slice(_) | ty::Array(_, _) | ty::Str => return,
 +                    _ => (),
 +                }
 +
 +                if is_type_diagnostic_item(cx, ty, sym::vec_type) {
 +                    return;
 +                }
 +            }
 +        }
 +
 +        if_chain! {
 +            if KNOW_TYPES.iter().any(|k| k.2.contains(&name));
 +
 +            if is_lazyness_candidate(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 args.len() == 2 {
 +        match args[1].kind {
 +            hir::ExprKind::Call(fun, or_args) => {
 +                let or_has_args = !or_args.is_empty();
 +                if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) {
 +                    let fun_span = if or_has_args { None } else { Some(fun.span) };
 +                    check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, fun_span);
 +                }
 +            },
 +            hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
 +                check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
 +            },
 +            hir::ExprKind::Block(block, _) => {
 +                if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules {
 +                    if let Some(block_expr) = block.expr {
 +                        if let hir::ExprKind::MethodCall(..) = block_expr.kind {
 +                            check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
 +                        }
 +                    }
 +                }
 +            },
 +            _ => (),
 +        }
 +    }
 +}
index a271df60572a22f0969910f22a5bc1c1828dabca,0000000000000000000000000000000000000000..1c546a15bf62b331a5993e378aee11e7c832dc72
mode 100644,000000..100644
--- /dev/null
@@@ -1,56 -1,0 +1,48 @@@
- use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::span_lint_and_note;
 +use if_chain::if_chain;
 +use rustc_ast::LitKind;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::LateContext;
 +use rustc_span::source_map::Spanned;
 +
 +use super::SUSPICIOUS_SPLITN;
 +
- pub(super) fn check(
-     cx: &LateContext<'_>,
-     method_name: &str,
-     expr: &Expr<'_>,
-     self_arg: &Expr<'_>,
-     count_arg: &Expr<'_>,
- ) {
++pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, count: u128) {
 +    if_chain! {
-         if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg);
 +        if count <= 1;
 +        if let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
 +        if let Some(impl_id) = cx.tcx.impl_of_method(call_id);
 +        let lang_items = cx.tcx.lang_items();
 +        if lang_items.slice_impl() == Some(impl_id) || lang_items.str_impl() == Some(impl_id);
 +        then {
 +            // Ignore empty slice and string literals when used with a literal count.
-             if (matches!(self_arg.kind, ExprKind::Array([]))
++            if matches!(self_arg.kind, ExprKind::Array([]))
 +                || matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty())
-             ) && matches!(count_arg.kind, ExprKind::Lit(_))
++
 +            {
 +                return;
 +            }
 +
 +            let (msg, note_msg) = if count == 0 {
 +                (format!("`{}` called with `0` splits", method_name),
 +                "the resulting iterator will always return `None`")
 +            } else {
 +                (format!("`{}` called with `1` split", method_name),
 +                if lang_items.slice_impl() == Some(impl_id) {
 +                    "the resulting iterator will always return the entire slice followed by `None`"
 +                } else {
 +                    "the resulting iterator will always return the entire string followed by `None`"
 +                })
 +            };
 +
 +            span_lint_and_note(
 +                cx,
 +                SUSPICIOUS_SPLITN,
 +                expr.span,
 +                &msg,
 +                None,
 +                note_msg,
 +            );
 +        }
 +    }
 +}
index 0daea47816a512137265160cc7773706380b4b44,0000000000000000000000000000000000000000..30d6665a920ba3e7d18adc58f98583af8f57e87f
mode 100644,000000..100644
--- /dev/null
@@@ -1,75 -1,0 +1,75 @@@
-     if let hir::ExprKind::MethodCall(path, _, args, _) = expr.kind {
-         if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) {
-             Some(&args[0])
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_span::symbol::sym;
 +
 +pub(super) fn derefs_to_slice<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx hir::Expr<'tcx>,
 +    ty: Ty<'tcx>,
 +) -> Option<&'tcx hir::Expr<'tcx>> {
 +    fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool {
 +        match ty.kind() {
 +            ty::Slice(_) => true,
 +            ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
 +            ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type),
 +            ty::Array(_, size) => size.try_eval_usize(cx.tcx, cx.param_env).is_some(),
 +            ty::Ref(_, inner, _) => may_slice(cx, inner),
 +            _ => false,
 +        }
 +    }
 +
++    if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind {
++        if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(self_arg)) {
++            Some(self_arg)
 +        } else {
 +            None
 +        }
 +    } else {
 +        match ty.kind() {
 +            ty::Slice(_) => Some(expr),
 +            ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr),
 +            ty::Ref(_, inner, _) => {
 +                if may_slice(cx, inner) {
 +                    Some(expr)
 +                } else {
 +                    None
 +                }
 +            },
 +            _ => None,
 +        }
 +    }
 +}
 +
 +pub(super) fn get_hint_if_single_char_arg(
 +    cx: &LateContext<'_>,
 +    arg: &hir::Expr<'_>,
 +    applicability: &mut Applicability,
 +) -> Option<String> {
 +    if_chain! {
 +        if let hir::ExprKind::Lit(lit) = &arg.kind;
 +        if let ast::LitKind::Str(r, style) = lit.node;
 +        let string = r.as_str();
 +        if string.chars().count() == 1;
 +        then {
 +            let snip = snippet_with_applicability(cx, arg.span, &string, applicability);
 +            let ch = if let ast::StrStyle::Raw(nhash) = style {
 +                let nhash = nhash as usize;
 +                // for raw string: r##"a"##
 +                &snip[(nhash + 2)..(snip.len() - 1 - nhash)]
 +            } else {
 +                // for regular string: "a"
 +                &snip[1..(snip.len() - 1)]
 +            };
 +            let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch });
 +            Some(hint)
 +        } else {
 +            None
 +        }
 +    }
 +}
index c796abe9815a4c2dd3c2b4538c42ae99149e2992,0000000000000000000000000000000000000000..538fa4e1678fc348b90da7ca3aaf22fb6ca5fc5a
mode 100644,000000..100644
--- /dev/null
@@@ -1,779 -1,0 +1,779 @@@
-         if let ExprKind::MethodCall(method_name, _, expressions, _) = expr.kind;
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
 +use clippy_utils::source::{snippet, snippet_opt};
 +use clippy_utils::ty::implements_trait;
 +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, UnOp,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::hygiene::DesugaringKind;
 +use rustc_span::source_map::{ExpnKind, Span};
 +use rustc_span::symbol::sym;
 +
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::{
 +    expr_path_res, get_item_name, get_parent_expr, higher, in_constant, is_diag_trait_item, is_integer_const,
 +    iter_input_pats, last_path_segment, match_any_def_paths, paths, unsext, 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,ignore
 +    /// // Bad
 +    /// fn foo(ref x: u8) -> bool {
 +    ///     true
 +    /// }
 +    ///
 +    /// // Good
 +    /// fn foo(x: &u8) -> bool {
 +    ///     true
 +    /// }
 +    /// ```
 +    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 comparisons to NaN.
 +    ///
 +    /// ### Why is this bad?
 +    /// NaN does not compare meaningfully to anything – not
 +    /// even itself – so those comparisons are simply wrong.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 1.0;
 +    ///
 +    /// // Bad
 +    /// if x == f32::NAN { }
 +    ///
 +    /// // Good
 +    /// if x.is_nan() { }
 +    /// ```
 +    pub CMP_NAN,
 +    correctness,
 +    "comparisons to `NAN`, which will always return false, probably not intended"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for (in-)equality comparisons on floating-point
 +    /// values (apart from zero), except in functions called `*eq*` (which probably
 +    /// implement equality for a type involving floats).
 +    ///
 +    /// ### Why is this bad?
 +    /// Floating point calculations are usually imprecise, so
 +    /// asking if two values are *exactly* equal is asking for trouble. For a good
 +    /// guide on what to do, see [the floating point
 +    /// guide](http://www.floating-point-gui.de/errors/comparison).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 1.2331f64;
 +    /// let y = 1.2332f64;
 +    ///
 +    /// // Bad
 +    /// if y == 1.23f64 { }
 +    /// if y != x {} // where both are floats
 +    ///
 +    /// // Good
 +    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
 +    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
 +    /// // let error_margin = std::f64::EPSILON;
 +    /// if (y - 1.23f64).abs() < error_margin { }
 +    /// if (y - x).abs() > error_margin { }
 +    /// ```
 +    pub FLOAT_CMP,
 +    correctness,
 +    "using `==` or `!=` on float values instead of comparing difference with an epsilon"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for conversions to owned values just for the sake
 +    /// of a comparison.
 +    ///
 +    /// ### Why is this bad?
 +    /// The comparison can operate on a reference, so creating
 +    /// an owned value effectively throws it away directly afterwards, which is
 +    /// needlessly consuming code and heap space.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = "foo";
 +    /// # let y = String::from("foo");
 +    /// if x.to_owned() == y {}
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// # let x = "foo";
 +    /// # let y = String::from("foo");
 +    /// if x == y {}
 +    /// ```
 +    pub CMP_OWNED,
 +    perf,
 +    "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for getting the remainder of a division by one or minus
 +    /// one.
 +    ///
 +    /// ### Why is this bad?
 +    /// The result for a divisor of one can only ever be zero; for
 +    /// minus one it can cause panic/overflow (if the left operand is the minimal value of
 +    /// the respective integer type) or results in zero. No one will write such code
 +    /// deliberately, unless trying to win an Underhanded Rust Contest. Even for that
 +    /// contest, it's probably a bad idea. Use something more underhanded.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 1;
 +    /// let a = x % 1;
 +    /// let a = x % -1;
 +    /// ```
 +    pub MODULO_ONE,
 +    correctness,
 +    "taking a number modulo +/-1, which can either panic/overflow or always returns 0"
 +}
 +
 +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`
 +    /// ```
 +    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(); }`.
 +    /// ```
 +    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
 +    /// // Bad
 +    /// let a = 0 as *const u32;
 +    ///
 +    /// // Good
 +    /// let a = std::ptr::null::<u32>();
 +    /// ```
 +    pub ZERO_PTR,
 +    style,
 +    "using `0 as *{const, mut} T`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for (in-)equality comparisons on floating-point
 +    /// value and constant, except in functions called `*eq*` (which probably
 +    /// implement equality for a type involving floats).
 +    ///
 +    /// ### Why is this bad?
 +    /// Floating point calculations are usually imprecise, so
 +    /// asking if two values are *exactly* equal is asking for trouble. For a good
 +    /// guide on what to do, see [the floating point
 +    /// guide](http://www.floating-point-gui.de/errors/comparison).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: f64 = 1.0;
 +    /// const ONE: f64 = 1.00;
 +    ///
 +    /// // Bad
 +    /// if x == ONE { } // where both are floats
 +    ///
 +    /// // Good
 +    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
 +    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
 +    /// // let error_margin = std::f64::EPSILON;
 +    /// if (x - ONE).abs() < error_margin { }
 +    /// ```
 +    pub FLOAT_CMP_CONST,
 +    restriction,
 +    "using `==` or `!=` on float constants instead of comparing difference with an epsilon"
 +}
 +
 +declare_lint_pass!(MiscLints => [
 +    TOPLEVEL_REF_ARG,
 +    CMP_NAN,
 +    FLOAT_CMP,
 +    CMP_OWNED,
 +    MODULO_ONE,
 +    USED_UNDERSCORE_BINDING,
 +    SHORT_CIRCUIT_STATEMENT,
 +    ZERO_PTR,
 +    FLOAT_CMP_CONST
 +]);
 +
 +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 !higher::is_from_for_desugar(local);
 +            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<'_>) {
 +        match expr.kind {
 +            ExprKind::Cast(e, ty) => {
 +                check_cast(cx, expr.span, e, ty);
 +                return;
 +            },
 +            ExprKind::Binary(ref cmp, left, right) => {
 +                check_binary(cx, expr, cmp, left, right);
 +                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 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) => {
 +                let name = ident.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
 +                ),
 +            );
 +        }
 +    }
 +}
 +
 +fn get_lint_and_message(
 +    is_comparing_constants: bool,
 +    is_comparing_arrays: bool,
 +) -> (&'static rustc_lint::Lint, &'static str) {
 +    if is_comparing_constants {
 +        (
 +            FLOAT_CMP_CONST,
 +            if is_comparing_arrays {
 +                "strict comparison of `f32` or `f64` constant arrays"
 +            } else {
 +                "strict comparison of `f32` or `f64` constant"
 +            },
 +        )
 +    } else {
 +        (
 +            FLOAT_CMP,
 +            if is_comparing_arrays {
 +                "strict comparison of `f32` or `f64` arrays"
 +            } else {
 +                "strict comparison of `f32` or `f64`"
 +            },
 +        )
 +    }
 +}
 +
 +fn check_nan(cx: &LateContext<'_>, expr: &Expr<'_>, cmp_expr: &Expr<'_>) {
 +    if_chain! {
 +        if !in_constant(cx, cmp_expr.hir_id);
 +        if let Some((value, _)) = constant(cx, cx.typeck_results(), expr);
 +        if match value {
 +            Constant::F32(num) => num.is_nan(),
 +            Constant::F64(num) => num.is_nan(),
 +            _ => false,
 +        };
 +        then {
 +            span_lint(
 +                cx,
 +                CMP_NAN,
 +                cmp_expr.span,
 +                "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead",
 +            );
 +        }
 +    }
 +}
 +
 +fn is_named_constant<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
 +    if let Some((_, res)) = constant(cx, cx.typeck_results(), expr) {
 +        res
 +    } else {
 +        false
 +    }
 +}
 +
 +fn is_allowed<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
 +    match constant(cx, cx.typeck_results(), expr) {
 +        Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(),
 +        Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(),
 +        Some((Constant::Vec(vec), _)) => vec.iter().all(|f| match f {
 +            Constant::F32(f) => *f == 0.0 || (*f).is_infinite(),
 +            Constant::F64(f) => *f == 0.0 || (*f).is_infinite(),
 +            _ => false,
 +        }),
 +        _ => false,
 +    }
 +}
 +
 +// Return true if `expr` is the result of `signum()` invoked on a float value.
 +fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    // The negation of a signum is still a signum
 +    if let ExprKind::Unary(UnOp::Neg, child_expr) = expr.kind {
 +        return is_signum(cx, child_expr);
 +    }
 +
 +    if_chain! {
-             return is_float(cx, &expressions[0]);
++        if let ExprKind::MethodCall(method_name, _, [ref self_arg, ..], _) = expr.kind;
 +        if sym!(signum) == method_name.ident.name;
 +        // Check that the receiver of the signum() is a float (expressions[0] is the receiver of
 +        // the method call)
 +        then {
++            return is_float(cx, self_arg);
 +        }
 +    }
 +    false
 +}
 +
 +fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    let value = &cx.typeck_results().expr_ty(expr).peel_refs().kind();
 +
 +    if let ty::Array(arr_ty, _) = value {
 +        return matches!(arr_ty.kind(), ty::Float(_));
 +    };
 +
 +    matches!(value, ty::Float(_))
 +}
 +
 +fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
 +}
 +
 +fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
 +    #[derive(Default)]
 +    struct EqImpl {
 +        ty_eq_other: bool,
 +        other_eq_ty: bool,
 +    }
 +
 +    impl EqImpl {
 +        fn is_implemented(&self) -> bool {
 +            self.ty_eq_other || self.other_eq_ty
 +        }
 +    }
 +
 +    fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> Option<EqImpl> {
 +        cx.tcx.lang_items().eq_trait().map(|def_id| EqImpl {
 +            ty_eq_other: implements_trait(cx, ty, def_id, &[other.into()]),
 +            other_eq_ty: implements_trait(cx, other, def_id, &[ty.into()]),
 +        })
 +    }
 +
 +    let (arg_ty, snip) = match expr.kind {
 +        ExprKind::MethodCall(.., args, _) if args.len() == 1 => {
 +            if_chain!(
 +                if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
 +                if is_diag_trait_item(cx, expr_def_id, sym::ToString)
 +                    || is_diag_trait_item(cx, expr_def_id, sym::ToOwned);
 +                then {
 +                    (cx.typeck_results().expr_ty(&args[0]), snippet(cx, args[0].span, ".."))
 +                } else {
 +                    return;
 +                }
 +            )
 +        },
 +        ExprKind::Call(path, [arg]) => {
 +            if expr_path_res(cx, path)
 +                .opt_def_id()
 +                .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
 +                .is_some()
 +            {
 +                (cx.typeck_results().expr_ty(arg), snippet(cx, arg.span, ".."))
 +            } else {
 +                return;
 +            }
 +        },
 +        _ => return,
 +    };
 +
 +    let other_ty = cx.typeck_results().expr_ty(other);
 +
 +    let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default();
 +    let with_deref = arg_ty
 +        .builtin_deref(true)
 +        .and_then(|tam| symmetric_partial_eq(cx, tam.ty, other_ty))
 +        .unwrap_or_default();
 +
 +    if !with_deref.is_implemented() && !without_deref.is_implemented() {
 +        return;
 +    }
 +
 +    let other_gets_derefed = matches!(other.kind, ExprKind::Unary(UnOp::Deref, _));
 +
 +    let lint_span = if other_gets_derefed {
 +        expr.span.to(other.span)
 +    } else {
 +        expr.span
 +    };
 +
 +    span_lint_and_then(
 +        cx,
 +        CMP_OWNED,
 +        lint_span,
 +        "this creates an owned instance just for comparison",
 +        |diag| {
 +            // This also catches `PartialEq` implementations that call `to_owned`.
 +            if other_gets_derefed {
 +                diag.span_label(lint_span, "try implementing the comparison without allocating");
 +                return;
 +            }
 +
 +            let expr_snip;
 +            let eq_impl;
 +            if with_deref.is_implemented() {
 +                expr_snip = format!("*{}", snip);
 +                eq_impl = with_deref;
 +            } else {
 +                expr_snip = snip.to_string();
 +                eq_impl = without_deref;
 +            };
 +
 +            let span;
 +            let hint;
 +            if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) {
 +                span = expr.span;
 +                hint = expr_snip;
 +            } else {
 +                span = expr.span.to(other.span);
 +                if eq_impl.ty_eq_other {
 +                    hint = format!("{} == {}", expr_snip, snippet(cx, other.span, ".."));
 +                } else {
 +                    hint = format!("{} == {}", snippet(cx, other.span, ".."), expr_snip);
 +                }
 +            }
 +
 +            diag.span_suggestion(
 +                span,
 +                "try",
 +                hint,
 +                Applicability::MachineApplicable, // snippet
 +            );
 +        },
 +    );
 +}
 +
 +/// 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, _))
 +    } 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);
 +        }
 +    }
 +}
 +
 +fn check_binary(
 +    cx: &LateContext<'a>,
 +    expr: &Expr<'_>,
 +    cmp: &rustc_span::source_map::Spanned<rustc_hir::BinOpKind>,
 +    left: &'a Expr<'_>,
 +    right: &'a Expr<'_>,
 +) {
 +    let op = cmp.node;
 +    if op.is_comparison() {
 +        check_nan(cx, left, expr);
 +        check_nan(cx, right, expr);
 +        check_to_owned(cx, left, right, true);
 +        check_to_owned(cx, right, left, false);
 +    }
 +    if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
 +        if is_allowed(cx, left) || is_allowed(cx, right) {
 +            return;
 +        }
 +
 +        // Allow comparing the results of signum()
 +        if is_signum(cx, left) && is_signum(cx, right) {
 +            return;
 +        }
 +
 +        if let Some(name) = get_item_name(cx, expr) {
 +            let name = name.as_str();
 +            if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") {
 +                return;
 +            }
 +        }
 +        let is_comparing_arrays = is_array(cx, left) || is_array(cx, right);
 +        let (lint, msg) = get_lint_and_message(
 +            is_named_constant(cx, left) || is_named_constant(cx, right),
 +            is_comparing_arrays,
 +        );
 +        span_lint_and_then(cx, lint, expr.span, msg, |diag| {
 +            let lhs = Sugg::hir(cx, left, "..");
 +            let rhs = Sugg::hir(cx, right, "..");
 +
 +            if !is_comparing_arrays {
 +                diag.span_suggestion(
 +                    expr.span,
 +                    "consider comparing them within some margin of error",
 +                    format!(
 +                        "({}).abs() {} error_margin",
 +                        lhs - rhs,
 +                        if op == BinOpKind::Eq { '<' } else { '>' }
 +                    ),
 +                    Applicability::HasPlaceholders, // snippet
 +                );
 +            }
 +            diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
 +        });
 +    } else if op == BinOpKind::Rem {
 +        if is_integer_const(cx, right, 1) {
 +            span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
 +        }
 +
 +        if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() {
 +            if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) {
 +                span_lint(
 +                    cx,
 +                    MODULO_ONE,
 +                    expr.span,
 +                    "any number modulo -1 will panic/overflow or result in 0",
 +                );
 +            }
 +        };
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..80a930d0c547b368460ba8e0e965261b1f7da7d9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,178 @@@
++use std::{
++    ffi::OsString,
++    path::{Component, Path},
++};
++
++use rustc_ast::ast;
++use rustc_data_structures::fx::{FxHashMap, FxHashSet};
++use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::{FileName, RealFileName, SourceFile, Span, SyntaxContext};
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks that module layout uses only self named module files, bans mod.rs files.
++    ///
++    /// ### Why is this bad?
++    /// Having multiple module layout styles in a project can be confusing.
++    ///
++    /// ### Example
++    /// ```text
++    /// src/
++    ///   stuff/
++    ///     stuff_files.rs
++    ///     mod.rs
++    ///   lib.rs
++    /// ```
++    /// Use instead:
++    /// ```text
++    /// src/
++    ///   stuff/
++    ///     stuff_files.rs
++    ///   stuff.rs
++    ///   lib.rs
++    /// ```
++    pub MOD_MODULE_FILES,
++    restriction,
++    "checks that module layout is consistent"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks that module layout uses only mod.rs files.
++    ///
++    /// ### Why is this bad?
++    /// Having multiple module layout styles in a project can be confusing.
++    ///
++    /// ### Example
++    /// ```text
++    /// src/
++    ///   stuff/
++    ///     stuff_files.rs
++    ///   stuff.rs
++    ///   lib.rs
++    /// ```
++    /// Use instead:
++    /// ```text
++    /// src/
++    ///   stuff/
++    ///     stuff_files.rs
++    ///     mod.rs
++    ///   lib.rs
++    /// ```
++
++    pub SELF_NAMED_MODULE_FILES,
++    restriction,
++    "checks that module layout is consistent"
++}
++
++pub struct ModStyle;
++
++impl_lint_pass!(ModStyle => [MOD_MODULE_FILES, SELF_NAMED_MODULE_FILES]);
++
++impl EarlyLintPass for ModStyle {
++    fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
++        if cx.builder.lint_level(MOD_MODULE_FILES).0 == Level::Allow
++            && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).0 == Level::Allow
++        {
++            return;
++        }
++
++        let files = cx.sess.source_map().files();
++
++        let trim_to_src = if let RealFileName::LocalPath(p) = &cx.sess.opts.working_dir {
++            p.to_string_lossy()
++        } else {
++            return;
++        };
++
++        // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives
++        // `[path, to]` but not foo
++        let mut folder_segments = FxHashSet::default();
++        // `mod_folders` is all the unique folder names that contain a mod.rs file
++        let mut mod_folders = FxHashSet::default();
++        // `file_map` maps file names to the full path including the file name
++        // `{ foo => path/to/foo.rs, .. }
++        let mut file_map = FxHashMap::default();
++        for file in files.iter() {
++            match &file.name {
++                FileName::Real(RealFileName::LocalPath(lp))
++                    if lp.to_string_lossy().starts_with(trim_to_src.as_ref()) =>
++                {
++                    let p = lp.to_string_lossy();
++                    let path = Path::new(p.trim_start_matches(trim_to_src.as_ref()));
++                    if let Some(stem) = path.file_stem() {
++                        file_map.insert(stem.to_os_string(), (file, path.to_owned()));
++                    }
++                    process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders);
++                    check_self_named_mod_exists(cx, path, file);
++                }
++                _ => {},
++            }
++        }
++
++        for folder in &folder_segments {
++            if !mod_folders.contains(folder) {
++                if let Some((file, path)) = file_map.get(folder) {
++                    let mut correct = path.clone();
++                    correct.pop();
++                    correct.push(folder);
++                    correct.push("mod.rs");
++                    cx.struct_span_lint(
++                        SELF_NAMED_MODULE_FILES,
++                        Span::new(file.start_pos, file.start_pos, SyntaxContext::root()),
++                        |build| {
++                            let mut lint =
++                                build.build(&format!("`mod.rs` files are required, found `{}`", path.display()));
++                            lint.help(&format!("move `{}` to `{}`", path.display(), correct.display(),));
++                            lint.emit();
++                        },
++                    );
++                }
++            }
++        }
++    }
++}
++
++/// For each `path` we add each folder component to `folder_segments` and if the file name
++/// is `mod.rs` we add it's parent folder to `mod_folders`.
++fn process_paths_for_mod_files(
++    path: &Path,
++    folder_segments: &mut FxHashSet<OsString>,
++    mod_folders: &mut FxHashSet<OsString>,
++) {
++    let mut comp = path.components().rev().peekable();
++    let _ = comp.next();
++    if path.ends_with("mod.rs") {
++        mod_folders.insert(comp.peek().map(|c| c.as_os_str().to_owned()).unwrap_or_default());
++    }
++    let folders = comp
++        .filter_map(|c| {
++            if let Component::Normal(s) = c {
++                Some(s.to_os_string())
++            } else {
++                None
++            }
++        })
++        .collect::<Vec<_>>();
++    folder_segments.extend(folders);
++}
++
++/// Checks every path for the presence of `mod.rs` files and emits the lint if found.
++fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) {
++    if path.ends_with("mod.rs") {
++        let mut mod_file = path.to_path_buf();
++        mod_file.pop();
++        mod_file.set_extension("rs");
++
++        cx.struct_span_lint(
++            MOD_MODULE_FILES,
++            Span::new(file.start_pos, file.start_pos, SyntaxContext::root()),
++            |build| {
++                let mut lint = build.build(&format!("`mod.rs` files are not allowed, found `{}`", path.display()));
++                lint.help(&format!("move `{}` to `{}`", path.display(), mod_file.display(),));
++                lint.emit();
++            },
++        );
++    }
++}
index 85e870632a5cab5c19f4ae30cd7260fd1a856aea,0000000000000000000000000000000000000000..e9dcc7b227d7601a51cac5fd6869e5d5e9b9d3c1
mode 100644,000000..100644
--- /dev/null
@@@ -1,68 -1,0 +1,68 @@@
-             if let ExprKind::MethodCall(path, method_span, args, _) = &ex.kind;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, Mutability};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `&mut Mutex::lock` calls
 +    ///
 +    /// ### Why is this bad?
 +    /// `Mutex::lock` is less efficient than
 +    /// calling `Mutex::get_mut`. In addition you also have a statically
 +    /// guarantee that the mutex isn't locked, instead of just a runtime
 +    /// guarantee.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::sync::{Arc, Mutex};
 +    ///
 +    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
 +    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
 +    ///
 +    /// let mut value = value_mutex.lock().unwrap();
 +    /// *value += 1;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::sync::{Arc, Mutex};
 +    ///
 +    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
 +    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
 +    ///
 +    /// let value = value_mutex.get_mut().unwrap();
 +    /// *value += 1;
 +    /// ```
 +    pub MUT_MUTEX_LOCK,
 +    style,
 +    "`&mut Mutex::lock` does unnecessary locking"
 +}
 +
 +declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MutMutexLock {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) {
 +        if_chain! {
-             let ty = cx.typeck_results().expr_ty(&args[0]);
++            if let ExprKind::MethodCall(path, method_span, [self_arg, ..], _) = &ex.kind;
 +            if path.ident.name == sym!(lock);
++            let ty = cx.typeck_results().expr_ty(self_arg);
 +            if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind();
 +            if is_type_diagnostic_item(cx, inner_ty, sym!(mutex_type));
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    MUT_MUTEX_LOCK,
 +                    *method_span,
 +                    "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference",
 +                    "change this to",
 +                    "get_mut".to_owned(),
 +                    Applicability::MaybeIncorrect,
 +                );
 +            }
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5024a881d2aa8b10b63d586fc70b2b7fe688a99b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,66 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::in_macro;
++use clippy_utils::source::snippet_opt;
++use clippy_utils::ty::is_type_diagnostic_item;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::TyS;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::symbol::sym;
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for no-op uses of Option::{as_deref,as_deref_mut},
++    /// for example, `Option<&T>::as_deref()` returns the same type.
++    ///
++    /// ### Why is this bad?
++    /// Redundant code and improving readability.
++    ///
++    /// ### Example
++    /// ```rust
++    /// let a = Some(&1);
++    /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
++    /// ```
++    /// Could be written as:
++    /// ```rust
++    /// let a = Some(&1);
++    /// let b = a;
++    /// ```
++    pub NEEDLESS_OPTION_AS_DEREF,
++    complexity,
++    "no-op use of `deref` or `deref_mut` method to `Option`."
++}
++
++declare_lint_pass!(OptionNeedlessDeref=> [
++    NEEDLESS_OPTION_AS_DEREF,
++]);
++
++impl<'tcx> LateLintPass<'tcx> for OptionNeedlessDeref {
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
++        if expr.span.from_expansion() || in_macro(expr.span) {
++            return;
++        }
++        let typeck = cx.typeck_results();
++        let outer_ty = typeck.expr_ty(expr);
++
++        if_chain! {
++            if is_type_diagnostic_item(cx,outer_ty,sym::option_type);
++            if let ExprKind::MethodCall(path, _, [sub_expr], _) = expr.kind;
++            let symbol = path.ident.as_str();
++            if symbol=="as_deref" || symbol=="as_deref_mut";
++            if TyS::same_type( outer_ty, typeck.expr_ty(sub_expr) );
++            then{
++                span_lint_and_sugg(
++                    cx,
++                    NEEDLESS_OPTION_AS_DEREF,
++                    expr.span,
++                    "derefed type is same as origin",
++                    "try this",
++                    snippet_opt(cx,sub_expr.span).unwrap(),
++                    Applicability::MachineApplicable
++                );
++            }
++        }
++    }
++}
index 28e9e6f438e3de59f8f63ef67acccbee8da82397,0000000000000000000000000000000000000000..c5a5cde4b110fccbb874801a53cd72dcc9b2e747
mode 100644,000000..100644
--- /dev/null
@@@ -1,181 -1,0 +1,216 @@@
-                 let mut snippet = String::new();
-                 for e in reduced {
 +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::has_drop;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use std::ops::Deref;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for statements which have no effect.
 +    ///
 +    /// ### Why is this bad?
 +    /// Similar to dead code, these statements are actually
 +    /// executed. However, as they have no effect, all they do is make the code less
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// 0;
 +    /// ```
 +    pub NO_EFFECT,
 +    complexity,
 +    "statements with no effect"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expression statements that can be reduced to a
 +    /// sub-expression.
 +    ///
 +    /// ### Why is this bad?
 +    /// Expressions by themselves often have no side-effects.
 +    /// Having such expressions reduces readability.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// compute_array()[0];
 +    /// ```
 +    pub UNNECESSARY_OPERATION,
 +    complexity,
 +    "outer expressions with no effect"
 +}
 +
 +fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if expr.span.from_expansion() {
 +        return false;
 +    }
 +    match expr.kind {
 +        ExprKind::Lit(..) | ExprKind::Closure(..) => true,
 +        ExprKind::Path(..) => !has_drop(cx, cx.typeck_results().expr_ty(expr)),
 +        ExprKind::Index(a, b) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b),
 +        ExprKind::Array(v) | ExprKind::Tup(v) => v.iter().all(|val| has_no_effect(cx, val)),
 +        ExprKind::Repeat(inner, _)
 +        | ExprKind::Cast(inner, _)
 +        | ExprKind::Type(inner, _)
 +        | ExprKind::Unary(_, inner)
 +        | ExprKind::Field(inner, _)
 +        | ExprKind::AddrOf(_, _, inner)
 +        | ExprKind::Box(inner) => has_no_effect(cx, inner),
 +        ExprKind::Struct(_, fields, ref base) => {
 +            !has_drop(cx, cx.typeck_results().expr_ty(expr))
 +                && fields.iter().all(|field| has_no_effect(cx, field.expr))
 +                && base.as_ref().map_or(true, |base| has_no_effect(cx, base))
 +        },
 +        ExprKind::Call(callee, args) => {
 +            if let ExprKind::Path(ref qpath) = callee.kind {
 +                let res = cx.qpath_res(qpath, callee.hir_id);
 +                let def_matched = matches!(
 +                    res,
 +                    Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..)
 +                );
 +                if def_matched || is_range_literal(expr) {
 +                    !has_drop(cx, cx.typeck_results().expr_ty(expr)) && args.iter().all(|arg| has_no_effect(cx, arg))
 +                } else {
 +                    false
 +                }
 +            } else {
 +                false
 +            }
 +        },
 +        ExprKind::Block(block, _) => {
 +            block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| has_no_effect(cx, expr))
 +        },
 +        _ => false,
 +    }
 +}
 +
 +declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION]);
 +
 +impl<'tcx> LateLintPass<'tcx> for NoEffect {
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
 +        if let StmtKind::Semi(expr) = stmt.kind {
 +            if has_no_effect(cx, expr) {
 +                span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect");
 +            } else if let Some(reduced) = reduce_expression(cx, expr) {
-                     if let Some(snip) = snippet_opt(cx, e.span) {
-                         snippet.push_str(&snip);
-                         snippet.push(';');
-                     } else {
-                         return;
++                for e in &reduced {
 +                    if e.span.from_expansion() {
 +                        return;
 +                    }
-                 span_lint_hir_and_then(
-                     cx,
-                     UNNECESSARY_OPERATION,
-                     expr.hir_id,
-                     stmt.span,
-                     "statement can be reduced",
-                     |diag| {
-                         diag.span_suggestion(stmt.span, "replace it with", snippet, Applicability::MachineApplicable);
-                     },
-                 );
++                }
++                if let ExprKind::Index(..) = &expr.kind {
++                    let snippet;
++                    if_chain! {
++                        if let Some(arr) = snippet_opt(cx, reduced[0].span);
++                        if let Some(func) = snippet_opt(cx, reduced[1].span);
++                        then {
++                            snippet = format!("assert!({}.len() > {});", &arr, &func);
++                        } else {
++                            return;
++                        }
 +                    }
++                    span_lint_hir_and_then(
++                        cx,
++                        UNNECESSARY_OPERATION,
++                        expr.hir_id,
++                        stmt.span,
++                        "unnecessary operation",
++                        |diag| {
++                            diag.span_suggestion(
++                                stmt.span,
++                                "statement can be written as",
++                                snippet,
++                                Applicability::MaybeIncorrect,
++                            );
++                        },
++                    );
++                } else {
++                    let mut snippet = String::new();
++                    for e in reduced {
++                        if let Some(snip) = snippet_opt(cx, e.span) {
++                            snippet.push_str(&snip);
++                            snippet.push(';');
++                        } else {
++                            return;
++                        }
++                    }
++                    span_lint_hir_and_then(
++                        cx,
++                        UNNECESSARY_OPERATION,
++                        expr.hir_id,
++                        stmt.span,
++                        "unnecessary operation",
++                        |diag| {
++                            diag.span_suggestion(
++                                stmt.span,
++                                "statement can be reduced to",
++                                snippet,
++                                Applicability::MachineApplicable,
++                            );
++                        },
++                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec<&'a Expr<'a>>> {
 +    if expr.span.from_expansion() {
 +        return None;
 +    }
 +    match expr.kind {
 +        ExprKind::Index(a, b) => Some(vec![a, b]),
 +        ExprKind::Binary(ref binop, a, b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => {
 +            Some(vec![a, b])
 +        },
 +        ExprKind::Array(v) | ExprKind::Tup(v) => Some(v.iter().collect()),
 +        ExprKind::Repeat(inner, _)
 +        | ExprKind::Cast(inner, _)
 +        | ExprKind::Type(inner, _)
 +        | ExprKind::Unary(_, inner)
 +        | ExprKind::Field(inner, _)
 +        | ExprKind::AddrOf(_, _, inner)
 +        | ExprKind::Box(inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
 +        ExprKind::Struct(_, fields, ref base) => {
 +            if has_drop(cx, cx.typeck_results().expr_ty(expr)) {
 +                None
 +            } else {
 +                Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
 +            }
 +        },
 +        ExprKind::Call(callee, args) => {
 +            if let ExprKind::Path(ref qpath) = callee.kind {
 +                let res = cx.qpath_res(qpath, callee.hir_id);
 +                match res {
 +                    Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..)
 +                        if !has_drop(cx, cx.typeck_results().expr_ty(expr)) =>
 +                    {
 +                        Some(args.iter().collect())
 +                    },
 +                    _ => None,
 +                }
 +            } else {
 +                None
 +            }
 +        },
 +        ExprKind::Block(block, _) => {
 +            if block.stmts.is_empty() {
 +                block.expr.as_ref().and_then(|e| {
 +                    match block.rules {
 +                        BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => None,
 +                        BlockCheckMode::DefaultBlock => Some(vec![&**e]),
 +                        // in case of compiler-inserted signaling blocks
 +                        BlockCheckMode::UnsafeBlock(_) => reduce_expression(cx, e),
 +                    }
 +                })
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
index 4064d94da2abff2928d59e40f213a1bf93d04988,0000000000000000000000000000000000000000..5752342cf623b67e55292b5079b2c89ab43fa8cb
mode 100644,000000..100644
--- /dev/null
@@@ -1,201 -1,0 +1,201 @@@
-         if let ExprKind::MethodCall(path, _, arguments, _) = e.kind {
-             let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::paths;
 +use clippy_utils::ty::match_type;
 +use rustc_ast::ast::LitKind;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::{Span, Spanned};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for duplicate open options as well as combinations
 +    /// that make no sense.
 +    ///
 +    /// ### Why is this bad?
 +    /// In the best case, the code will be harder to read than
 +    /// necessary. I don't know the worst case.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::fs::OpenOptions;
 +    ///
 +    /// OpenOptions::new().read(true).truncate(true);
 +    /// ```
 +    pub NONSENSICAL_OPEN_OPTIONS,
 +    correctness,
 +    "nonsensical combination of options for opening a file"
 +}
 +
 +declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for OpenOptions {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-                 get_open_options(cx, &arguments[0], &mut options);
++        if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &e.kind {
++            let obj_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
 +            if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) {
 +                let mut options = Vec::new();
++                get_open_options(cx, self_arg, &mut options);
 +                check_open_options(cx, &options, e.span);
 +            }
 +        }
 +    }
 +}
 +
 +#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 +enum Argument {
 +    True,
 +    False,
 +    Unknown,
 +}
 +
 +#[derive(Debug)]
 +enum OpenOption {
 +    Write,
 +    Read,
 +    Truncate,
 +    Create,
 +    Append,
 +}
 +
 +fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) {
 +    if let ExprKind::MethodCall(path, _, arguments, _) = argument.kind {
 +        let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
 +
 +        // Only proceed if this is a call on some object of type std::fs::OpenOptions
 +        if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {
 +            let argument_option = match arguments[1].kind {
 +                ExprKind::Lit(ref span) => {
 +                    if let Spanned {
 +                        node: LitKind::Bool(lit),
 +                        ..
 +                    } = *span
 +                    {
 +                        if lit { Argument::True } else { Argument::False }
 +                    } else {
 +                        // The function is called with a literal which is not a boolean literal.
 +                        // This is theoretically possible, but not very likely.
 +                        return;
 +                    }
 +                },
 +                _ => Argument::Unknown,
 +            };
 +
 +            match &*path.ident.as_str() {
 +                "create" => {
 +                    options.push((OpenOption::Create, argument_option));
 +                },
 +                "append" => {
 +                    options.push((OpenOption::Append, argument_option));
 +                },
 +                "truncate" => {
 +                    options.push((OpenOption::Truncate, argument_option));
 +                },
 +                "read" => {
 +                    options.push((OpenOption::Read, argument_option));
 +                },
 +                "write" => {
 +                    options.push((OpenOption::Write, argument_option));
 +                },
 +                _ => (),
 +            }
 +
 +            get_open_options(cx, &arguments[0], options);
 +        }
 +    }
 +}
 +
 +fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], span: Span) {
 +    let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false);
 +    let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) =
 +        (false, false, false, false, false);
 +    // This code is almost duplicated (oh, the irony), but I haven't found a way to
 +    // unify it.
 +
 +    for option in options {
 +        match *option {
 +            (OpenOption::Create, arg) => {
 +                if create {
 +                    span_lint(
 +                        cx,
 +                        NONSENSICAL_OPEN_OPTIONS,
 +                        span,
 +                        "the method `create` is called more than once",
 +                    );
 +                } else {
 +                    create = true;
 +                }
 +                create_arg = create_arg || (arg == Argument::True);
 +            },
 +            (OpenOption::Append, arg) => {
 +                if append {
 +                    span_lint(
 +                        cx,
 +                        NONSENSICAL_OPEN_OPTIONS,
 +                        span,
 +                        "the method `append` is called more than once",
 +                    );
 +                } else {
 +                    append = true;
 +                }
 +                append_arg = append_arg || (arg == Argument::True);
 +            },
 +            (OpenOption::Truncate, arg) => {
 +                if truncate {
 +                    span_lint(
 +                        cx,
 +                        NONSENSICAL_OPEN_OPTIONS,
 +                        span,
 +                        "the method `truncate` is called more than once",
 +                    );
 +                } else {
 +                    truncate = true;
 +                }
 +                truncate_arg = truncate_arg || (arg == Argument::True);
 +            },
 +            (OpenOption::Read, arg) => {
 +                if read {
 +                    span_lint(
 +                        cx,
 +                        NONSENSICAL_OPEN_OPTIONS,
 +                        span,
 +                        "the method `read` is called more than once",
 +                    );
 +                } else {
 +                    read = true;
 +                }
 +                read_arg = read_arg || (arg == Argument::True);
 +            },
 +            (OpenOption::Write, arg) => {
 +                if write {
 +                    span_lint(
 +                        cx,
 +                        NONSENSICAL_OPEN_OPTIONS,
 +                        span,
 +                        "the method `write` is called more than once",
 +                    );
 +                } else {
 +                    write = true;
 +                }
 +                write_arg = write_arg || (arg == Argument::True);
 +            },
 +        }
 +    }
 +
 +    if read && truncate && read_arg && truncate_arg && !(write && write_arg) {
 +        span_lint(
 +            cx,
 +            NONSENSICAL_OPEN_OPTIONS,
 +            span,
 +            "file opened with `truncate` and `read`",
 +        );
 +    }
 +    if append && truncate && append_arg && truncate_arg {
 +        span_lint(
 +            cx,
 +            NONSENSICAL_OPEN_OPTIONS,
 +            span,
 +            "file opened with `append` and `truncate`",
 +        );
 +    }
 +}
index eff3d3abff80cdc5affd4e0f03025be6acb9d7a5,0000000000000000000000000000000000000000..15f6dcae8870b498563190acabdb263ff9e49e17
mode 100644,000000..100644
--- /dev/null
@@@ -1,182 -1,0 +1,212 @@@
- use clippy_utils::usage::contains_return_break_continue_macro;
- use clippy_utils::{eager_or_lazy, in_macro, is_else_clause, is_lang_ctor};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::is_type_diagnostic_item;
- use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Mutability, PatKind, UnOp};
++use clippy_utils::{
++    can_move_expr_to_closure, eager_or_lazy, in_constant, in_macro, is_else_clause, is_lang_ctor, peel_hir_expr_while,
++    CaptureKind,
++};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::LangItem::OptionSome;
-     pedantic,
++use rustc_hir::{def::Res, BindingAnnotation, Block, 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);
 +    /// ```
 +    pub OPTION_IF_LET_ELSE,
-         if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(cx, expr);
++    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_type)
 +    } else {
 +        false
 +    }
 +}
 +
 +/// A struct containing information about occurrences of the
 +/// `if let Some(..) = .. else` construct that this lint detects.
 +struct OptionIfLetElseOccurence {
 +    option: String,
 +    method_sugg: String,
 +    some_expr: String,
 +    none_expr: String,
 +}
 +
 +/// Extracts the body of a given arm. If the arm contains only an expression,
 +/// then it returns the expression. Otherwise, it returns the entire block
 +fn extract_body_from_expr<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
 +    if let ExprKind::Block(
 +        Block {
 +            stmts: block_stmts,
 +            expr: Some(block_expr),
 +            ..
 +        },
 +        _,
 +    ) = expr.kind
 +    {
 +        if let [] = block_stmts {
 +            Some(block_expr)
 +        } else {
 +            Some(expr)
 +        }
 +    } else {
 +        None
 +    }
 +}
 +
 +fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String {
 +    format!(
 +        "{}{}",
 +        Sugg::hir(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 `OptionIfLetElseOccurence` 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<OptionIfLetElseOccurence> {
 +    if_chain! {
 +        if !in_macro(expr.span); // Don't lint macros, because it behaves weirdly
-         if !contains_return_break_continue_macro(if_then);
-         if !contains_return_break_continue_macro(if_else);
++        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, _) = &inner_pat.kind;
-             let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) { "map_or" } else { "map_or_else" };
++        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 = extract_body_from_expr(if_then)?;
 +            let none_body = extract_body_from_expr(if_else)?;
++            let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) {
++                "map_or"
++            } else {
++                "map_or_else"
++            };
 +            let capture_name = id.name.to_ident_string();
 +            let (as_ref, as_mut) = match &let_expr.kind {
 +                ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
 +                ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
 +                _ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut),
 +            };
 +            let cond_expr = match let_expr.kind {
 +                // Pointer dereferencing happens automatically, so we can omit it in the suggestion
 +                ExprKind::Unary(UnOp::Deref, expr) | ExprKind::AddrOf(_, _, expr) => expr,
 +                _ => let_expr,
 +            };
++            // Check if captures the closure will need conflict with borrows made in the scrutinee.
++            // TODO: check all the references made in the scrutinee expression. This will require interacting
++            // with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
++            if as_ref || as_mut {
++                let e = peel_hir_expr_while(cond_expr, |e| match e.kind {
++                    ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e),
++                    _ => None,
++                });
++                if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(local_id), .. })) = e.kind {
++                    match some_captures.get(local_id)
++                        .or_else(|| (method_sugg == "map_or_else").then(|| ()).and_then(|_| none_captures.get(local_id)))
++                    {
++                        Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None,
++                        Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None,
++                        Some(CaptureKind::Ref(Mutability::Not)) | None => (),
++                    }
++                }
++            }
 +            Some(OptionIfLetElseOccurence {
 +                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(cx, some_body, "..")),
 +                none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(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 35cff4141a903b40cdf58c3f176a9a599aeca3dc,0000000000000000000000000000000000000000..e7bc24465908b43851b87e2af2483e6ac7758028
mode 100644,000000..100644
--- /dev/null
@@@ -1,315 -1,0 +1,315 @@@
-             if let Some(ref expr_ty) = cx.typeck_results().node_type_opt(let_expr.hir_id) {
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::last_path_segment;
 +use rustc_hir::{
 +    intravisit, Body, Expr, ExprKind, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatField, PatKind,
 +    QPath, Stmt, StmtKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::subst::SubstsRef;
 +use rustc_middle::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use std::iter;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for patterns that aren't exact representations of the types
 +    /// they are applied to.
 +    ///
 +    /// To satisfy this lint, you will have to adjust either the expression that is matched
 +    /// against or the pattern itself, as well as the bindings that are introduced by the
 +    /// adjusted patterns. For matching you will have to either dereference the expression
 +    /// with the `*` operator, or amend the patterns to explicitly match against `&<pattern>`
 +    /// or `&mut <pattern>` depending on the reference mutability. For the bindings you need
 +    /// to use the inverse. You can leave them as plain bindings if you wish for the value
 +    /// to be copied, but you must use `ref mut <variable>` or `ref <variable>` to construct
 +    /// a reference into the matched structure.
 +    ///
 +    /// If you are looking for a way to learn about ownership semantics in more detail, it
 +    /// is recommended to look at IDE options available to you to highlight types, lifetimes
 +    /// and reference semantics in your code. The available tooling would expose these things
 +    /// in a general way even outside of the various pattern matching mechanics. Of course
 +    /// this lint can still be used to highlight areas of interest and ensure a good understanding
 +    /// of ownership semantics.
 +    ///
 +    /// ### Why is this bad?
 +    /// It isn't bad in general. But in some contexts it can be desirable
 +    /// because it increases ownership hints in the code, and will guard against some changes
 +    /// in ownership.
 +    ///
 +    /// ### Example
 +    /// This example shows the basic adjustments necessary to satisfy the lint. Note how
 +    /// the matched expression is explicitly dereferenced with `*` and the `inner` variable
 +    /// is bound to a shared borrow via `ref inner`.
 +    ///
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// let value = &Some(Box::new(23));
 +    /// match value {
 +    ///     Some(inner) => println!("{}", inner),
 +    ///     None => println!("none"),
 +    /// }
 +    ///
 +    /// // Good
 +    /// let value = &Some(Box::new(23));
 +    /// match *value {
 +    ///     Some(ref inner) => println!("{}", inner),
 +    ///     None => println!("none"),
 +    /// }
 +    /// ```
 +    ///
 +    /// The following example demonstrates one of the advantages of the more verbose style.
 +    /// Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable
 +    /// borrow, while `b` is simply taken by value. This ensures that the loop body cannot
 +    /// accidentally modify the wrong part of the structure.
 +    ///
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// let mut values = vec![(2, 3), (3, 4)];
 +    /// for (a, b) in &mut values {
 +    ///     *a += *b;
 +    /// }
 +    ///
 +    /// // Good
 +    /// let mut values = vec![(2, 3), (3, 4)];
 +    /// for &mut (ref mut a, b) in &mut values {
 +    ///     *a += b;
 +    /// }
 +    /// ```
 +    pub PATTERN_TYPE_MISMATCH,
 +    restriction,
 +    "type of pattern does not match the expression type"
 +}
 +
 +declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]);
 +
 +impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
 +        if let StmtKind::Local(local) = stmt.kind {
 +            if let Some(init) = &local.init {
 +                if let Some(init_ty) = cx.typeck_results().node_type_opt(init.hir_id) {
 +                    let pat = &local.pat;
 +                    if in_external_macro(cx.sess(), pat.span) {
 +                        return;
 +                    }
 +                    let deref_possible = match local.source {
 +                        LocalSource::Normal => DerefPossible::Possible,
 +                        _ => DerefPossible::Impossible,
 +                    };
 +                    apply_lint(cx, pat, init_ty, deref_possible);
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = expr.kind {
 +            if let Some(expr_ty) = cx.typeck_results().node_type_opt(scrutinee.hir_id) {
 +                'pattern_checks: for arm in arms {
 +                    let pat = &arm.pat;
 +                    if in_external_macro(cx.sess(), pat.span) {
 +                        continue 'pattern_checks;
 +                    }
 +                    if apply_lint(cx, pat, expr_ty, DerefPossible::Possible) {
 +                        break 'pattern_checks;
 +                    }
 +                }
 +            }
 +        }
 +        if let ExprKind::Let(let_pat, let_expr, _) = expr.kind {
++            if let Some(expr_ty) = cx.typeck_results().node_type_opt(let_expr.hir_id) {
 +                if in_external_macro(cx.sess(), let_pat.span) {
 +                    return;
 +                }
 +                apply_lint(cx, let_pat, expr_ty, DerefPossible::Possible);
 +            }
 +        }
 +    }
 +
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        _: intravisit::FnKind<'tcx>,
 +        _: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        _: Span,
 +        hir_id: HirId,
 +    ) {
 +        if let Some(fn_sig) = cx.typeck_results().liberated_fn_sigs().get(hir_id) {
 +            for (param, ty) in iter::zip(body.params, fn_sig.inputs()) {
 +                apply_lint(cx, param.pat, ty, DerefPossible::Impossible);
 +            }
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy)]
 +enum DerefPossible {
 +    Possible,
 +    Impossible,
 +}
 +
 +fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, expr_ty: Ty<'tcx>, deref_possible: DerefPossible) -> bool {
 +    let maybe_mismatch = find_first_mismatch(cx, pat, expr_ty, Level::Top);
 +    if let Some((span, mutability, level)) = maybe_mismatch {
 +        span_lint_and_help(
 +            cx,
 +            PATTERN_TYPE_MISMATCH,
 +            span,
 +            "type of pattern does not match the expression type",
 +            None,
 +            &format!(
 +                "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings",
 +                match (deref_possible, level) {
 +                    (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ",
 +                    _ => "",
 +                },
 +                match mutability {
 +                    Mutability::Mut => "&mut _",
 +                    Mutability::Not => "&_",
 +                },
 +            ),
 +        );
 +        true
 +    } else {
 +        false
 +    }
 +}
 +
 +#[derive(Debug, Copy, Clone)]
 +enum Level {
 +    Top,
 +    Lower,
 +}
 +
 +#[allow(rustc::usage_of_ty_tykind)]
 +fn find_first_mismatch<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    pat: &Pat<'_>,
 +    ty: Ty<'tcx>,
 +    level: Level,
 +) -> Option<(Span, Mutability, Level)> {
 +    if let PatKind::Ref(sub_pat, _) = pat.kind {
 +        if let TyKind::Ref(_, sub_ty, _) = ty.kind() {
 +            return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower);
 +        }
 +    }
 +
 +    if let TyKind::Ref(_, _, mutability) = *ty.kind() {
 +        if is_non_ref_pattern(&pat.kind) {
 +            return Some((pat.span, mutability, level));
 +        }
 +    }
 +
 +    if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind {
 +        if let TyKind::Adt(adt_def, substs_ref) = ty.kind() {
 +            if let Some(variant) = get_variant(adt_def, qpath) {
 +                let field_defs = &variant.fields;
 +                return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref);
 +            }
 +        }
 +    }
 +
 +    if let PatKind::TupleStruct(ref qpath, pats, _) = pat.kind {
 +        if let TyKind::Adt(adt_def, substs_ref) = ty.kind() {
 +            if let Some(variant) = get_variant(adt_def, qpath) {
 +                let field_defs = &variant.fields;
 +                let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref));
 +                return find_first_mismatch_in_tuple(cx, pats, ty_iter);
 +            }
 +        }
 +    }
 +
 +    if let PatKind::Tuple(pats, _) = pat.kind {
 +        if let TyKind::Tuple(..) = ty.kind() {
 +            return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields());
 +        }
 +    }
 +
 +    if let PatKind::Or(sub_pats) = pat.kind {
 +        for pat in sub_pats {
 +            let maybe_mismatch = find_first_mismatch(cx, pat, ty, level);
 +            if let Some(mismatch) = maybe_mismatch {
 +                return Some(mismatch);
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn get_variant<'a>(adt_def: &'a AdtDef, qpath: &QPath<'_>) -> Option<&'a VariantDef> {
 +    if adt_def.is_struct() {
 +        if let Some(variant) = adt_def.variants.iter().next() {
 +            return Some(variant);
 +        }
 +    }
 +
 +    if adt_def.is_enum() {
 +        let pat_ident = last_path_segment(qpath).ident;
 +        for variant in &adt_def.variants {
 +            if variant.ident == pat_ident {
 +                return Some(variant);
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn find_first_mismatch_in_tuple<'tcx, I>(
 +    cx: &LateContext<'tcx>,
 +    pats: &[Pat<'_>],
 +    ty_iter_src: I,
 +) -> Option<(Span, Mutability, Level)>
 +where
 +    I: IntoIterator<Item = Ty<'tcx>>,
 +{
 +    let mut field_tys = ty_iter_src.into_iter();
 +    'fields: for pat in pats {
 +        let field_ty = if let Some(ty) = field_tys.next() {
 +            ty
 +        } else {
 +            break 'fields;
 +        };
 +
 +        let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
 +        if let Some(mismatch) = maybe_mismatch {
 +            return Some(mismatch);
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn find_first_mismatch_in_struct<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    field_pats: &[PatField<'_>],
 +    field_defs: &[FieldDef],
 +    substs_ref: SubstsRef<'tcx>,
 +) -> Option<(Span, Mutability, Level)> {
 +    for field_pat in field_pats {
 +        'definitions: for field_def in field_defs {
 +            if field_pat.ident == field_def.ident {
 +                let field_ty = field_def.ty(cx.tcx, substs_ref);
 +                let pat = &field_pat.pat;
 +                let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
 +                if let Some(mismatch) = maybe_mismatch {
 +                    return Some(mismatch);
 +                }
 +                break 'definitions;
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn is_non_ref_pattern(pat_kind: &PatKind<'_>) -> bool {
 +    match pat_kind {
 +        PatKind::Struct(..) | PatKind::Tuple(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => true,
 +        PatKind::Or(sub_pats) => sub_pats.iter().any(|pat| is_non_ref_pattern(&pat.kind)),
 +        _ => false,
 +    }
 +}
index f1975056ddc9ba40751c82161bdc96f14cafc0fd,0000000000000000000000000000000000000000..cfb5287c6673fabe6a5a0642472916f768f9fe39
mode 100644,000000..100644
--- /dev/null
@@@ -1,152 -1,0 +1,152 @@@
-     if let ExprKind::MethodCall(path_segment, _, args, _) = expr.kind {
-         if is_expr_ty_raw_ptr(cx, &args[0]) {
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 +use clippy_utils::source::snippet_opt;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +use std::fmt;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of the `offset` pointer method with a `usize` casted to an
 +    /// `isize`.
 +    ///
 +    /// ### Why is this bad?
 +    /// If we’re always increasing the pointer address, we can avoid the numeric
 +    /// cast by using the `add` method instead.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec = vec![b'a', b'b', b'c'];
 +    /// let ptr = vec.as_ptr();
 +    /// let offset = 1_usize;
 +    ///
 +    /// unsafe {
 +    ///     ptr.offset(offset as isize);
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```rust
 +    /// let vec = vec![b'a', b'b', b'c'];
 +    /// let ptr = vec.as_ptr();
 +    /// let offset = 1_usize;
 +    ///
 +    /// unsafe {
 +    ///     ptr.add(offset);
 +    /// }
 +    /// ```
 +    pub PTR_OFFSET_WITH_CAST,
 +    complexity,
 +    "unneeded pointer offset cast"
 +}
 +
 +declare_lint_pass!(PtrOffsetWithCast => [PTR_OFFSET_WITH_CAST]);
 +
 +impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call
 +        let (receiver_expr, arg_expr, method) = match expr_as_ptr_offset_call(cx, expr) {
 +            Some(call_arg) => call_arg,
 +            None => return,
 +        };
 +
 +        // Check if the argument to the method call is a cast from usize
 +        let cast_lhs_expr = match expr_as_cast_from_usize(cx, arg_expr) {
 +            Some(cast_lhs_expr) => cast_lhs_expr,
 +            None => return,
 +        };
 +
 +        let msg = format!("use of `{}` with a `usize` casted to an `isize`", method);
 +        if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) {
 +            span_lint_and_sugg(
 +                cx,
 +                PTR_OFFSET_WITH_CAST,
 +                expr.span,
 +                &msg,
 +                "try",
 +                sugg,
 +                Applicability::MachineApplicable,
 +            );
 +        } else {
 +            span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, &msg);
 +        }
 +    }
 +}
 +
 +// If the given expression is a cast from a usize, return the lhs of the cast
 +fn expr_as_cast_from_usize<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +    if let ExprKind::Cast(cast_lhs_expr, _) = expr.kind {
 +        if is_expr_ty_usize(cx, cast_lhs_expr) {
 +            return Some(cast_lhs_expr);
 +        }
 +    }
 +    None
 +}
 +
 +// If the given expression is a ptr::offset  or ptr::wrapping_offset method call, return the
 +// receiver, the arg of the method call, and the method.
 +fn expr_as_ptr_offset_call<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> {
-                 return Some((&args[0], &args[1], Method::Offset));
++    if let ExprKind::MethodCall(path_segment, _, [arg_0, arg_1, ..], _) = &expr.kind {
++        if is_expr_ty_raw_ptr(cx, arg_0) {
 +            if path_segment.ident.name == sym::offset {
-                 return Some((&args[0], &args[1], Method::WrappingOffset));
++                return Some((arg_0, arg_1, Method::Offset));
 +            }
 +            if path_segment.ident.name == sym!(wrapping_offset) {
++                return Some((arg_0, arg_1, Method::WrappingOffset));
 +            }
 +        }
 +    }
 +    None
 +}
 +
 +// Is the type of the expression a usize?
 +fn is_expr_ty_usize<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool {
 +    cx.typeck_results().expr_ty(expr) == cx.tcx.types.usize
 +}
 +
 +// Is the type of the expression a raw pointer?
 +fn is_expr_ty_raw_ptr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool {
 +    cx.typeck_results().expr_ty(expr).is_unsafe_ptr()
 +}
 +
 +fn build_suggestion<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    method: Method,
 +    receiver_expr: &Expr<'_>,
 +    cast_lhs_expr: &Expr<'_>,
 +) -> Option<String> {
 +    let receiver = snippet_opt(cx, receiver_expr.span)?;
 +    let cast_lhs = snippet_opt(cx, cast_lhs_expr.span)?;
 +    Some(format!("{}.{}({})", receiver, method.suggestion(), cast_lhs))
 +}
 +
 +#[derive(Copy, Clone)]
 +enum Method {
 +    Offset,
 +    WrappingOffset,
 +}
 +
 +impl Method {
 +    #[must_use]
 +    fn suggestion(self) -> &'static str {
 +        match self {
 +            Self::Offset => "add",
 +            Self::WrappingOffset => "wrapping_add",
 +        }
 +    }
 +}
 +
 +impl fmt::Display for Method {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        match self {
 +            Self::Offset => write!(f, "offset"),
 +            Self::WrappingOffset => write!(f, "wrapping_offset"),
 +        }
 +    }
 +}
index 7b6a0894e6d2fcc4ccb1c70133f5cff65078d51a,0000000000000000000000000000000000000000..e79cd7ed4ec4a969c95a2c0db407735712130d81
mode 100644,000000..100644
--- /dev/null
@@@ -1,194 -1,0 +1,195 @@@
-             if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(cx, expr);
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::is_lang_ctor;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{eq_expr_value, path_to_local_id};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::LangItem::{OptionNone, OptionSome};
 +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, PatKind, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expressions that could be replaced by the question mark operator.
 +    ///
 +    /// ### Why is this bad?
 +    /// Question mark usage is more idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// if option.is_none() {
 +    ///     return None;
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```ignore
 +    /// option?;
 +    /// ```
 +    pub QUESTION_MARK,
 +    style,
 +    "checks for expressions that could be replaced by the question mark operator"
 +}
 +
 +declare_lint_pass!(QuestionMark => [QUESTION_MARK]);
 +
 +impl QuestionMark {
 +    /// Checks if the given expression on the given context matches the following structure:
 +    ///
 +    /// ```ignore
 +    /// if option.is_none() {
 +    ///    return None;
 +    /// }
 +    /// ```
 +    ///
 +    /// If it matches, it will suggest to use the question mark operator instead
 +    fn check_is_none_and_early_return_none(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 segment.ident.name == sym!(is_none);
 +            if Self::expression_returns_none(cx, then);
 +            if let Some(subject) = args.get(0);
 +            if Self::is_option(cx, subject);
 +
 +            then {
 +                let mut applicability = Applicability::MachineApplicable;
 +                let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability);
 +                let mut replacement: Option<String> = None;
 +                if let Some(else_inner) = r#else {
 +                    if_chain! {
 +                        if let ExprKind::Block(block, None) = &else_inner.kind;
 +                        if block.stmts.is_empty();
 +                        if let Some(block_expr) = &block.expr;
 +                        if eq_expr_value(cx, subject, block_expr);
 +                        then {
 +                            replacement = Some(format!("Some({}?)", receiver_str));
 +                        }
 +                    }
 +                } else if Self::moves_by_default(cx, subject)
 +                    && !matches!(subject.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
 +                {
 +                    replacement = Some(format!("{}.as_ref()?;", receiver_str));
 +                } else {
 +                    replacement = Some(format!("{}?;", receiver_str));
 +                }
 +
 +                if let Some(replacement_str) = replacement {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        QUESTION_MARK,
 +                        expr.span,
 +                        "this block may be rewritten with the `?` operator",
 +                        "replace it with",
 +                        replacement_str,
 +                        applicability,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_if_let_some_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        if_chain! {
-             if let ExprKind::Block(ref block, None) = if_then.kind;
++            if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
++                = higher::IfLet::hir(cx, expr);
 +            if Self::is_option(cx, let_expr);
 +
 +            if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind;
 +            if is_lang_ctor(cx, path1, OptionSome);
 +            if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind;
 +            let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
 +
++            if let ExprKind::Block(block, None) = if_then.kind;
 +            if block.stmts.is_empty();
 +            if let Some(trailing_expr) = &block.expr;
 +            if path_to_local_id(trailing_expr, bind_id);
 +
 +            if Self::expression_returns_none(cx, if_else);
 +            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 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_type)
 +    }
 +
 +    fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
 +        match expression.kind {
 +            ExprKind::Block(block, _) => {
 +                if let Some(return_expression) = Self::return_expression(block) {
 +                    return Self::expression_returns_none(cx, return_expression);
 +                }
 +
 +                false
 +            },
 +            ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr),
 +            ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
 +            _ => false,
 +        }
 +    }
 +
 +    fn return_expression<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +        // Check if last expression is a return statement. Then, return the expression
 +        if_chain! {
 +            if block.stmts.len() == 1;
 +            if let Some(expr) = block.stmts.iter().last();
 +            if let StmtKind::Semi(expr) = expr.kind;
 +            if let ExprKind::Ret(Some(ret_expr)) = expr.kind;
 +
 +            then {
 +                return Some(ret_expr);
 +            }
 +        }
 +
 +        // Check for `return` without a semicolon.
 +        if_chain! {
 +            if block.stmts.is_empty();
 +            if let Some(ExprKind::Ret(Some(ret_expr))) = block.expr.as_ref().map(|e| &e.kind);
 +            then {
 +                return Some(ret_expr);
 +            }
 +        }
 +
 +        None
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for QuestionMark {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        Self::check_is_none_and_early_return_none(cx, expr);
 +        Self::check_if_let_some_and_early_return_none(cx, expr);
 +    }
 +}
index f5e43264a5c69809b4dfc04d00d93b4b2f0886ee,0000000000000000000000000000000000000000..cfa12ef3a321dd7ba04fbd992c819aaa24436ed7
mode 100644,000000..100644
--- /dev/null
@@@ -1,772 -1,0 +1,775 @@@
-             if ContainsRegion(self.cx.tcx).visit_ty(self.body.local_decls[*dest].ty).is_break() {
 +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth};
 +use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_data_structures::{fx::FxHashMap, transitive_relation::TransitiveRelation};
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::FnKind;
 +use rustc_hir::{def_id, Body, FnDecl, HirId};
 +use rustc_index::bit_set::{BitSet, HybridBitSet};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::mir::{
 +    self, traversal,
 +    visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _},
 +    Mutability,
 +};
 +use rustc_middle::ty::{self, fold::TypeVisitor, Ty, TyCtxt};
 +use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::{BytePos, Span};
 +use rustc_span::sym;
 +use std::convert::TryFrom;
 +use std::ops::ControlFlow;
 +
 +macro_rules! unwrap_or_continue {
 +    ($x:expr) => {
 +        match $x {
 +            Some(x) => x,
 +            None => continue,
 +        }
 +    };
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for a redundant `clone()` (and its relatives) which clones an owned
 +    /// value that is going to be dropped without further use.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is not always possible for the compiler to eliminate useless
 +    /// allocations and deallocations generated by redundant `clone()`s.
 +    ///
 +    /// ### Known problems
 +    /// False-negatives: analysis performed by this lint is conservative and limited.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::path::Path;
 +    /// # #[derive(Clone)]
 +    /// # struct Foo;
 +    /// # impl Foo {
 +    /// #     fn new() -> Self { Foo {} }
 +    /// # }
 +    /// # fn call(x: Foo) {}
 +    /// {
 +    ///     let x = Foo::new();
 +    ///     call(x.clone());
 +    ///     call(x.clone()); // this can just pass `x`
 +    /// }
 +    ///
 +    /// ["lorem", "ipsum"].join(" ").to_string();
 +    ///
 +    /// Path::new("/a/b").join("c").to_path_buf();
 +    /// ```
 +    pub REDUNDANT_CLONE,
 +    perf,
 +    "`clone()` of an owned value that is going to be dropped immediately"
 +}
 +
 +declare_lint_pass!(RedundantClone => [REDUNDANT_CLONE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for RedundantClone {
 +    #[allow(clippy::too_many_lines)]
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        _: FnKind<'tcx>,
 +        _: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        _: Span,
 +        _: HirId,
 +    ) {
 +        let def_id = cx.tcx.hir().body_owner_def_id(body.id());
 +
 +        // Building MIR for `fn`s with unsatisfiable preds results in ICE.
 +        if fn_has_unsatisfiable_preds(cx, def_id.to_def_id()) {
 +            return;
 +        }
 +
 +        let mir = cx.tcx.optimized_mir(def_id.to_def_id());
 +
 +        let possible_origin = {
 +            let mut vis = PossibleOriginVisitor::new(mir);
 +            vis.visit_body(mir);
 +            vis.into_map(cx)
 +        };
 +        let maybe_storage_live_result = MaybeStorageLive
 +            .into_engine(cx.tcx, mir)
 +            .pass_name("redundant_clone")
 +            .iterate_to_fixpoint()
 +            .into_results_cursor(mir);
 +        let mut possible_borrower = {
 +            let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin);
 +            vis.visit_body(mir);
 +            vis.into_map(cx, maybe_storage_live_result)
 +        };
 +
 +        for (bb, bbdata) in mir.basic_blocks().iter_enumerated() {
 +            let terminator = bbdata.terminator();
 +
 +            if terminator.source_info.span.from_expansion() {
 +                continue;
 +            }
 +
 +            // Give up on loops
 +            if terminator.successors().any(|s| *s == bb) {
 +                continue;
 +            }
 +
 +            let (fn_def_id, arg, arg_ty, clone_ret) =
 +                unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind));
 +
 +            let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
 +                || match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD)
 +                || (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD)
 +                    && is_type_diagnostic_item(cx, arg_ty, sym::string_type));
 +
 +            let from_deref = !from_borrow
 +                && (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF)
 +                    || match_def_path(cx, fn_def_id, &paths::OS_STR_TO_OS_STRING));
 +
 +            if !from_borrow && !from_deref {
 +                continue;
 +            }
 +
 +            if let ty::Adt(def, _) = arg_ty.kind() {
 +                if match_def_path(cx, def.did, &paths::MEM_MANUALLY_DROP) {
 +                    continue;
 +                }
 +            }
 +
 +            // `{ arg = &cloned; clone(move arg); }` or `{ arg = &cloned; to_path_buf(arg); }`
 +            let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(cx, mir, arg, from_borrow, bb));
 +
 +            let loc = mir::Location {
 +                block: bb,
 +                statement_index: bbdata.statements.len(),
 +            };
 +
 +            // `Local` to be cloned, and a local of `clone` call's destination
 +            let (local, ret_local) = if from_borrow {
 +                // `res = clone(arg)` can be turned into `res = move arg;`
 +                // if `arg` is the only borrow of `cloned` at this point.
 +
 +                if cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc) {
 +                    continue;
 +                }
 +
 +                (cloned, clone_ret)
 +            } else {
 +                // `arg` is a reference as it is `.deref()`ed in the previous block.
 +                // Look into the predecessor block and find out the source of deref.
 +
 +                let ps = &mir.predecessors()[bb];
 +                if ps.len() != 1 {
 +                    continue;
 +                }
 +                let pred_terminator = mir[ps[0]].terminator();
 +
 +                // receiver of the `deref()` call
 +                let (pred_arg, deref_clone_ret) = if_chain! {
 +                    if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, res)) =
 +                        is_call_with_ref_arg(cx, mir, &pred_terminator.kind);
 +                    if res == cloned;
 +                    if cx.tcx.is_diagnostic_item(sym::deref_method, pred_fn_def_id);
 +                    if is_type_diagnostic_item(cx, pred_arg_ty, sym::PathBuf)
 +                        || is_type_diagnostic_item(cx, pred_arg_ty, sym::OsString);
 +                    then {
 +                        (pred_arg, res)
 +                    } else {
 +                        continue;
 +                    }
 +                };
 +
 +                let (local, cannot_move_out) =
 +                    unwrap_or_continue!(find_stmt_assigns_to(cx, mir, pred_arg, true, ps[0]));
 +                let loc = mir::Location {
 +                    block: bb,
 +                    statement_index: mir.basic_blocks()[bb].statements.len(),
 +                };
 +
 +                // This can be turned into `res = move local` if `arg` and `cloned` are not borrowed
 +                // at the last statement:
 +                //
 +                // ```
 +                // pred_arg = &local;
 +                // cloned = deref(pred_arg);
 +                // arg = &cloned;
 +                // StorageDead(pred_arg);
 +                // res = to_path_buf(cloned);
 +                // ```
 +                if cannot_move_out || !possible_borrower.only_borrowers(&[arg, cloned], local, loc) {
 +                    continue;
 +                }
 +
 +                (local, deref_clone_ret)
 +            };
 +
 +            let clone_usage = if local == ret_local {
 +                CloneUsage {
 +                    cloned_used: false,
 +                    cloned_consume_or_mutate_loc: None,
 +                    clone_consumed_or_mutated: true,
 +                }
 +            } else {
 +                let clone_usage = visit_clone_usage(local, ret_local, mir, bb);
 +                if clone_usage.cloned_used && clone_usage.clone_consumed_or_mutated {
 +                    // cloned value is used, and the clone is modified or moved
 +                    continue;
 +                } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc {
 +                    // cloned value is mutated, and the clone is alive.
 +                    if possible_borrower.is_alive_at(ret_local, loc) {
 +                        continue;
 +                    }
 +                }
 +                clone_usage
 +            };
 +
 +            let span = terminator.source_info.span;
 +            let scope = terminator.source_info.scope;
 +            let node = mir.source_scopes[scope]
 +                .local_data
 +                .as_ref()
 +                .assert_crate_local()
 +                .lint_root;
 +
 +            if_chain! {
 +                if let Some(snip) = snippet_opt(cx, span);
 +                if let Some(dot) = snip.rfind('.');
 +                then {
 +                    let sugg_span = span.with_lo(
 +                        span.lo() + BytePos(u32::try_from(dot).unwrap())
 +                    );
 +                    let mut app = Applicability::MaybeIncorrect;
 +
 +                    let call_snip = &snip[dot + 1..];
 +                    // Machine applicable when `call_snip` looks like `foobar()`
 +                    if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) {
 +                        if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') {
 +                            app = Applicability::MachineApplicable;
 +                        }
 +                    }
 +
 +                    span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| {
 +                        diag.span_suggestion(
 +                            sugg_span,
 +                            "remove this",
 +                            String::new(),
 +                            app,
 +                        );
 +                        if clone_usage.cloned_used {
 +                            diag.span_note(
 +                                span,
 +                                "cloned value is neither consumed nor mutated",
 +                            );
 +                        } else {
 +                            diag.span_note(
 +                                span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())),
 +                                "this value is dropped without further use",
 +                            );
 +                        }
 +                    });
 +                } else {
 +                    span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone");
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// If `kind` is `y = func(x: &T)` where `T: !Copy`, returns `(DefId of func, x, T, y)`.
 +fn is_call_with_ref_arg<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    mir: &'tcx mir::Body<'tcx>,
 +    kind: &'tcx mir::TerminatorKind<'tcx>,
 +) -> Option<(def_id::DefId, mir::Local, Ty<'tcx>, mir::Local)> {
 +    if_chain! {
 +        if let mir::TerminatorKind::Call { func, args, destination, .. } = kind;
 +        if args.len() == 1;
 +        if let mir::Operand::Move(mir::Place { local, .. }) = &args[0];
 +        if let ty::FnDef(def_id, _) = *func.ty(&*mir, cx.tcx).kind();
 +        if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
 +        if !is_copy(cx, inner_ty);
 +        then {
 +            Some((def_id, *local, inner_ty, destination.as_ref().map(|(dest, _)| dest)?.as_local()?))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +type CannotMoveOut = bool;
 +
 +/// Finds the first `to = (&)from`, and returns
 +/// ``Some((from, whether `from` cannot be moved out))``.
 +fn find_stmt_assigns_to<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    mir: &mir::Body<'tcx>,
 +    to_local: mir::Local,
 +    by_ref: bool,
 +    bb: mir::BasicBlock,
 +) -> Option<(mir::Local, CannotMoveOut)> {
 +    let rvalue = mir.basic_blocks()[bb].statements.iter().rev().find_map(|stmt| {
 +        if let mir::StatementKind::Assign(box (mir::Place { local, .. }, v)) = &stmt.kind {
 +            return if *local == to_local { Some(v) } else { None };
 +        }
 +
 +        None
 +    })?;
 +
 +    match (by_ref, &*rvalue) {
 +        (true, mir::Rvalue::Ref(_, _, place)) | (false, mir::Rvalue::Use(mir::Operand::Copy(place))) => {
 +            Some(base_local_and_movability(cx, mir, *place))
 +        },
 +        (false, mir::Rvalue::Ref(_, _, place)) => {
 +            if let [mir::ProjectionElem::Deref] = place.as_ref().projection {
 +                Some(base_local_and_movability(cx, mir, *place))
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +/// Extracts and returns the undermost base `Local` of given `place`. Returns `place` itself
 +/// if it is already a `Local`.
 +///
 +/// Also reports whether given `place` cannot be moved out.
 +fn base_local_and_movability<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    mir: &mir::Body<'tcx>,
 +    place: mir::Place<'tcx>,
 +) -> (mir::Local, CannotMoveOut) {
 +    use rustc_middle::mir::PlaceRef;
 +
 +    // Dereference. You cannot move things out from a borrowed value.
 +    let mut deref = false;
 +    // Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509.
 +    let mut field = false;
 +    // If projection is a slice index then clone can be removed only if the
 +    // underlying type implements Copy
 +    let mut slice = false;
 +
 +    let PlaceRef { local, mut projection } = place.as_ref();
 +    while let [base @ .., elem] = projection {
 +        projection = base;
 +        deref |= matches!(elem, mir::ProjectionElem::Deref);
 +        field |= matches!(elem, mir::ProjectionElem::Field(..))
 +            && has_drop(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty);
 +        slice |= matches!(elem, mir::ProjectionElem::Index(..))
 +            && !is_copy(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty);
 +    }
 +
 +    (local, deref || field || slice)
 +}
 +
 +#[derive(Default)]
 +struct CloneUsage {
 +    /// Whether the cloned value is used after the clone.
 +    cloned_used: bool,
 +    /// The first location where the cloned value is consumed or mutated, if any.
 +    cloned_consume_or_mutate_loc: Option<mir::Location>,
 +    /// Whether the clone value is mutated.
 +    clone_consumed_or_mutated: bool,
 +}
 +fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage {
 +    struct V {
 +        cloned: mir::Local,
 +        clone: mir::Local,
 +        result: CloneUsage,
 +    }
 +    impl<'tcx> mir::visit::Visitor<'tcx> for V {
 +        fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) {
 +            let statements = &data.statements;
 +            for (statement_index, statement) in statements.iter().enumerate() {
 +                self.visit_statement(statement, mir::Location { block, statement_index });
 +            }
 +
 +            self.visit_terminator(
 +                data.terminator(),
 +                mir::Location {
 +                    block,
 +                    statement_index: statements.len(),
 +                },
 +            );
 +        }
 +
 +        fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, loc: mir::Location) {
 +            let local = place.local;
 +
 +            if local == self.cloned
 +                && !matches!(
 +                    ctx,
 +                    PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)
 +                )
 +            {
 +                self.result.cloned_used = true;
 +                self.result.cloned_consume_or_mutate_loc = self.result.cloned_consume_or_mutate_loc.or_else(|| {
 +                    matches!(
 +                        ctx,
 +                        PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
 +                            | PlaceContext::MutatingUse(MutatingUseContext::Borrow)
 +                    )
 +                    .then(|| loc)
 +                });
 +            } else if local == self.clone {
 +                match ctx {
 +                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
 +                    | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
 +                        self.result.clone_consumed_or_mutated = true;
 +                    },
 +                    _ => {},
 +                }
 +            }
 +        }
 +    }
 +
 +    let init = CloneUsage {
 +        cloned_used: false,
 +        cloned_consume_or_mutate_loc: None,
 +        // Consider non-temporary clones consumed.
 +        // TODO: Actually check for mutation of non-temporaries.
 +        clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp,
 +    };
 +    traversal::ReversePostorder::new(mir, bb)
 +        .skip(1)
 +        .fold(init, |usage, (tbb, tdata)| {
 +            // Short-circuit
 +            if (usage.cloned_used && usage.clone_consumed_or_mutated) ||
 +                // Give up on loops
 +                tdata.terminator().successors().any(|s| *s == bb)
 +            {
 +                return CloneUsage {
 +                    cloned_used: true,
 +                    clone_consumed_or_mutated: true,
 +                    ..usage
 +                };
 +            }
 +
 +            let mut v = V {
 +                cloned,
 +                clone,
 +                result: usage,
 +            };
 +            v.visit_basic_block_data(tbb, tdata);
 +            v.result
 +        })
 +}
 +
 +/// Determines liveness of each local purely based on `StorageLive`/`Dead`.
 +#[derive(Copy, Clone)]
 +struct MaybeStorageLive;
 +
 +impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive {
 +    type Domain = BitSet<mir::Local>;
 +    const NAME: &'static str = "maybe_storage_live";
 +
 +    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
 +        // bottom = dead
 +        BitSet::new_empty(body.local_decls.len())
 +    }
 +
 +    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
 +        for arg in body.args_iter() {
 +            state.insert(arg);
 +        }
 +    }
 +}
 +
 +impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive {
 +    type Idx = mir::Local;
 +
 +    fn statement_effect(&self, trans: &mut impl GenKill<Self::Idx>, stmt: &mir::Statement<'tcx>, _: mir::Location) {
 +        match stmt.kind {
 +            mir::StatementKind::StorageLive(l) => trans.gen(l),
 +            mir::StatementKind::StorageDead(l) => trans.kill(l),
 +            _ => (),
 +        }
 +    }
 +
 +    fn terminator_effect(
 +        &self,
 +        _trans: &mut impl GenKill<Self::Idx>,
 +        _terminator: &mir::Terminator<'tcx>,
 +        _loc: mir::Location,
 +    ) {
 +    }
 +
 +    fn call_return_effect(
 +        &self,
 +        _in_out: &mut impl GenKill<Self::Idx>,
 +        _block: mir::BasicBlock,
 +        _func: &mir::Operand<'tcx>,
 +        _args: &[mir::Operand<'tcx>],
 +        _return_place: mir::Place<'tcx>,
 +    ) {
 +        // Nothing to do when a call returns successfully
 +    }
 +}
 +
 +/// Collects the possible borrowers of each local.
 +/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
 +/// possible borrowers of `a`.
 +struct PossibleBorrowerVisitor<'a, 'tcx> {
 +    possible_borrower: TransitiveRelation<mir::Local>,
 +    body: &'a mir::Body<'tcx>,
 +    cx: &'a LateContext<'tcx>,
 +    possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
 +}
 +
 +impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> {
 +    fn new(
 +        cx: &'a LateContext<'tcx>,
 +        body: &'a mir::Body<'tcx>,
 +        possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
 +    ) -> Self {
 +        Self {
 +            possible_borrower: TransitiveRelation::default(),
 +            cx,
 +            body,
 +            possible_origin,
 +        }
 +    }
 +
 +    fn into_map(
 +        self,
 +        cx: &LateContext<'tcx>,
 +        maybe_live: ResultsCursor<'tcx, 'tcx, MaybeStorageLive>,
 +    ) -> PossibleBorrowerMap<'a, 'tcx> {
 +        let mut map = FxHashMap::default();
 +        for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
 +            if is_copy(cx, self.body.local_decls[row].ty) {
 +                continue;
 +            }
 +
 +            let borrowers = self.possible_borrower.reachable_from(&row);
 +            if !borrowers.is_empty() {
 +                let mut bs = HybridBitSet::new_empty(self.body.local_decls.len());
 +                for &c in borrowers {
 +                    if c != mir::Local::from_usize(0) {
 +                        bs.insert(c);
 +                    }
 +                }
 +
 +                if !bs.is_empty() {
 +                    map.insert(row, bs);
 +                }
 +            }
 +        }
 +
 +        let bs = BitSet::new_empty(self.body.local_decls.len());
 +        PossibleBorrowerMap {
 +            map,
 +            maybe_live,
 +            bitset: (bs.clone(), bs),
 +        }
 +    }
 +}
 +
 +impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> {
 +    fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
 +        let lhs = place.local;
 +        match rvalue {
 +            mir::Rvalue::Ref(_, _, borrowed) => {
 +                self.possible_borrower.add(borrowed.local, lhs);
 +            },
 +            other => {
 +                if ContainsRegion(self.cx.tcx)
 +                    .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty)
 +                    .is_continue()
 +                {
 +                    return;
 +                }
 +                rvalue_locals(other, |rhs| {
 +                    if lhs != rhs {
 +                        self.possible_borrower.add(rhs, lhs);
 +                    }
 +                });
 +            },
 +        }
 +    }
 +
 +    fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) {
 +        if let mir::TerminatorKind::Call {
 +            args,
 +            destination: Some((mir::Place { local: dest, .. }, _)),
 +            ..
 +        } = &terminator.kind
 +        {
 +            // TODO add doc
 +            // If the call returns something with lifetimes,
 +            // let's conservatively assume the returned value contains lifetime of all the arguments.
 +            // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`.
 +
 +            let mut immutable_borrowers = vec![];
 +            let mut mutable_borrowers = vec![];
 +
 +            for op in args {
 +                match op {
 +                    mir::Operand::Copy(p) | mir::Operand::Move(p) => {
 +                        if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() {
 +                            mutable_borrowers.push(p.local);
 +                        } else {
 +                            immutable_borrowers.push(p.local);
 +                        }
 +                    },
 +                    mir::Operand::Constant(..) => (),
 +                }
 +            }
 +
 +            let mut mutable_variables: Vec<mir::Local> = mutable_borrowers
 +                .iter()
 +                .filter_map(|r| self.possible_origin.get(r))
 +                .flat_map(HybridBitSet::iter)
 +                .collect();
 +
++            if ContainsRegion(self.cx.tcx)
++                .visit_ty(self.body.local_decls[*dest].ty)
++                .is_break()
++            {
 +                mutable_variables.push(*dest);
 +            }
 +
 +            for y in mutable_variables {
 +                for x in &immutable_borrowers {
 +                    self.possible_borrower.add(*x, y);
 +                }
 +                for x in &mutable_borrowers {
 +                    self.possible_borrower.add(*x, y);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Collect possible borrowed for every `&mut` local.
 +/// For exampel, `_1 = &mut _2` generate _1: {_2,...}
 +/// Known Problems: not sure all borrowed are tracked
 +struct PossibleOriginVisitor<'a, 'tcx> {
 +    possible_origin: TransitiveRelation<mir::Local>,
 +    body: &'a mir::Body<'tcx>,
 +}
 +
 +impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> {
 +    fn new(body: &'a mir::Body<'tcx>) -> Self {
 +        Self {
 +            possible_origin: TransitiveRelation::default(),
 +            body,
 +        }
 +    }
 +
 +    fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, HybridBitSet<mir::Local>> {
 +        let mut map = FxHashMap::default();
 +        for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
 +            if is_copy(cx, self.body.local_decls[row].ty) {
 +                continue;
 +            }
 +
 +            let borrowers = self.possible_origin.reachable_from(&row);
 +            if !borrowers.is_empty() {
 +                let mut bs = HybridBitSet::new_empty(self.body.local_decls.len());
 +                for &c in borrowers {
 +                    if c != mir::Local::from_usize(0) {
 +                        bs.insert(c);
 +                    }
 +                }
 +
 +                if !bs.is_empty() {
 +                    map.insert(row, bs);
 +                }
 +            }
 +        }
 +        map
 +    }
 +}
 +
 +impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> {
 +    fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
 +        let lhs = place.local;
 +        match rvalue {
 +            // Only consider `&mut`, which can modify origin place
 +            mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) |
 +            // _2: &mut _;
 +            // _3 = move _2
 +            mir::Rvalue::Use(mir::Operand::Move(borrowed))  |
 +            // _3 = move _2 as &mut _;
 +            mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _)
 +                => {
 +                self.possible_origin.add(lhs, borrowed.local);
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +struct ContainsRegion<'tcx>(TyCtxt<'tcx>);
 +
 +impl<'tcx> TypeVisitor<'tcx> for ContainsRegion<'tcx> {
 +    type BreakTy = ();
 +    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
 +        Some(self.0)
 +    }
 +
 +    fn visit_region(&mut self, _: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
 +        ControlFlow::BREAK
 +    }
 +}
 +
 +fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
 +    use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use};
 +
 +    let mut visit_op = |op: &mir::Operand<'_>| match op {
 +        mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local),
 +        mir::Operand::Constant(..) => (),
 +    };
 +
 +    match rvalue {
 +        Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op),
 +        Aggregate(_, ops) => ops.iter().for_each(visit_op),
 +        BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => {
 +            visit_op(lhs);
 +            visit_op(rhs);
 +        },
 +        _ => (),
 +    }
 +}
 +
 +/// Result of `PossibleBorrowerVisitor`.
 +struct PossibleBorrowerMap<'a, 'tcx> {
 +    /// Mapping `Local -> its possible borrowers`
 +    map: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
 +    maybe_live: ResultsCursor<'a, 'tcx, MaybeStorageLive>,
 +    // Caches to avoid allocation of `BitSet` on every query
 +    bitset: (BitSet<mir::Local>, BitSet<mir::Local>),
 +}
 +
 +impl PossibleBorrowerMap<'_, '_> {
 +    /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`.
 +    fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool {
 +        self.maybe_live.seek_after_primary_effect(at);
 +
 +        self.bitset.0.clear();
 +        let maybe_live = &mut self.maybe_live;
 +        if let Some(bitset) = self.map.get(&borrowed) {
 +            for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) {
 +                self.bitset.0.insert(b);
 +            }
 +        } else {
 +            return false;
 +        }
 +
 +        self.bitset.1.clear();
 +        for b in borrowers {
 +            self.bitset.1.insert(*b);
 +        }
 +
 +        self.bitset.0 == self.bitset.1
 +    }
 +
 +    fn is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {
 +        self.maybe_live.seek_after_primary_effect(at);
 +        self.maybe_live.contains(local)
 +    }
 +}
index 7314bce83e03867d83c8e6f7e2300d9485360f60,0000000000000000000000000000000000000000..90e3c3f4b3e98f78121f2d23f43bc4a1c89148f6
mode 100644,000000..100644
--- /dev/null
@@@ -1,157 -1,0 +1,155 @@@
-         if let ast::ExprKind::Ret(_) = ex.kind {
-             self.found_return = true;
-         } else if let ast::ExprKind::Try(_) = ex.kind {
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 +use clippy_utils::source::snippet_with_applicability;
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_ast::visit as ast_visit;
 +use rustc_ast::visit::Visitor as AstVisitor;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit as hir_visit;
 +use rustc_hir::intravisit::Visitor as HirVisitor;
 +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects closures called in the same expression where they
 +    /// are defined.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is unnecessarily adding to the expression's
 +    /// complexity.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// let a = (|| 42)()
 +    ///
 +    /// // Good
 +    /// let a = 42
 +    /// ```
 +    pub REDUNDANT_CLOSURE_CALL,
 +    complexity,
 +    "throwaway closures called in the expression they are defined"
 +}
 +
 +declare_lint_pass!(RedundantClosureCall => [REDUNDANT_CLOSURE_CALL]);
 +
 +// Used to find `return` statements or equivalents e.g., `?`
 +struct ReturnVisitor {
 +    found_return: bool,
 +}
 +
 +impl ReturnVisitor {
 +    #[must_use]
 +    fn new() -> Self {
 +        Self { found_return: false }
 +    }
 +}
 +
 +impl<'ast> ast_visit::Visitor<'ast> for ReturnVisitor {
 +    fn visit_expr(&mut self, ex: &'ast ast::Expr) {
++        if let ast::ExprKind::Ret(_) | ast::ExprKind::Try(_) = ex.kind {
 +            self.found_return = true;
 +        }
 +
 +        ast_visit::walk_expr(self, ex);
 +    }
 +}
 +
 +impl EarlyLintPass for RedundantClosureCall {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
 +        if in_external_macro(cx.sess, expr.span) {
 +            return;
 +        }
 +        if_chain! {
 +            if let ast::ExprKind::Call(ref paren, _) = expr.kind;
 +            if let ast::ExprKind::Paren(ref closure) = paren.kind;
 +            if let ast::ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.kind;
 +            then {
 +                let mut visitor = ReturnVisitor::new();
 +                visitor.visit_expr(block);
 +                if !visitor.found_return {
 +                    span_lint_and_then(
 +                        cx,
 +                        REDUNDANT_CLOSURE_CALL,
 +                        expr.span,
 +                        "try not to call a closure in the expression where it is declared",
 +                        |diag| {
 +                            if decl.inputs.is_empty() {
 +                                let mut app = Applicability::MachineApplicable;
 +                                let hint =
 +                                    snippet_with_applicability(cx, block.span, "..", &mut app).into_owned();
 +                                diag.span_suggestion(expr.span, "try doing something like", hint, app);
 +                            }
 +                        },
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
 +        fn count_closure_usage<'a, 'tcx>(
 +            cx: &'a LateContext<'tcx>,
 +            block: &'tcx hir::Block<'_>,
 +            path: &'tcx hir::Path<'tcx>,
 +        ) -> usize {
 +            struct ClosureUsageCount<'a, 'tcx> {
 +                cx: &'a LateContext<'tcx>,
 +                path: &'tcx hir::Path<'tcx>,
 +                count: usize,
 +            }
 +            impl<'a, 'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> {
 +                type Map = Map<'tcx>;
 +
 +                fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
 +                    if_chain! {
 +                        if let hir::ExprKind::Call(closure, _) = expr.kind;
 +                        if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind;
 +                        if self.path.segments[0].ident == path.segments[0].ident;
 +                        if self.path.res == path.res;
 +                        then {
 +                            self.count += 1;
 +                        }
 +                    }
 +                    hir_visit::walk_expr(self, expr);
 +                }
 +
 +                fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap<Self::Map> {
 +                    hir_visit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
 +                }
 +            }
 +            let mut closure_usage_count = ClosureUsageCount { cx, path, count: 0 };
 +            closure_usage_count.visit_block(block);
 +            closure_usage_count.count
 +        }
 +
 +        for w in block.stmts.windows(2) {
 +            if_chain! {
 +                if let hir::StmtKind::Local(local) = w[0].kind;
 +                if let Option::Some(t) = local.init;
 +                if let hir::ExprKind::Closure(..) = t.kind;
 +                if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind;
 +                if let hir::StmtKind::Semi(second) = w[1].kind;
 +                if let hir::ExprKind::Assign(_, call, _) = second.kind;
 +                if let hir::ExprKind::Call(closure, _) = call.kind;
 +                if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind;
 +                if ident == path.segments[0].ident;
 +                if count_closure_usage(cx, block, path) == 1;
 +                then {
 +                    span_lint(
 +                        cx,
 +                        REDUNDANT_CLOSURE_CALL,
 +                        second.span,
 +                        "closure called just once immediately after it was declared",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
index 681baed8c3696f0323cda6a590bea11e8b325798,0000000000000000000000000000000000000000..341b5a61631dff896fb93510799316fb6cb324a3
mode 100644,000000..100644
--- /dev/null
@@@ -1,301 -1,0 +1,298 @@@
-         ExprKind::Match(_, arms, source) => match source {
-             MatchSource::Normal => {
-                 for arm in arms.iter() {
-                     check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block);
-                 }
-             },
-             _ => (),
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::{fn_def_id, in_macro, path_to_local_id};
 +use if_chain::if_chain;
 +use rustc_ast::ast::Attribute;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor};
 +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::subst::GenericArgKind;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `let`-bindings, which are subsequently
 +    /// returned.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is just extraneous code. Remove it to make your code
 +    /// more rusty.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo() -> String {
 +    ///     let x = String::new();
 +    ///     x
 +    /// }
 +    /// ```
 +    /// instead, use
 +    /// ```
 +    /// fn foo() -> String {
 +    ///     String::new()
 +    /// }
 +    /// ```
 +    pub LET_AND_RETURN,
 +    style,
 +    "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for return statements at the end of a block.
 +    ///
 +    /// ### Why is this bad?
 +    /// Removing the `return` and semicolon will make the code
 +    /// more rusty.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo(x: usize) -> usize {
 +    ///     return x;
 +    /// }
 +    /// ```
 +    /// simplify to
 +    /// ```rust
 +    /// fn foo(x: usize) -> usize {
 +    ///     x
 +    /// }
 +    /// ```
 +    pub NEEDLESS_RETURN,
 +    style,
 +    "using a return statement like `return expr;` where an expression would suffice"
 +}
 +
 +#[derive(PartialEq, Eq, Copy, Clone)]
 +enum RetReplacement {
 +    Empty,
 +    Block,
 +}
 +
 +declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Return {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
 +        // we need both a let-binding stmt and an expr
 +        if_chain! {
 +            if let Some(retexpr) = block.expr;
 +            if let Some(stmt) = block.stmts.iter().last();
 +            if let StmtKind::Local(local) = &stmt.kind;
 +            if local.ty.is_none();
 +            if cx.tcx.hir().attrs(local.hir_id).is_empty();
 +            if let Some(initexpr) = &local.init;
 +            if let PatKind::Binding(_, local_id, _, _) = local.pat.kind;
 +            if path_to_local_id(retexpr, local_id);
 +            if !last_statement_borrows(cx, initexpr);
 +            if !in_external_macro(cx.sess(), initexpr.span);
 +            if !in_external_macro(cx.sess(), retexpr.span);
 +            if !in_external_macro(cx.sess(), local.span);
 +            if !in_macro(local.span);
 +            then {
 +                span_lint_and_then(
 +                    cx,
 +                    LET_AND_RETURN,
 +                    retexpr.span,
 +                    "returning the result of a `let` binding from a block",
 +                    |err| {
 +                        err.span_label(local.span, "unnecessary `let` binding");
 +
 +                        if let Some(mut snippet) = snippet_opt(cx, initexpr.span) {
 +                            if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
 +                                snippet.push_str(" as _");
 +                            }
 +                            err.multipart_suggestion(
 +                                "return the expression directly",
 +                                vec![
 +                                    (local.span, String::new()),
 +                                    (retexpr.span, snippet),
 +                                ],
 +                                Applicability::MachineApplicable,
 +                            );
 +                        } else {
 +                            err.span_help(initexpr.span, "this expression can be directly returned");
 +                        }
 +                    },
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: FnKind<'tcx>,
 +        _: &'tcx FnDecl<'tcx>,
 +        body: &'tcx Body<'tcx>,
 +        _: Span,
 +        _: HirId,
 +    ) {
 +        match kind {
 +            FnKind::Closure => {
 +                // when returning without value in closure, replace this `return`
 +                // with an empty block to prevent invalid suggestion (see #6501)
 +                let replacement = if let ExprKind::Ret(None) = &body.value.kind {
 +                    RetReplacement::Block
 +                } else {
 +                    RetReplacement::Empty
 +                };
 +                check_final_expr(cx, &body.value, Some(body.value.span), replacement);
 +            },
 +            FnKind::ItemFn(..) | FnKind::Method(..) => {
 +                if let ExprKind::Block(block, _) = body.value.kind {
 +                    check_block_return(cx, block);
 +                }
 +            },
 +        }
 +    }
 +}
 +
 +fn attr_is_cfg(attr: &Attribute) -> bool {
 +    attr.meta_item_list().is_some() && attr.has_name(sym::cfg)
 +}
 +
 +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) {
 +    if let Some(expr) = block.expr {
 +        check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty);
 +    } else if let Some(stmt) = block.stmts.iter().last() {
 +        match stmt.kind {
 +            StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
 +                check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
 +            },
 +            _ => (),
 +        }
 +    }
 +}
 +
 +fn check_final_expr<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    span: Option<Span>,
 +    replacement: RetReplacement,
 +) {
 +    match expr.kind {
 +        // simple return is always "bad"
 +        ExprKind::Ret(ref inner) => {
 +            // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
 +            let attrs = cx.tcx.hir().attrs(expr.hir_id);
 +            if !attrs.iter().any(attr_is_cfg) {
 +                let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
 +                if !borrows {
 +                    emit_return_lint(
 +                        cx,
 +                        span.expect("`else return` is not possible"),
 +                        inner.as_ref().map(|i| i.span),
 +                        replacement,
 +                    );
 +                }
 +            }
 +        },
 +        // a whole block? check it!
 +        ExprKind::Block(block, _) => {
 +            check_block_return(cx, block);
 +        },
 +        ExprKind::If(_, then, else_clause_opt) => {
 +            if let ExprKind::Block(ifblock, _) = then.kind {
 +                check_block_return(cx, ifblock);
 +            }
 +            if let Some(else_clause) = else_clause_opt {
 +                check_final_expr(cx, else_clause, None, RetReplacement::Empty);
 +            }
 +        },
 +        // a match expr, check all arms
 +        // an if/if let expr, check both exprs
 +        // note, if without else is going to be a type checking error anyways
 +        // (except for unit type functions) so we don't match it
++        ExprKind::Match(_, arms, MatchSource::Normal) => {
++            for arm in arms.iter() {
++                check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block);
++            }
 +        },
 +        ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty),
 +        _ => (),
 +    }
 +}
 +
 +fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option<Span>, replacement: RetReplacement) {
 +    if ret_span.from_expansion() {
 +        return;
 +    }
 +    match inner_span {
 +        Some(inner_span) => {
 +            if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() {
 +                return;
 +            }
 +
 +            span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
 +                if let Some(snippet) = snippet_opt(cx, inner_span) {
 +                    diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable);
 +                }
 +            });
 +        },
 +        None => match replacement {
 +            RetReplacement::Empty => {
 +                span_lint_and_sugg(
 +                    cx,
 +                    NEEDLESS_RETURN,
 +                    ret_span,
 +                    "unneeded `return` statement",
 +                    "remove `return`",
 +                    String::new(),
 +                    Applicability::MachineApplicable,
 +                );
 +            },
 +            RetReplacement::Block => {
 +                span_lint_and_sugg(
 +                    cx,
 +                    NEEDLESS_RETURN,
 +                    ret_span,
 +                    "unneeded `return` statement",
 +                    "replace `return` with an empty block",
 +                    "{}".to_string(),
 +                    Applicability::MachineApplicable,
 +                );
 +            },
 +        },
 +    }
 +}
 +
 +fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
 +    let mut visitor = BorrowVisitor { cx, borrows: false };
 +    walk_expr(&mut visitor, expr);
 +    visitor.borrows
 +}
 +
 +struct BorrowVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    borrows: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.borrows {
 +            return;
 +        }
 +
 +        if let Some(def_id) = fn_def_id(self.cx, expr) {
 +            self.borrows = self
 +                .cx
 +                .tcx
 +                .fn_sig(def_id)
 +                .output()
 +                .skip_binder()
 +                .walk(self.cx.tcx)
 +                .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
index 1a78a4968e5a3a0030b57de6c1e1518674312409,0000000000000000000000000000000000000000..13d8f954c44138cd1bad5835adac4d8f7251d496
mode 100644,000000..100644
--- /dev/null
@@@ -1,413 -1,0 +1,413 @@@
-             if let ExprKind::MethodCall(path, _, args, _) = &expr.kind;
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg};
 +use clippy_utils::source::{snippet, snippet_with_applicability};
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::SpanlessEq;
 +use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Spanned;
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for string appends of the form `x = x + y` (without
 +    /// `let`!).
 +    ///
 +    /// ### Why is this bad?
 +    /// It's not really bad, but some people think that the
 +    /// `.push_str(_)` method is more readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut x = "Hello".to_owned();
 +    /// x = x + ", World";
 +    ///
 +    /// // More readable
 +    /// x += ", World";
 +    /// x.push_str(", World");
 +    /// ```
 +    pub STRING_ADD_ASSIGN,
 +    pedantic,
 +    "using `x = x + ..` where x is a `String` instead of `push_str()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for all instances of `x + _` where `x` is of type
 +    /// `String`, but only if [`string_add_assign`](#string_add_assign) does *not*
 +    /// match.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's not bad in and of itself. However, this particular
 +    /// `Add` implementation is asymmetric (the other operand need not be `String`,
 +    /// but `x` does), while addition as mathematically defined is symmetric, also
 +    /// the `String::push_str(_)` function is a perfectly good replacement.
 +    /// Therefore, some dislike it and wish not to have it in their code.
 +    ///
 +    /// That said, other people think that string addition, having a long tradition
 +    /// in other languages is actually fine, which is why we decided to make this
 +    /// particular lint `allow` by default.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = "Hello".to_owned();
 +    /// x + ", World";
 +    /// ```
 +    pub STRING_ADD,
 +    restriction,
 +    "using `x + ..` where x is a `String` instead of `push_str()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the `as_bytes` method called on string literals
 +    /// that contain only ASCII characters.
 +    ///
 +    /// ### Why is this bad?
 +    /// Byte string literals (e.g., `b"foo"`) can be used
 +    /// instead. They are shorter but less discoverable than `as_bytes()`.
 +    ///
 +    /// ### Known problems
 +    /// `"str".as_bytes()` and the suggested replacement of `b"str"` are not
 +    /// equivalent because they have different types. The former is `&[u8]`
 +    /// while the latter is `&[u8; 3]`. That means in general they will have a
 +    /// different set of methods and different trait implementations.
 +    ///
 +    /// ```compile_fail
 +    /// fn f(v: Vec<u8>) {}
 +    ///
 +    /// f("...".as_bytes().to_owned()); // works
 +    /// f(b"...".to_owned()); // does not work, because arg is [u8; 3] not Vec<u8>
 +    ///
 +    /// fn g(r: impl std::io::Read) {}
 +    ///
 +    /// g("...".as_bytes()); // works
 +    /// g(b"..."); // does not work
 +    /// ```
 +    ///
 +    /// The actual equivalent of `"str".as_bytes()` with the same type is not
 +    /// `b"str"` but `&b"str"[..]`, which is a great deal of punctuation and not
 +    /// more readable than a function call.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let bs = "a byte string".as_bytes();
 +    ///
 +    /// // Good
 +    /// let bs = b"a byte string";
 +    /// ```
 +    pub STRING_LIT_AS_BYTES,
 +    nursery,
 +    "calling `as_bytes` on a string literal instead of using a byte string literal"
 +}
 +
 +declare_lint_pass!(StringAdd => [STRING_ADD, STRING_ADD_ASSIGN]);
 +
 +impl<'tcx> LateLintPass<'tcx> for StringAdd {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if in_external_macro(cx.sess(), e.span) {
 +            return;
 +        }
 +
 +        if let ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Add, ..
 +            },
 +            left,
 +            _,
 +        ) = e.kind
 +        {
 +            if is_string(cx, left) {
 +                if !is_lint_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) {
 +                    let parent = get_parent_expr(cx, e);
 +                    if let Some(p) = parent {
 +                        if let ExprKind::Assign(target, _, _) = p.kind {
 +                            // avoid duplicate matches
 +                            if SpanlessEq::new(cx).eq_expr(target, left) {
 +                                return;
 +                            }
 +                        }
 +                    }
 +                }
 +                span_lint(
 +                    cx,
 +                    STRING_ADD,
 +                    e.span,
 +                    "you added something to a string. Consider using `String::push_str()` instead",
 +                );
 +            }
 +        } else if let ExprKind::Assign(target, src, _) = e.kind {
 +            if is_string(cx, target) && is_add(cx, src, target) {
 +                span_lint(
 +                    cx,
 +                    STRING_ADD_ASSIGN,
 +                    e.span,
 +                    "you assigned the result of adding something to this string. Consider using \
 +                     `String::push_str()` instead",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::string_type)
 +}
 +
 +fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
 +    match src.kind {
 +        ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Add, ..
 +            },
 +            left,
 +            _,
 +        ) => SpanlessEq::new(cx).eq_expr(target, left),
 +        ExprKind::Block(block, _) => {
 +            block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target))
 +        },
 +        _ => false,
 +    }
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Check if the string is transformed to byte array and casted back to string.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's unnecessary, the string can be used directly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap();
 +    /// ```
 +    /// could be written as
 +    /// ```rust
 +    /// let _ = &"Hello World!"[6..11];
 +    /// ```
 +    pub STRING_FROM_UTF8_AS_BYTES,
 +    complexity,
 +    "casting string slices to byte slices and back"
 +}
 +
 +// Max length a b"foo" string can take
 +const MAX_LENGTH_BYTE_STRING_LIT: usize = 32;
 +
 +declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES, STRING_FROM_UTF8_AS_BYTES]);
 +
 +impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        use rustc_ast::LitKind;
 +
 +        if_chain! {
 +            // Find std::str::converts::from_utf8
 +            if let Some(args) = match_function_call(cx, e, &paths::STR_FROM_UTF8);
 +
 +            // Find string::as_bytes
 +            if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind;
 +            if let ExprKind::Index(left, right) = args.kind;
 +            let (method_names, expressions, _) = method_calls(left, 1);
 +            if method_names.len() == 1;
 +            if expressions.len() == 1;
 +            if expressions[0].len() == 1;
 +            if method_names[0] == sym!(as_bytes);
 +
 +            // Check for slicer
 +            if let ExprKind::Struct(QPath::LangItem(LangItem::Range, _), _, _) = right.kind;
 +
 +            then {
 +                let mut applicability = Applicability::MachineApplicable;
 +                let string_expression = &expressions[0][0];
 +
 +                let snippet_app = snippet_with_applicability(
 +                    cx,
 +                    string_expression.span, "..",
 +                    &mut applicability,
 +                );
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    STRING_FROM_UTF8_AS_BYTES,
 +                    e.span,
 +                    "calling a slice of `as_bytes()` with `from_utf8` should be not necessary",
 +                    "try",
 +                    format!("Some(&{}[{}])", snippet_app, snippet(cx, right.span, "..")),
 +                    applicability
 +                )
 +            }
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::MethodCall(path, _, args, _) = &e.kind;
 +            if path.ident.name == sym!(as_bytes);
 +            if let ExprKind::Lit(lit) = &args[0].kind;
 +            if let LitKind::Str(lit_content, _) = &lit.node;
 +            then {
 +                let callsite = snippet(cx, args[0].span.source_callsite(), r#""foo""#);
 +                let mut applicability = Applicability::MachineApplicable;
 +                if callsite.starts_with("include_str!") {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        STRING_LIT_AS_BYTES,
 +                        e.span,
 +                        "calling `as_bytes()` on `include_str!(..)`",
 +                        "consider using `include_bytes!(..)` instead",
 +                        snippet_with_applicability(cx, args[0].span, r#""foo""#, &mut applicability).replacen(
 +                            "include_str",
 +                            "include_bytes",
 +                            1,
 +                        ),
 +                        applicability,
 +                    );
 +                } else if lit_content.as_str().is_ascii()
 +                    && lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT
 +                    && !args[0].span.from_expansion()
 +                {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        STRING_LIT_AS_BYTES,
 +                        e.span,
 +                        "calling `as_bytes()` on a string literal",
 +                        "consider using a byte string literal instead",
 +                        format!(
 +                            "b{}",
 +                            snippet_with_applicability(cx, args[0].span, r#""foo""#, &mut applicability)
 +                        ),
 +                        applicability,
 +                    );
 +                }
 +            }
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::MethodCall(path, _, [recv], _) = &e.kind;
 +            if path.ident.name == sym!(into_bytes);
 +            if let ExprKind::MethodCall(path, _, [recv], _) = &recv.kind;
 +            if matches!(&*path.ident.name.as_str(), "to_owned" | "to_string");
 +            if let ExprKind::Lit(lit) = &recv.kind;
 +            if let LitKind::Str(lit_content, _) = &lit.node;
 +
 +            if lit_content.as_str().is_ascii();
 +            if lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT;
 +            if !recv.span.from_expansion();
 +            then {
 +                let mut applicability = Applicability::MachineApplicable;
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    STRING_LIT_AS_BYTES,
 +                    e.span,
 +                    "calling `into_bytes()` on a string literal",
 +                    "consider using a byte string literal instead",
 +                    format!(
 +                        "b{}.to_vec()",
 +                        snippet_with_applicability(cx, recv.span, r#""..""#, &mut applicability)
 +                    ),
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for `.to_string()` method calls on values of type `&str`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `to_string` method is also used on other types to convert them to a string.
 +    /// When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better
 +    /// expressed with `.to_owned()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // example code where clippy issues a warning
 +    /// let _ = "str".to_string();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// // example code which does not raise clippy warning
 +    /// let _ = "str".to_owned();
 +    /// ```
 +    pub STR_TO_STRING,
 +    restriction,
 +    "using `to_string()` on a `&str`, which should be `to_owned()`"
 +}
 +
 +declare_lint_pass!(StrToString => [STR_TO_STRING]);
 +
 +impl LateLintPass<'_> for StrToString {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
 +        if_chain! {
-             let ty = cx.typeck_results().expr_ty(&args[0]);
++            if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind;
 +            if path.ident.name == sym!(to_string);
-             if let ExprKind::MethodCall(path, _, args, _) = &expr.kind;
++            let ty = cx.typeck_results().expr_ty(self_arg);
 +            if let ty::Ref(_, ty, ..) = ty.kind();
 +            if *ty.kind() == ty::Str;
 +            then {
 +                span_lint_and_help(
 +                    cx,
 +                    STR_TO_STRING,
 +                    expr.span,
 +                    "`to_string()` called on a `&str`",
 +                    None,
 +                    "consider using `.to_owned()`",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for `.to_string()` method calls on values of type `String`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `to_string` method is also used on other types to convert them to a string.
 +    /// When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // example code where clippy issues a warning
 +    /// let msg = String::from("Hello World");
 +    /// let _ = msg.to_string();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// // example code which does not raise clippy warning
 +    /// let msg = String::from("Hello World");
 +    /// let _ = msg.clone();
 +    /// ```
 +    pub STRING_TO_STRING,
 +    restriction,
 +    "using `to_string()` on a `String`, which should be `clone()`"
 +}
 +
 +declare_lint_pass!(StringToString => [STRING_TO_STRING]);
 +
 +impl LateLintPass<'_> for StringToString {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
 +        if_chain! {
-             let ty = cx.typeck_results().expr_ty(&args[0]);
++            if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind;
 +            if path.ident.name == sym!(to_string);
++            let ty = cx.typeck_results().expr_ty(self_arg);
 +            if is_type_diagnostic_item(cx, ty, sym::string_type);
 +            then {
 +                span_lint_and_help(
 +                    cx,
 +                    STRING_TO_STRING,
 +                    expr.span,
 +                    "`to_string()` called on a `String`",
 +                    None,
 +                    "consider using `.clone()`",
 +                );
 +            }
 +        }
 +    }
 +}
index b036ed9a3d2e7f48f6774f2a3475fafb9694dba2,0000000000000000000000000000000000000000..b7414cec87cd2f3a6600d2525d61a3bea3625e88
mode 100644,000000..100644
--- /dev/null
@@@ -1,122 -1,0 +1,122 @@@
-             if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::{is_diag_trait_item, match_def_path, path_to_local_id, paths};
 +use if_chain::if_chain;
 +use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for uses of `to_string()` in `Display` traits.
 +    ///
 +    /// ### Why is this bad?
 +    /// Usually `to_string` is implemented indirectly
 +    /// via `Display`. Hence using it while implementing `Display` would
 +    /// lead to infinite recursion.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust
 +    /// use std::fmt;
 +    ///
 +    /// struct Structure(i32);
 +    /// impl fmt::Display for Structure {
 +    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 +    ///         write!(f, "{}", self.to_string())
 +    ///     }
 +    /// }
 +    ///
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::fmt;
 +    ///
 +    /// struct Structure(i32);
 +    /// impl fmt::Display for Structure {
 +    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 +    ///         write!(f, "{}", self.0)
 +    ///     }
 +    /// }
 +    /// ```
 +    pub TO_STRING_IN_DISPLAY,
 +    correctness,
 +    "`to_string` method used while implementing `Display` trait"
 +}
 +
 +#[derive(Default)]
 +pub struct ToStringInDisplay {
 +    in_display_impl: bool,
 +    self_hir_id: Option<HirId>,
 +}
 +
 +impl ToStringInDisplay {
 +    pub fn new() -> Self {
 +        Self {
 +            in_display_impl: false,
 +            self_hir_id: None,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(ToStringInDisplay => [TO_STRING_IN_DISPLAY]);
 +
 +impl LateLintPass<'_> for ToStringInDisplay {
 +    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
 +        if is_display_impl(cx, item) {
 +            self.in_display_impl = true;
 +        }
 +    }
 +
 +    fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
 +        if is_display_impl(cx, item) {
 +            self.in_display_impl = false;
 +            self.self_hir_id = None;
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
 +        if_chain! {
 +            if self.in_display_impl;
 +            if let ImplItemKind::Fn(.., body_id) = &impl_item.kind;
 +            let body = cx.tcx.hir().body(*body_id);
 +            if !body.params.is_empty();
 +            then {
 +                let self_param = &body.params[0];
 +                self.self_hir_id = Some(self_param.pat.hir_id);
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        if_chain! {
 +            if self.in_display_impl;
 +            if let Some(self_hir_id) = self.self_hir_id;
-             if path_to_local_id(&args[0], self_hir_id);
++            if let ExprKind::MethodCall(path, _, [ref self_arg, ..], _) = expr.kind;
 +            if path.ident.name == sym!(to_string);
 +            if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
 +            if is_diag_trait_item(cx, expr_def_id, sym::ToString);
++            if path_to_local_id(self_arg, self_hir_id);
 +            then {
 +                span_lint(
 +                    cx,
 +                    TO_STRING_IN_DISPLAY,
 +                    expr.span,
 +                    "using `to_string` in `fmt::Display` implementation might lead to infinite recursion",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn is_display_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
 +    if_chain! {
 +        if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind;
 +        if let Some(did) = trait_ref.trait_def_id();
 +        then {
 +            match_def_path(cx, did, &paths::DISPLAY_TRAIT)
 +        } else {
 +            false
 +        }
 +    }
 +}
index 371bb8b445a718eceba770325edb1a33fce9ce92,0000000000000000000000000000000000000000..9588de8459cfe3f337b2351cc6afc5b6c6e8b800
mode 100644,000000..100644
--- /dev/null
@@@ -1,497 -1,0 +1,544 @@@
-             ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty(cx, ty, CheckTyContext::default()),
 +mod borrowed_box;
 +mod box_vec;
 +mod linked_list;
 +mod option_option;
 +mod rc_buffer;
 +mod rc_mutex;
 +mod redundant_allocation;
 +mod type_complexity;
 +mod utils;
 +mod vec_box;
 +
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::FnKind;
 +use rustc_hir::{
 +    Body, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitItem,
 +    TraitItemKind, TyKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `Box<Vec<_>>` anywhere in the code.
 +    /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
 +    ///
 +    /// ### Why is this bad?
 +    /// `Vec` already keeps its contents in a separate area on
 +    /// the heap. So if you `Box` it, you just add another level of indirection
 +    /// without any benefit whatsoever.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// struct X {
 +    ///     values: Box<Vec<Foo>>,
 +    /// }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust,ignore
 +    /// struct X {
 +    ///     values: Vec<Foo>,
 +    /// }
 +    /// ```
 +    pub BOX_VEC,
 +    perf,
 +    "usage of `Box<Vec<T>>`, vector elements are already on the heap"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code.
 +    /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
 +    ///
 +    /// ### Why is this bad?
 +    /// `Vec` already keeps its contents in a separate area on
 +    /// the heap. So if you `Box` its contents, you just add another level of indirection.
 +    ///
 +    /// ### Known problems
 +    /// Vec<Box<T: Sized>> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530),
 +    /// 1st comment).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct X {
 +    ///     values: Vec<Box<i32>>,
 +    /// }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust
 +    /// struct X {
 +    ///     values: Vec<i32>,
 +    /// }
 +    /// ```
 +    pub VEC_BOX,
 +    complexity,
 +    "usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `Option<Option<_>>` in function signatures and type
 +    /// definitions
 +    ///
 +    /// ### Why is this bad?
 +    /// `Option<_>` represents an optional value. `Option<Option<_>>`
 +    /// represents an optional optional value which is logically the same thing as an optional
 +    /// value but has an unneeded extra level of wrapping.
 +    ///
 +    /// If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases,
 +    /// consider a custom `enum` instead, with clear names for each case.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn get_data() -> Option<Option<u32>> {
 +    ///     None
 +    /// }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust
 +    /// pub enum Contents {
 +    ///     Data(Vec<u8>), // Was Some(Some(Vec<u8>))
 +    ///     NotYetFetched, // Was Some(None)
 +    ///     None,          // Was None
 +    /// }
 +    ///
 +    /// fn get_data() -> Contents {
 +    ///     Contents::None
 +    /// }
 +    /// ```
 +    pub OPTION_OPTION,
 +    pedantic,
 +    "usage of `Option<Option<T>>`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of any `LinkedList`, suggesting to use a
 +    /// `Vec` or a `VecDeque` (formerly called `RingBuf`).
 +    ///
 +    /// ### Why is this bad?
 +    /// Gankro says:
 +    ///
 +    /// > The TL;DR of `LinkedList` is that it's built on a massive amount of
 +    /// pointers and indirection.
 +    /// > It wastes memory, it has terrible cache locality, and is all-around slow.
 +    /// `RingBuf`, while
 +    /// > "only" amortized for push/pop, should be faster in the general case for
 +    /// almost every possible
 +    /// > workload, and isn't even amortized at all if you can predict the capacity
 +    /// you need.
 +    /// >
 +    /// > `LinkedList`s are only really good if you're doing a lot of merging or
 +    /// splitting of lists.
 +    /// > This is because they can just mangle some pointers instead of actually
 +    /// copying the data. Even
 +    /// > if you're doing a lot of insertion in the middle of the list, `RingBuf`
 +    /// can still be better
 +    /// > because of how expensive it is to seek to the middle of a `LinkedList`.
 +    ///
 +    /// ### Known problems
 +    /// False positives – the instances where using a
 +    /// `LinkedList` makes sense are few and far between, but they can still happen.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::LinkedList;
 +    /// let x: LinkedList<usize> = LinkedList::new();
 +    /// ```
 +    pub LINKEDLIST,
 +    pedantic,
 +    "usage of LinkedList, usually a vector is faster, or a more specialized data structure like a `VecDeque`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `&Box<T>` anywhere in the code.
 +    /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
 +    ///
 +    /// ### Why is this bad?
 +    /// Any `&Box<T>` can also be a `&T`, which is more
 +    /// general.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// fn foo(bar: &Box<T>) { ... }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust,ignore
 +    /// fn foo(bar: &T) { ... }
 +    /// ```
 +    pub BORROWED_BOX,
 +    complexity,
 +    "a borrow of a boxed type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of redundant allocations anywhere in the code.
 +    ///
 +    /// ### Why is this bad?
 +    /// Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Arc<T>>`, `Rc<Box<T>>`, `Arc<&T>`, `Arc<Rc<T>>`,
 +    /// `Arc<Arc<T>>`, `Arc<Box<T>>`, `Box<&T>`, `Box<Rc<T>>`, `Box<Arc<T>>`, `Box<Box<T>>`, add an unnecessary level of indirection.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// fn foo(bar: Rc<&usize>) {}
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust
 +    /// fn foo(bar: &usize) {}
 +    /// ```
 +    pub REDUNDANT_ALLOCATION,
 +    perf,
 +    "redundant allocation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `Rc<T>` and `Arc<T>` when `T` is a mutable buffer type such as `String` or `Vec`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Expressions such as `Rc<String>` usually have no advantage over `Rc<str>`, since
 +    /// it is larger and involves an extra level of indirection, and doesn't implement `Borrow<str>`.
 +    ///
 +    /// While mutating a buffer type would still be possible with `Rc::get_mut()`, it only
 +    /// works if there are no additional references yet, which usually defeats the purpose of
 +    /// enclosing it in a shared ownership type. Instead, additionally wrapping the inner
 +    /// type with an interior mutable container (such as `RefCell` or `Mutex`) would normally
 +    /// be used.
 +    ///
 +    /// ### Known problems
 +    /// This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for
 +    /// cases where mutation only happens before there are any additional references.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// # use std::rc::Rc;
 +    /// fn foo(interned: Rc<String>) { ... }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust,ignore
 +    /// fn foo(interned: Rc<str>) { ... }
 +    /// ```
 +    pub RC_BUFFER,
 +    restriction,
 +    "shared ownership of a buffer type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for types used in structs, parameters and `let`
 +    /// declarations above a certain complexity threshold.
 +    ///
 +    /// ### Why is this bad?
 +    /// Too complex types make the code less readable. Consider
 +    /// using a `type` definition to simplify them.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// struct Foo {
 +    ///     inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>,
 +    /// }
 +    /// ```
 +    pub TYPE_COMPLEXITY,
 +    complexity,
 +    "usage of very complex types that might be better factored into `type` definitions"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `Rc<Mutex<T>>`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `Rc` is used in single thread and `Mutex` is used in multi thread.
 +    /// Consider using `Rc<RefCell<T>>` in single thread or `Arc<Mutex<T>>` in multi thread.
 +    ///
 +    /// ### Known problems
 +    /// Sometimes combining generic types can lead to the requirement that a
 +    /// type use Rc in conjunction with Mutex. We must consider those cases false positives, but
 +    /// alas they are quite hard to rule out. Luckily they are also rare.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// use std::rc::Rc;
 +    /// use std::sync::Mutex;
 +    /// fn foo(interned: Rc<Mutex<i32>>) { ... }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust,ignore
 +    /// use std::rc::Rc;
 +    /// use std::cell::RefCell
 +    /// fn foo(interned: Rc<RefCell<i32>>) { ... }
 +    /// ```
 +    pub RC_MUTEX,
 +    restriction,
 +    "usage of `Rc<Mutex<T>>`"
 +}
 +
 +pub struct Types {
 +    vec_box_size_threshold: u64,
 +    type_complexity_threshold: u64,
++    avoid_breaking_exported_api: bool,
 +}
 +
 +impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Types {
 +    fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) {
 +        let is_in_trait_impl = if let Some(hir::Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_item(id))
 +        {
 +            matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
 +        } else {
 +            false
 +        };
 +
++        let is_exported = cx.access_levels.is_exported(cx.tcx.hir().local_def_id(id));
++
 +        self.check_fn_decl(
 +            cx,
 +            decl,
 +            CheckTyContext {
 +                is_in_trait_impl,
++                is_exported,
 +                ..CheckTyContext::default()
 +            },
 +        );
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
++        let is_exported = cx.access_levels.is_exported(item.def_id);
++
 +        match item.kind {
-         self.check_ty(cx, field.ty, CheckTyContext::default());
++            ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty(
++                cx,
++                ty,
++                CheckTyContext {
++                    is_exported,
++                    ..CheckTyContext::default()
++                },
++            ),
 +            // functions, enums, structs, impls and traits are covered
 +            _ => (),
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
 +        match item.kind {
 +            ImplItemKind::Const(ty, _) | ImplItemKind::TyAlias(ty) => self.check_ty(
 +                cx,
 +                ty,
 +                CheckTyContext {
 +                    is_in_trait_impl: true,
 +                    ..CheckTyContext::default()
 +                },
 +            ),
 +            // methods are covered by check_fn
 +            ImplItemKind::Fn(..) => (),
 +        }
 +    }
 +
 +    fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) {
-     fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
++        let is_exported = cx.access_levels.is_exported(cx.tcx.hir().local_def_id(field.hir_id));
++
++        self.check_ty(
++            cx,
++            field.ty,
++            CheckTyContext {
++                is_exported,
++                ..CheckTyContext::default()
++            },
++        );
 +    }
 +
-                 self.check_ty(cx, ty, CheckTyContext::default());
++    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &TraitItem<'_>) {
++        let is_exported = cx.access_levels.is_exported(item.def_id);
++
++        let context = CheckTyContext {
++            is_exported,
++            ..CheckTyContext::default()
++        };
++
 +        match item.kind {
 +            TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => {
-             TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, CheckTyContext::default()),
++                self.check_ty(cx, ty, context);
 +            },
-     pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64) -> Self {
++            TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, context),
 +            TraitItemKind::Type(..) => (),
 +        }
 +    }
 +
 +    fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
 +        if let Some(ty) = local.ty {
 +            self.check_ty(
 +                cx,
 +                ty,
 +                CheckTyContext {
 +                    is_local: true,
 +                    ..CheckTyContext::default()
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +impl Types {
-                     let mut triggered = false;
-                     triggered |= box_vec::check(cx, hir_ty, qpath, def_id);
-                     triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id);
-                     triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id);
-                     triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold);
-                     triggered |= option_option::check(cx, hir_ty, qpath, def_id);
-                     triggered |= linked_list::check(cx, hir_ty, def_id);
-                     triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id);
-                     if triggered {
-                         return;
++    pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64, avoid_breaking_exported_api: bool) -> Self {
 +        Self {
 +            vec_box_size_threshold,
 +            type_complexity_threshold,
++            avoid_breaking_exported_api,
 +        }
 +    }
 +
 +    fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, context: CheckTyContext) {
 +        for input in decl.inputs {
 +            self.check_ty(cx, input, context);
 +        }
 +
 +        if let FnRetTy::Return(ty) = decl.output {
 +            self.check_ty(cx, ty, context);
 +        }
 +    }
 +
 +    /// Recursively check for `TypePass` lints in the given type. Stop at the first
 +    /// lint found.
 +    ///
 +    /// The parameter `is_local` distinguishes the context of the type.
 +    fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, mut context: CheckTyContext) {
 +        if hir_ty.span.from_expansion() {
 +            return;
 +        }
 +
 +        if !context.is_nested_call && type_complexity::check(cx, hir_ty, self.type_complexity_threshold) {
 +            return;
 +        }
 +
 +        // Skip trait implementations; see issue #605.
 +        if context.is_in_trait_impl {
 +            return;
 +        }
 +
 +        match hir_ty.kind {
 +            TyKind::Path(ref qpath) if !context.is_local => {
 +                let hir_id = hir_ty.hir_id;
 +                let res = cx.qpath_res(qpath, hir_id);
 +                if let Some(def_id) = res.opt_def_id() {
++                    if self.is_type_change_allowed(context) {
++                        // All lints that are being checked in this block are guarded by
++                        // the `avoid_breaking_exported_api` configuration. When adding a
++                        // new lint, please also add the name to the configuration documentation
++                        // in `clippy_lints::utils::conf.rs`
++
++                        let mut triggered = false;
++                        triggered |= box_vec::check(cx, hir_ty, qpath, def_id);
++                        triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id);
++                        triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id);
++                        triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold);
++                        triggered |= option_option::check(cx, hir_ty, qpath, def_id);
++                        triggered |= linked_list::check(cx, hir_ty, def_id);
++                        triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id);
++
++                        if triggered {
++                            return;
++                        }
 +                    }
 +                }
 +                match *qpath {
 +                    QPath::Resolved(Some(ty), p) => {
 +                        context.is_nested_call = true;
 +                        self.check_ty(cx, ty, context);
 +                        for ty in p.segments.iter().flat_map(|seg| {
 +                            seg.args
 +                                .as_ref()
 +                                .map_or_else(|| [].iter(), |params| params.args.iter())
 +                                .filter_map(|arg| match arg {
 +                                    GenericArg::Type(ty) => Some(ty),
 +                                    _ => None,
 +                                })
 +                        }) {
 +                            self.check_ty(cx, ty, context);
 +                        }
 +                    },
 +                    QPath::Resolved(None, p) => {
 +                        context.is_nested_call = true;
 +                        for ty in p.segments.iter().flat_map(|seg| {
 +                            seg.args
 +                                .as_ref()
 +                                .map_or_else(|| [].iter(), |params| params.args.iter())
 +                                .filter_map(|arg| match arg {
 +                                    GenericArg::Type(ty) => Some(ty),
 +                                    _ => None,
 +                                })
 +                        }) {
 +                            self.check_ty(cx, ty, context);
 +                        }
 +                    },
 +                    QPath::TypeRelative(ty, seg) => {
 +                        context.is_nested_call = true;
 +                        self.check_ty(cx, ty, context);
 +                        if let Some(params) = seg.args {
 +                            for ty in params.args.iter().filter_map(|arg| match arg {
 +                                GenericArg::Type(ty) => Some(ty),
 +                                _ => None,
 +                            }) {
 +                                self.check_ty(cx, ty, context);
 +                            }
 +                        }
 +                    },
 +                    QPath::LangItem(..) => {},
 +                }
 +            },
 +            TyKind::Rptr(ref lt, ref mut_ty) => {
 +                context.is_nested_call = true;
 +                if !borrowed_box::check(cx, hir_ty, lt, mut_ty) {
 +                    self.check_ty(cx, mut_ty.ty, context);
 +                }
 +            },
 +            TyKind::Slice(ty) | TyKind::Array(ty, _) | TyKind::Ptr(MutTy { ty, .. }) => {
 +                context.is_nested_call = true;
 +                self.check_ty(cx, ty, context);
 +            },
 +            TyKind::Tup(tys) => {
 +                context.is_nested_call = true;
 +                for ty in tys {
 +                    self.check_ty(cx, ty, context);
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
++
++    /// This function checks if the type is allowed to change in the current context
++    /// based on the `avoid_breaking_exported_api` configuration
++    fn is_type_change_allowed(&self, context: CheckTyContext) -> bool {
++        !(context.is_exported && self.avoid_breaking_exported_api)
++    }
 +}
 +
++#[allow(clippy::struct_excessive_bools)]
 +#[derive(Clone, Copy, Default)]
 +struct CheckTyContext {
 +    is_in_trait_impl: bool,
++    /// `true` for types on local variables.
 +    is_local: bool,
++    /// `true` for types that are part of the public API.
++    is_exported: bool,
 +    is_nested_call: bool,
 +}
index bd7a0ee6408fa2078868c1d493d8d4192dc9ad30,0000000000000000000000000000000000000000..12db7afb81ca113dd822582d6b16c65e815a3b39
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,28 @@@
- use clippy_utils::diagnostics::span_lint;
++use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::is_ty_param_diagnostic_item;
 +use if_chain::if_chain;
 +use rustc_hir::{self as hir, def_id::DefId, QPath};
 +use rustc_lint::LateContext;
 +use rustc_span::symbol::sym;
 +
 +use super::RC_MUTEX;
 +
 +pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
 +    if_chain! {
 +        if cx.tcx.is_diagnostic_item(sym::Rc, def_id) ;
 +        if let Some(_) = is_ty_param_diagnostic_item(cx, qpath, sym!(mutex_type)) ;
-         then{
-             span_lint(
++        then {
++            span_lint_and_help(
 +                cx,
 +                RC_MUTEX,
 +                hir_ty.span,
-                 "found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead",
++                "usage of `Rc<Mutex<_>>`",
++                None,
++                "consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead",
 +            );
 +            return true;
 +        }
 +    }
 +
 +    false
 +}
index 8e83dcbf704e0930fecd8384910e51acbc10fa9e,0000000000000000000000000000000000000000..ac7bdd6a1ebebf318c6e8eab2a46bbcb999b2328
mode 100644,000000..100644
--- /dev/null
@@@ -1,107 -1,0 +1,113 @@@
-         Some(ty) => ty.span,
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::{snippet, snippet_with_applicability};
 +use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item};
 +use rustc_errors::Applicability;
 +use rustc_hir::{self as hir, def_id::DefId, LangItem, QPath, TyKind};
 +use rustc_lint::LateContext;
 +use rustc_span::symbol::sym;
 +
 +use super::{utils, REDUNDANT_ALLOCATION};
 +
 +pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
 +    let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() {
 +        "Box"
 +    } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
 +        "Rc"
 +    } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
 +        "Arc"
 +    } else {
 +        return false;
 +    };
 +
 +    if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
 +        let mut applicability = Applicability::MaybeIncorrect;
 +        let generic_snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
 +        span_lint_and_then(
 +            cx,
 +            REDUNDANT_ALLOCATION,
 +            hir_ty.span,
 +            &format!("usage of `{}<{}>`", outer_sym, generic_snippet),
 +            |diag| {
 +                diag.span_suggestion(hir_ty.span, "try", format!("{}", generic_snippet), applicability);
 +                diag.note(&format!(
 +                    "`{generic}` is already a pointer, `{outer}<{generic}>` allocates a pointer on the heap",
 +                    outer = outer_sym,
 +                    generic = generic_snippet
 +                ));
 +            },
 +        );
 +        return true;
 +    }
 +
 +    let (inner_sym, ty) = if let Some(ty) = is_ty_param_lang_item(cx, qpath, LangItem::OwnedBox) {
 +        ("Box", ty)
 +    } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Rc) {
 +        ("Rc", ty)
 +    } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Arc) {
 +        ("Arc", ty)
 +    } else {
 +        return false;
 +    };
 +
 +    let inner_qpath = match &ty.kind {
 +        TyKind::Path(inner_qpath) => inner_qpath,
 +        _ => return false,
 +    };
 +    let inner_span = match get_qpath_generic_tys(inner_qpath).next() {
++        Some(ty) => {
++            // Box<Box<dyn T>> is smaller than Box<dyn T> because of wide pointers
++            if matches!(ty.kind, TyKind::TraitObject(..)) {
++                return false;
++            }
++            ty.span
++        },
 +        None => return false,
 +    };
 +    if inner_sym == outer_sym {
 +        let mut applicability = Applicability::MaybeIncorrect;
 +        let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability);
 +        span_lint_and_then(
 +            cx,
 +            REDUNDANT_ALLOCATION,
 +            hir_ty.span,
 +            &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
 +            |diag| {
 +                diag.span_suggestion(
 +                    hir_ty.span,
 +                    "try",
 +                    format!("{}<{}>", outer_sym, generic_snippet),
 +                    applicability,
 +                );
 +                diag.note(&format!(
 +                    "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
 +                    outer = outer_sym,
 +                    inner = inner_sym,
 +                    generic = generic_snippet
 +                ));
 +            },
 +        );
 +    } else {
 +        let generic_snippet = snippet(cx, inner_span, "..");
 +        span_lint_and_then(
 +            cx,
 +            REDUNDANT_ALLOCATION,
 +            hir_ty.span,
 +            &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
 +            |diag| {
 +                diag.note(&format!(
 +                    "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
 +                    outer = outer_sym,
 +                    inner = inner_sym,
 +                    generic = generic_snippet
 +                ));
 +                diag.help(&format!(
 +                    "consider using just `{outer}<{generic}>` or `{inner}<{generic}>`",
 +                    outer = outer_sym,
 +                    inner = inner_sym,
 +                    generic = generic_snippet
 +                ));
 +            },
 +        );
 +    }
 +    true
 +}
index 47571e608c7803b16efc859a1d71ee784ca06971,0000000000000000000000000000000000000000..095706165936237e7921422b29c23daa080bd84d
mode 100644,000000..100644
--- /dev/null
@@@ -1,54 -1,0 +1,54 @@@
-         if let Some(args) = match_function_call(cx, expr, &paths::DROP) {
-             let ty = cx.typeck_results().expr_ty(&args[0]);
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::ty::is_type_lang_item;
 +use clippy_utils::{match_function_call, paths};
 +use rustc_hir::{lang_items, Expr};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +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));
 +    /// }
 +    /// ```
 +    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"
 +}
 +
 +declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]);
 +
 +impl LateLintPass<'tcx> for UndroppedManuallyDrops {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
++        if let Some([arg_0, ..]) = match_function_call(cx, expr, &paths::DROP) {
++            let ty = cx.typeck_results().expr_ty(arg_0);
 +            if is_type_lang_item(cx, ty, lang_items::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",
 +                );
 +            }
 +        }
 +    }
 +}
index 97b1b2dae3c1a3d03f3bd5e051fe74396a62410c,0000000000000000000000000000000000000000..61670fe124e36f54055be9a58e1465e51ecfd9b1
mode 100644,000000..100644
--- /dev/null
@@@ -1,266 -1,0 +1,269 @@@
-     matches!(ty.kind(), ty::Ref(..)) || ty.walk(cx.tcx).any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::{self, subst::GenericArgKind};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +use rustc_span::symbol::Ident;
 +use std::iter;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects uses of `Vec::sort_by` passing in a closure
 +    /// which compares the two arguments, either directly or indirectly.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
 +    /// possible) than to use `Vec::sort_by` and a more complicated
 +    /// closure.
 +    ///
 +    /// ### Known problems
 +    /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
 +    /// imported by a use statement, then it will need to be added manually.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct A;
 +    /// # impl A { fn foo(&self) {} }
 +    /// # let mut vec: Vec<A> = Vec::new();
 +    /// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # struct A;
 +    /// # impl A { fn foo(&self) {} }
 +    /// # let mut vec: Vec<A> = Vec::new();
 +    /// vec.sort_by_key(|a| a.foo());
 +    /// ```
 +    pub UNNECESSARY_SORT_BY,
 +    complexity,
 +    "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
 +}
 +
 +declare_lint_pass!(UnnecessarySortBy => [UNNECESSARY_SORT_BY]);
 +
 +enum LintTrigger {
 +    Sort(SortDetection),
 +    SortByKey(SortByKeyDetection),
 +}
 +
 +struct SortDetection {
 +    vec_name: String,
 +    unstable: bool,
 +}
 +
 +struct SortByKeyDetection {
 +    vec_name: String,
 +    closure_arg: String,
 +    closure_body: String,
 +    reverse: bool,
 +    unstable: bool,
 +}
 +
 +/// Detect if the two expressions are mirrored (identical, except one
 +/// contains a and the other replaces it with b)
 +fn mirrored_exprs(
 +    cx: &LateContext<'_>,
 +    a_expr: &Expr<'_>,
 +    a_ident: &Ident,
 +    b_expr: &Expr<'_>,
 +    b_ident: &Ident,
 +) -> bool {
 +    match (&a_expr.kind, &b_expr.kind) {
 +        // Two boxes with mirrored contents
 +        (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => {
 +            mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident)
 +        },
 +        // Two arrays with mirrored contents
 +        (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => {
 +            iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident))
 +        },
 +        // The two exprs are function calls.
 +        // Check to see that the function itself and its arguments are mirrored
 +        (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => {
 +            mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident)
 +                && iter::zip(*left_args, *right_args)
 +                    .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident))
 +        },
 +        // The two exprs are method calls.
 +        // Check to see that the function is the same and the arguments are mirrored
 +        // This is enough because the receiver of the method is listed in the arguments
 +        (
 +            ExprKind::MethodCall(left_segment, _, left_args, _),
 +            ExprKind::MethodCall(right_segment, _, right_args, _),
 +        ) => {
 +            left_segment.ident == right_segment.ident
 +                && iter::zip(*left_args, *right_args)
 +                    .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident))
 +        },
 +        // Two tuples with mirrored contents
 +        (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => {
 +            iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident))
 +        },
 +        // Two binary ops, which are the same operation and which have mirrored arguments
 +        (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => {
 +            left_op.node == right_op.node
 +                && mirrored_exprs(cx, left_left, a_ident, right_left, b_ident)
 +                && mirrored_exprs(cx, left_right, a_ident, right_right, b_ident)
 +        },
 +        // Two unary ops, which are the same operation and which have the same argument
 +        (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => {
 +            left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident)
 +        },
 +        // The two exprs are literals of some kind
 +        (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node,
 +        (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(cx, left, a_ident, right, b_ident),
 +        (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => {
 +            mirrored_exprs(cx, left_block, a_ident, right_block, b_ident)
 +        },
 +        (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => {
 +            left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident)
 +        },
 +        // Two paths: either one is a and the other is b, or they're identical to each other
 +        (
 +            ExprKind::Path(QPath::Resolved(
 +                _,
 +                Path {
 +                    segments: left_segments,
 +                    ..
 +                },
 +            )),
 +            ExprKind::Path(QPath::Resolved(
 +                _,
 +                Path {
 +                    segments: right_segments,
 +                    ..
 +                },
 +            )),
 +        ) => {
 +            (iter::zip(*left_segments, *right_segments).all(|(left, right)| left.ident == right.ident)
 +                && left_segments
 +                    .iter()
 +                    .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident))
 +                || (left_segments.len() == 1
 +                    && &left_segments[0].ident == a_ident
 +                    && right_segments.len() == 1
 +                    && &right_segments[0].ident == b_ident)
 +        },
 +        // Matching expressions, but one or both is borrowed
 +        (
 +            ExprKind::AddrOf(left_kind, Mutability::Not, left_expr),
 +            ExprKind::AddrOf(right_kind, Mutability::Not, right_expr),
 +        ) => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident),
 +        (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => {
 +            mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident)
 +        },
 +        (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident),
 +        _ => false,
 +    }
 +}
 +
 +fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
 +    if_chain! {
 +        if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind;
 +        if let name = name_ident.ident.name.to_ident_string();
 +        if name == "sort_by" || name == "sort_unstable_by";
 +        if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args;
 +        if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::vec_type);
 +        if let closure_body = cx.tcx.hir().body(*closure_body_id);
 +        if let &[
 +            Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
 +            Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. }
 +        ] = &closure_body.params;
 +        if let ExprKind::MethodCall(method_path, _, [ref left_expr, ref right_expr], _) = &closure_body.value.kind;
 +        if method_path.ident.name == sym::cmp;
 +        then {
 +            let (closure_body, closure_arg, reverse) = if mirrored_exprs(
 +                cx,
 +                left_expr,
 +                left_ident,
 +                right_expr,
 +                right_ident
 +            ) {
 +                (Sugg::hir(cx, left_expr, "..").to_string(), left_ident.name.to_string(), false)
 +            } else if mirrored_exprs(cx, left_expr, right_ident, right_expr, left_ident) {
 +                (Sugg::hir(cx, left_expr, "..").to_string(), right_ident.name.to_string(), true)
 +            } else {
 +                return None;
 +            };
 +            let vec_name = Sugg::hir(cx, &args[0], "..").to_string();
 +            let unstable = name == "sort_unstable_by";
 +
 +            if let ExprKind::Path(QPath::Resolved(_, Path {
 +                segments: [PathSegment { ident: left_name, .. }], ..
 +            })) = &left_expr.kind {
 +                if left_name == left_ident {
 +                    return Some(LintTrigger::Sort(SortDetection { vec_name, unstable }));
 +                }
 +            }
 +
 +            if !expr_borrows(cx, left_expr) {
 +                return Some(LintTrigger::SortByKey(SortByKeyDetection {
 +                    vec_name,
 +                    closure_arg,
 +                    closure_body,
 +                    reverse,
 +                    unstable,
 +                }));
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    let ty = cx.typeck_results().expr_ty(expr);
++    matches!(ty.kind(), ty::Ref(..))
++        || ty
++            .walk(cx.tcx)
++            .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
 +}
 +
 +impl LateLintPass<'_> for UnnecessarySortBy {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        match detect_lint(cx, expr) {
 +            Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
 +                cx,
 +                UNNECESSARY_SORT_BY,
 +                expr.span,
 +                "use Vec::sort_by_key here instead",
 +                "try",
 +                format!(
 +                    "{}.sort{}_by_key(|{}| {})",
 +                    trigger.vec_name,
 +                    if trigger.unstable { "_unstable" } else { "" },
 +                    trigger.closure_arg,
 +                    if trigger.reverse {
 +                        format!("Reverse({})", trigger.closure_body)
 +                    } else {
 +                        trigger.closure_body.to_string()
 +                    },
 +                ),
 +                if trigger.reverse {
 +                    Applicability::MaybeIncorrect
 +                } else {
 +                    Applicability::MachineApplicable
 +                },
 +            ),
 +            Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
 +                cx,
 +                UNNECESSARY_SORT_BY,
 +                expr.span,
 +                "use Vec::sort here instead",
 +                "try",
 +                format!(
 +                    "{}.sort{}()",
 +                    trigger.vec_name,
 +                    if trigger.unstable { "_unstable" } else { "" },
 +                ),
 +                Applicability::MachineApplicable,
 +            ),
 +            None => {},
 +        }
 +    }
 +}
index 3a6a07c522630b22ce7b2dab5297c3828a6eb68f,0000000000000000000000000000000000000000..f4808682b69279dc1e4445d7c00770c030748866
mode 100644,000000..100644
--- /dev/null
@@@ -1,91 -1,0 +1,86 @@@
- use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, Item, ItemKind, YieldSource};
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor};
-     fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
-         if let ItemKind::Trait(..) = item.kind {
-             return;
-         }
-     }
++use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, YieldSource};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::map::Map;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for functions that are declared `async` but have no `.await`s inside of them.
 +    ///
 +    /// ### Why is this bad?
 +    /// Async functions with no async code create overhead, both mentally and computationally.
 +    /// Callers of async methods either need to be calling from an async function themselves or run it on an executor, both of which
 +    /// causes runtime overhead and hassle for the caller.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// async fn get_random_number() -> i64 {
 +    ///     4 // Chosen by fair dice roll. Guaranteed to be random.
 +    /// }
 +    /// let number_future = get_random_number();
 +    ///
 +    /// // Good
 +    /// fn get_random_number_improved() -> i64 {
 +    ///     4 // Chosen by fair dice roll. Guaranteed to be random.
 +    /// }
 +    /// let number_future = async { get_random_number_improved() };
 +    /// ```
 +    pub UNUSED_ASYNC,
 +    pedantic,
 +    "finds async functions with no await statements"
 +}
 +
 +declare_lint_pass!(UnusedAsync => [UNUSED_ASYNC]);
 +
 +struct AsyncFnVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    found_await: bool,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
 +        if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind {
 +            self.found_await = true;
 +        }
 +        walk_expr(self, ex);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        fn_kind: FnKind<'tcx>,
 +        fn_decl: &'tcx FnDecl<'tcx>,
 +        body: &Body<'tcx>,
 +        span: Span,
 +        hir_id: HirId,
 +    ) {
 +        if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }, _) = &fn_kind {
 +            if matches!(asyncness, IsAsync::Async) {
 +                let mut visitor = AsyncFnVisitor { cx, found_await: false };
 +                walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id);
 +                if !visitor.found_await {
 +                    span_lint_and_help(
 +                        cx,
 +                        UNUSED_ASYNC,
 +                        span,
 +                        "unused `async` for function with no await statements",
 +                        None,
 +                        "consider removing the `async` from this function",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
index 82bc4a6d15343d2ef09dbc9b9aa7e86099e27493,0000000000000000000000000000000000000000..031b182bd2fa0cdb68cad28c905db1ae5376dc6e
mode 100644,000000..100644
--- /dev/null
@@@ -1,106 -1,0 +1,106 @@@
-                 if let hir::ExprKind::Call(func, args) = res.kind {
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::{is_try, match_trait_method, paths};
 +use rustc_hir as hir;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unused written/read amount.
 +    ///
 +    /// ### Why is this bad?
 +    /// `io::Write::write(_vectored)` and
 +    /// `io::Read::read(_vectored)` are not guaranteed to
 +    /// process the entire buffer. They return how many bytes were processed, which
 +    /// might be smaller
 +    /// than a given buffer's length. If you don't need to deal with
 +    /// partial-write/read, use
 +    /// `write_all`/`read_exact` instead.
 +    ///
 +    /// ### Known problems
 +    /// Detects only common patterns.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// use std::io;
 +    /// fn foo<W: io::Write>(w: &mut W) -> io::Result<()> {
 +    ///     // must be `w.write_all(b"foo")?;`
 +    ///     w.write(b"foo")?;
 +    ///     Ok(())
 +    /// }
 +    /// ```
 +    pub UNUSED_IO_AMOUNT,
 +    correctness,
 +    "unused written/read amount"
 +}
 +
 +declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
 +    fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
 +        let expr = match s.kind {
 +            hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr,
 +            _ => return,
 +        };
 +
 +        match expr.kind {
 +            hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => {
-                         check_map_error(cx, &args[0], expr);
++                if let hir::ExprKind::Call(func, [ref arg_0, ..]) = res.kind {
 +                    if matches!(
 +                        func.kind,
 +                        hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, _))
 +                    ) {
-             hir::ExprKind::MethodCall(path, _, args, _) => match &*path.ident.as_str() {
++                        check_map_error(cx, arg_0, expr);
 +                    }
 +                } else {
 +                    check_map_error(cx, res, expr);
 +                }
 +            },
-                     check_map_error(cx, &args[0], expr);
++            hir::ExprKind::MethodCall(path, _, [ref arg_0, ..], _) => match &*path.ident.as_str() {
 +                "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => {
++                    check_map_error(cx, arg_0, expr);
 +                },
 +                _ => (),
 +            },
 +            _ => (),
 +        }
 +    }
 +}
 +
 +fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
 +    let mut call = call;
 +    while let hir::ExprKind::MethodCall(path, _, args, _) = call.kind {
 +        if matches!(&*path.ident.as_str(), "or" | "or_else" | "ok") {
 +            call = &args[0];
 +        } else {
 +            break;
 +        }
 +    }
 +    check_method_call(cx, call, expr);
 +}
 +
 +fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
 +    if let hir::ExprKind::MethodCall(path, _, _, _) = call.kind {
 +        let symbol = &*path.ident.as_str();
 +        let read_trait = match_trait_method(cx, call, &paths::IO_READ);
 +        let write_trait = match_trait_method(cx, call, &paths::IO_WRITE);
 +
 +        match (read_trait, write_trait, symbol) {
 +            (true, _, "read") => span_lint(
 +                cx,
 +                UNUSED_IO_AMOUNT,
 +                expr.span,
 +                "read amount is not handled. Use `Read::read_exact` instead",
 +            ),
 +            (true, _, "read_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"),
 +            (_, true, "write") => span_lint(
 +                cx,
 +                UNUSED_IO_AMOUNT,
 +                expr.span,
 +                "written amount is not handled. Use `Write::write_all` instead",
 +            ),
 +            (_, true, "write_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"),
 +            _ => (),
 +        }
 +    }
 +}
index 658ac81f6eac8d4e477ed3fa11ab666f03db7770,0000000000000000000000000000000000000000..e7e249c79a2fad0f6f97e1a8110d62b41f763029
mode 100644,000000..100644
--- /dev/null
@@@ -1,67 -1,0 +1,66 @@@
- use clippy_utils::visitors::LocalUsedVisitor;
 +use clippy_utils::diagnostics::span_lint_and_help;
-             let self_hir_id = self_param.pat.hir_id;
-             if !LocalUsedVisitor::new(cx, self_hir_id).check_body(body);
++use clippy_utils::visitors::is_local_used;
 +use if_chain::if_chain;
 +use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks methods that contain a `self` argument but don't use it
 +    ///
 +    /// ### Why is this bad?
 +    /// It may be clearer to define the method as an associated function instead
 +    /// of an instance method if it doesn't require `self`.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// struct A;
 +    /// impl A {
 +    ///     fn method(&self) {}
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```rust,ignore
 +    /// struct A;
 +    /// impl A {
 +    ///     fn method() {}
 +    /// }
 +    /// ```
 +    pub UNUSED_SELF,
 +    pedantic,
 +    "methods that contain a `self` argument but don't use it"
 +}
 +
 +declare_lint_pass!(UnusedSelf => [UNUSED_SELF]);
 +
 +impl<'tcx> LateLintPass<'tcx> for UnusedSelf {
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'_>) {
 +        if impl_item.span.from_expansion() {
 +            return;
 +        }
 +        let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id());
 +        let parent_item = cx.tcx.hir().expect_item(parent);
 +        let assoc_item = cx.tcx.associated_item(impl_item.def_id);
 +        if_chain! {
 +            if let ItemKind::Impl(Impl { of_trait: None, .. }) = parent_item.kind;
 +            if assoc_item.fn_has_self_parameter;
 +            if let ImplItemKind::Fn(.., body_id) = &impl_item.kind;
 +            let body = cx.tcx.hir().body(*body_id);
 +            if let [self_param, ..] = body.params;
++            if !is_local_used(cx, body, self_param.pat.hir_id);
 +            then {
 +                span_lint_and_help(
 +                    cx,
 +                    UNUSED_SELF,
 +                    self_param.span,
 +                    "unused `self` argument",
 +                    None,
 +                    "consider refactoring to a associated function",
 +                );
 +            }
 +        }
 +    }
 +}
index bffd9f3612b0a978ffb6426b9fc3932bfb728f87,0000000000000000000000000000000000000000..b2ab300c2e937faed6ef49aaff09957203807dfb
mode 100644,000000..100644
--- /dev/null
@@@ -1,237 -1,0 +1,326 @@@
- use clippy_utils::{differing_macro_contexts, usage::is_potentially_mutated};
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::higher;
 +use clippy_utils::ty::is_type_diagnostic_item;
- use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Path, QPath, UnOp};
++use clippy_utils::{differing_macro_contexts, path_to_local, usage::is_potentially_mutated};
 +use if_chain::if_chain;
++use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor};
-     ident: &'tcx Path<'tcx>,
++use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, PathSegment, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::Ty;
 +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 calls of `unwrap[_err]()` that cannot fail.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `if let` or `match` is more idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let option = Some(0);
 +    /// # fn do_something_with(_x: usize) {}
 +    /// if option.is_some() {
 +    ///     do_something_with(option.unwrap())
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```rust
 +    /// # let option = Some(0);
 +    /// # fn do_something_with(_x: usize) {}
 +    /// if let Some(value) = option {
 +    ///     do_something_with(value)
 +    /// }
 +    /// ```
 +    pub UNNECESSARY_UNWRAP,
 +    complexity,
 +    "checks for calls of `unwrap[_err]()` that cannot fail"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls of `unwrap[_err]()` that will always fail.
 +    ///
 +    /// ### Why is this bad?
 +    /// If panicking is desired, an explicit `panic!()` should be used.
 +    ///
 +    /// ### Known problems
 +    /// This lint only checks `if` conditions not assignments.
 +    /// So something like `let x: Option<()> = None; x.unwrap();` will not be recognized.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let option = Some(0);
 +    /// # fn do_something_with(_x: usize) {}
 +    /// if option.is_none() {
 +    ///     do_something_with(option.unwrap())
 +    /// }
 +    /// ```
 +    ///
 +    /// This code will always panic. The if condition should probably be inverted.
 +    pub PANICKING_UNWRAP,
 +    correctness,
 +    "checks for calls of `unwrap[_err]()` that will always fail"
 +}
 +
 +/// Visitor that keeps track of which variables are unwrappable.
 +struct UnwrappableVariablesVisitor<'a, 'tcx> {
 +    unwrappables: Vec<UnwrapInfo<'tcx>>,
 +    cx: &'a LateContext<'tcx>,
 +}
++
++/// What kind of unwrappable this is.
++#[derive(Copy, Clone, Debug)]
++enum UnwrappableKind {
++    Option,
++    Result,
++}
++
++impl UnwrappableKind {
++    fn success_variant_pattern(self) -> &'static str {
++        match self {
++            UnwrappableKind::Option => "Some(..)",
++            UnwrappableKind::Result => "Ok(..)",
++        }
++    }
++
++    fn error_variant_pattern(self) -> &'static str {
++        match self {
++            UnwrappableKind::Option => "None",
++            UnwrappableKind::Result => "Err(..)",
++        }
++    }
++}
++
 +/// Contains information about whether a variable can be unwrapped.
 +#[derive(Copy, Clone, Debug)]
 +struct UnwrapInfo<'tcx> {
 +    /// The variable that is checked
-                 let mut unwrap_info = collect_unwrap_info(cx, left, branch, invert);
-                 unwrap_info.append(&mut collect_unwrap_info(cx, right, branch, invert));
++    local_id: HirId,
++    /// The if itself
++    if_expr: &'tcx Expr<'tcx>,
 +    /// The check, like `x.is_ok()`
 +    check: &'tcx Expr<'tcx>,
++    /// The check's name, like `is_ok`
++    check_name: &'tcx PathSegment<'tcx>,
 +    /// The branch where the check takes place, like `if x.is_ok() { .. }`
 +    branch: &'tcx Expr<'tcx>,
 +    /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`).
 +    safe_to_unwrap: bool,
++    /// What kind of unwrappable this is.
++    kind: UnwrappableKind,
++    /// If the check is the entire condition (`if x.is_ok()`) or only a part of it (`foo() &&
++    /// x.is_ok()`)
++    is_entire_condition: bool,
 +}
 +
 +/// Collects the information about unwrappable variables from an if condition
 +/// The `invert` argument tells us whether the condition is negated.
 +fn collect_unwrap_info<'tcx>(
 +    cx: &LateContext<'tcx>,
++    if_expr: &'tcx Expr<'_>,
 +    expr: &'tcx Expr<'_>,
 +    branch: &'tcx Expr<'_>,
 +    invert: bool,
++    is_entire_condition: bool,
 +) -> Vec<UnwrapInfo<'tcx>> {
 +    fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool {
 +        is_type_diagnostic_item(cx, ty, sym::option_type) && ["is_some", "is_none"].contains(&method_name)
 +    }
 +
 +    fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool {
 +        is_type_diagnostic_item(cx, ty, sym::result_type) && ["is_ok", "is_err"].contains(&method_name)
 +    }
 +
 +    if let ExprKind::Binary(op, left, right) = &expr.kind {
 +        match (invert, op.node) {
 +            (false, BinOpKind::And | BinOpKind::BitAnd) | (true, BinOpKind::Or | BinOpKind::BitOr) => {
-         return collect_unwrap_info(cx, expr, branch, !invert);
++                let mut unwrap_info = collect_unwrap_info(cx, if_expr, left, branch, invert, false);
++                unwrap_info.append(&mut collect_unwrap_info(cx, if_expr, right, branch, invert, false));
 +                return unwrap_info;
 +            },
 +            _ => (),
 +        }
 +    } else if let ExprKind::Unary(UnOp::Not, expr) = &expr.kind {
-             if let ExprKind::Path(QPath::Resolved(None, path)) = &args[0].kind;
++        return collect_unwrap_info(cx, if_expr, expr, branch, !invert, false);
 +    } else {
 +        if_chain! {
 +            if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind;
-                 return vec![UnwrapInfo { ident: path, check: expr, branch, safe_to_unwrap }];
++            if let Some(local_id) = path_to_local(&args[0]);
 +            let ty = cx.typeck_results().expr_ty(&args[0]);
 +            let name = method_name.ident.as_str();
 +            if is_relevant_option_call(cx, ty, &name) || is_relevant_result_call(cx, ty, &name);
 +            then {
 +                assert!(args.len() == 1);
 +                let unwrappable = match name.as_ref() {
 +                    "is_some" | "is_ok" => true,
 +                    "is_err" | "is_none" => false,
 +                    _ => unreachable!(),
 +                };
 +                let safe_to_unwrap = unwrappable != invert;
-     fn visit_branch(&mut self, cond: &'tcx Expr<'_>, branch: &'tcx Expr<'_>, else_branch: bool) {
++                let kind = if is_type_diagnostic_item(cx, ty, sym::option_type) {
++                    UnwrappableKind::Option
++                } else {
++                    UnwrappableKind::Result
++                };
++
++                return vec![
++                    UnwrapInfo {
++                        local_id,
++                        if_expr,
++                        check: expr,
++                        check_name: method_name,
++                        branch,
++                        safe_to_unwrap,
++                        kind,
++                        is_entire_condition,
++                    }
++                ]
 +            }
 +        }
 +    }
 +    Vec::new()
 +}
 +
 +impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> {
-         for unwrap_info in collect_unwrap_info(self.cx, cond, branch, else_branch) {
-             if is_potentially_mutated(unwrap_info.ident, cond, self.cx)
-                 || is_potentially_mutated(unwrap_info.ident, branch, self.cx)
++    fn visit_branch(
++        &mut self,
++        if_expr: &'tcx Expr<'_>,
++        cond: &'tcx Expr<'_>,
++        branch: &'tcx Expr<'_>,
++        else_branch: bool,
++    ) {
 +        let prev_len = self.unwrappables.len();
-             self.visit_branch(cond, then, false);
++        for unwrap_info in collect_unwrap_info(self.cx, if_expr, cond, branch, else_branch, true) {
++            if is_potentially_mutated(unwrap_info.local_id, cond, self.cx)
++                || is_potentially_mutated(unwrap_info.local_id, branch, self.cx)
 +            {
 +                // if the variable is mutated, we don't know whether it can be unwrapped:
 +                continue;
 +            }
 +            self.unwrappables.push(unwrap_info);
 +        }
 +        walk_expr(self, branch);
 +        self.unwrappables.truncate(prev_len);
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        // Shouldn't lint when `expr` is in macro.
 +        if in_external_macro(self.cx.tcx.sess, expr.span) {
 +            return;
 +        }
 +        if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) {
 +            walk_expr(self, cond);
-                 self.visit_branch(cond, else_inner, true);
++            self.visit_branch(expr, cond, then, false);
 +            if let Some(else_inner) = r#else {
-                 if let ExprKind::Path(QPath::Resolved(None, path)) = args[0].kind;
-                 if [sym::unwrap, sym!(unwrap_err)].contains(&method_name.ident.name);
-                 let call_to_unwrap = method_name.ident.name == sym::unwrap;
++                self.visit_branch(expr, cond, else_inner, true);
 +            }
 +        } else {
 +            // find `unwrap[_err]()` calls:
 +            if_chain! {
 +                if let ExprKind::MethodCall(method_name, _, args, _) = expr.kind;
-                     .find(|u| u.ident.res == path.res);
++                if let Some(id) = path_to_local(&args[0]);
++                if [sym::unwrap, sym::expect, sym!(unwrap_err)].contains(&method_name.ident.name);
++                let call_to_unwrap = [sym::unwrap, sym::expect].contains(&method_name.ident.name);
 +                if let Some(unwrappable) = self.unwrappables.iter()
-                             &format!("you checked before that `{}()` cannot fail, \
-                             instead of checking and unwrapping, it's better to use `if let` or `match`",
-                             method_name.ident.name),
-                             |diag| { diag.span_label(unwrappable.check.span, "the check is happening here"); },
++                    .find(|u| u.local_id == id);
 +                // Span contexts should not differ with the conditional branch
 +                if !differing_macro_contexts(unwrappable.branch.span, expr.span);
 +                if !differing_macro_contexts(unwrappable.branch.span, unwrappable.check.span);
 +                then {
 +                    if call_to_unwrap == unwrappable.safe_to_unwrap {
++                        let is_entire_condition = unwrappable.is_entire_condition;
++                        let unwrappable_variable_name = self.cx.tcx.hir().name(unwrappable.local_id);
++                        let suggested_pattern = if call_to_unwrap {
++                            unwrappable.kind.success_variant_pattern()
++                        } else {
++                            unwrappable.kind.error_variant_pattern()
++                        };
++
 +                        span_lint_and_then(
 +                            self.cx,
 +                            UNNECESSARY_UNWRAP,
 +                            expr.span,
++                            &format!(
++                                "called `{}` on `{}` after checking its variant with `{}`",
++                                method_name.ident.name,
++                                unwrappable_variable_name,
++                                unwrappable.check_name.ident.as_str(),
++                            ),
++                            |diag| {
++                                if is_entire_condition {
++                                    diag.span_suggestion(
++                                        unwrappable.check.span.with_lo(unwrappable.if_expr.span.lo()),
++                                        "try",
++                                        format!(
++                                            "if let {} = {}",
++                                            suggested_pattern,
++                                            unwrappable_variable_name,
++                                        ),
++                                        // We don't track how the unwrapped value is used inside the
++                                        // block or suggest deleting the unwrap, so we can't offer a
++                                        // fixable solution.
++                                        Applicability::Unspecified,
++                                    );
++                                } else {
++                                    diag.span_label(unwrappable.check.span, "the check is happening here");
++                                    diag.help("try using `if let` or `match`");
++                                }
++                            },
 +                        );
 +                    } else {
 +                        span_lint_and_then(
 +                            self.cx,
 +                            PANICKING_UNWRAP,
 +                            expr.span,
 +                            &format!("this call to `{}()` will always panic",
 +                            method_name.ident.name),
 +                            |diag| { diag.span_label(unwrappable.check.span, "because of this check"); },
 +                        );
 +                    }
 +                }
 +            }
 +            walk_expr(self, expr);
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
 +    }
 +}
 +
 +declare_lint_pass!(Unwrap => [PANICKING_UNWRAP, UNNECESSARY_UNWRAP]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Unwrap {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: FnKind<'tcx>,
 +        decl: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        span: Span,
 +        fn_id: HirId,
 +    ) {
 +        if span.from_expansion() {
 +            return;
 +        }
 +
 +        let mut v = UnwrappableVariablesVisitor {
 +            cx,
 +            unwrappables: Vec::new(),
 +        };
 +
 +        walk_fn(&mut v, kind, decl, body.id(), span, fn_id);
 +    }
 +}
index c192f9094a8a97bdfd840c3edfd27e09948d61e4,0000000000000000000000000000000000000000..8651fa6fcf9eaddeee67be294468d6b9afd573f9
mode 100644,000000..100644
--- /dev/null
@@@ -1,323 -1,0 +1,319 @@@
- /// Note that the configuration parsing currently doesn't support documentation that will
- /// that spans over several lines. This will be possible with the new implementation
- /// See (rust-clippy#7172)
 +//! Read configurations files.
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor};
 +use serde::Deserialize;
 +use std::error::Error;
 +use std::path::{Path, PathBuf};
 +use std::{env, fmt, fs, io};
 +
 +/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint.
 +#[derive(Clone, Debug, Deserialize)]
 +pub struct Rename {
 +    pub path: String,
 +    pub rename: String,
 +}
 +
 +/// Conf with parse errors
 +#[derive(Default)]
 +pub struct TryConf {
 +    pub conf: Conf,
 +    pub errors: Vec<String>,
 +}
 +
 +impl TryConf {
 +    fn from_error(error: impl Error) -> Self {
 +        Self {
 +            conf: Conf::default(),
 +            errors: vec![error.to_string()],
 +        }
 +    }
 +}
 +
- // N.B., this macro is parsed by util/lintlib.py
 +macro_rules! define_Conf {
 +    ($(
 +        $(#[doc = $doc:literal])+
 +        $(#[conf_deprecated($dep:literal)])?
 +        ($name:ident: $ty:ty = $default:expr),
 +    )*) => {
 +        /// Clippy lint configuration
 +        pub struct Conf {
 +            $($(#[doc = $doc])+ pub $name: $ty,)*
 +        }
 +
 +        mod defaults {
 +            $(pub fn $name() -> $ty { $default })*
 +        }
 +
 +        impl Default for Conf {
 +            fn default() -> Self {
 +                Self { $($name: defaults::$name(),)* }
 +            }
 +        }
 +
 +        impl<'de> Deserialize<'de> for TryConf {
 +            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
 +                deserializer.deserialize_map(ConfVisitor)
 +            }
 +        }
 +
 +        #[derive(Deserialize)]
 +        #[serde(field_identifier, rename_all = "kebab-case")]
 +        #[allow(non_camel_case_types)]
 +        enum Field { $($name,)* third_party, }
 +
 +        struct ConfVisitor;
 +
 +        impl<'de> Visitor<'de> for ConfVisitor {
 +            type Value = TryConf;
 +
 +            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
 +                formatter.write_str("Conf")
 +            }
 +
 +            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapAccess<'de> {
 +                let mut errors = Vec::new();
 +                $(let mut $name = None;)*
 +                // could get `Field` here directly, but get `str` first for diagnostics
 +                while let Some(name) = map.next_key::<&str>()? {
 +                    match Field::deserialize(name.into_deserializer())? {
 +                        $(Field::$name => {
 +                            $(errors.push(format!("deprecated field `{}`. {}", name, $dep));)?
 +                            match map.next_value() {
 +                                Err(e) => errors.push(e.to_string()),
 +                                Ok(value) => match $name {
 +                                    Some(_) => errors.push(format!("duplicate field `{}`", name)),
 +                                    None => $name = Some(value),
 +                                }
 +                            }
 +                        })*
 +                        // white-listed; ignore
 +                        Field::third_party => drop(map.next_value::<IgnoredAny>())
 +                    }
 +                }
 +                let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
 +                Ok(TryConf { conf, errors })
 +            }
 +        }
 +
 +        #[cfg(feature = "metadata-collector-lint")]
 +        pub mod metadata {
 +            use crate::utils::internal_lints::metadata_collector::ClippyConfiguration;
 +
 +            macro_rules! wrap_option {
 +                () => (None);
 +                ($x:literal) => (Some($x));
 +            }
 +
 +            pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
 +                vec![
 +                    $(
 +                        {
 +                            let deprecation_reason = wrap_option!($($dep)?);
 +
 +                            ClippyConfiguration::new(
 +                                stringify!($name),
 +                                stringify!($ty),
 +                                format!("{:?}", super::defaults::$name()),
 +                                concat!($($doc, '\n',)*),
 +                                deprecation_reason,
 +                            )
 +                        },
 +                    )+
 +                ]
 +            }
 +        }
 +    };
 +}
 +
-     /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION.
 +define_Conf! {
-     /// Lint: MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE.
++    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_VEC, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
 +    ///
 +    /// Suppress lints whenever the suggested change would cause breakage for other crates.
 +    (avoid_breaking_exported_api: bool = true),
++    /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT.
 +    ///
 +    /// The minimum rust version that the project supports
 +    (msrv: Option<String> = None),
 +    /// Lint: BLACKLISTED_NAME.
 +    ///
 +    /// The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
 +    (blacklisted_names: Vec<String> = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
 +    /// Lint: COGNITIVE_COMPLEXITY.
 +    ///
 +    /// The maximum cognitive complexity a function can have
 +    (cognitive_complexity_threshold: u64 = 25),
 +    /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY.
 +    ///
 +    /// Use the Cognitive Complexity lint instead.
 +    #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")]
 +    (cyclomatic_complexity_threshold: Option<u64> = None),
 +    /// Lint: DOC_MARKDOWN.
 +    ///
 +    /// The list of words this lint should not consider as identifiers needing ticks
 +    (doc_valid_idents: Vec<String> = [
 +        "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
 +        "DirectX",
 +        "ECMAScript",
 +        "GPLv2", "GPLv3",
 +        "GitHub", "GitLab",
 +        "IPv4", "IPv6",
 +        "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
 +        "NaN", "NaNs",
 +        "OAuth", "GraphQL",
 +        "OCaml",
 +        "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS",
 +        "WebGL",
 +        "TensorFlow",
 +        "TrueType",
 +        "iOS", "macOS", "FreeBSD",
 +        "TeX", "LaTeX", "BibTeX", "BibLaTeX",
 +        "MinGW",
 +        "CamelCase",
 +    ].iter().map(ToString::to_string).collect()),
 +    /// Lint: TOO_MANY_ARGUMENTS.
 +    ///
 +    /// The maximum number of argument a function or method can have
 +    (too_many_arguments_threshold: u64 = 7),
 +    /// Lint: TYPE_COMPLEXITY.
 +    ///
 +    /// The maximum complexity a type can have
 +    (type_complexity_threshold: u64 = 250),
 +    /// Lint: MANY_SINGLE_CHAR_NAMES.
 +    ///
 +    /// The maximum number of single char bindings a scope may have
 +    (single_char_binding_names_threshold: u64 = 4),
 +    /// Lint: BOXED_LOCAL, USELESS_VEC.
 +    ///
 +    /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
 +    (too_large_for_stack: u64 = 200),
 +    /// Lint: ENUM_VARIANT_NAMES.
 +    ///
 +    /// The minimum number of enum variants for the lints about variant names to trigger
 +    (enum_variant_name_threshold: u64 = 3),
 +    /// Lint: LARGE_ENUM_VARIANT.
 +    ///
 +    /// The maximum size of an enum's variant to avoid box suggestion
 +    (enum_variant_size_threshold: u64 = 200),
 +    /// Lint: VERBOSE_BIT_MASK.
 +    ///
 +    /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
 +    (verbose_bit_mask_threshold: u64 = 1),
 +    /// Lint: DECIMAL_LITERAL_REPRESENTATION.
 +    ///
 +    /// The lower bound for linting decimal literals
 +    (literal_representation_threshold: u64 = 16384),
 +    /// Lint: TRIVIALLY_COPY_PASS_BY_REF.
 +    ///
 +    /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
 +    (trivial_copy_size_limit: Option<u64> = None),
 +    /// Lint: LARGE_TYPE_PASS_BY_MOVE.
 +    ///
 +    /// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
 +    (pass_by_value_size_limit: u64 = 256),
 +    /// Lint: TOO_MANY_LINES.
 +    ///
 +    /// The maximum number of lines a function or method can have
 +    (too_many_lines_threshold: u64 = 100),
 +    /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS.
 +    ///
 +    /// The maximum allowed size for arrays on the stack
 +    (array_size_threshold: u64 = 512_000),
 +    /// Lint: VEC_BOX.
 +    ///
 +    /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed
 +    (vec_box_size_threshold: u64 = 4096),
 +    /// Lint: TYPE_REPETITION_IN_BOUNDS.
 +    ///
 +    /// The maximum number of bounds a trait can have to be linted
 +    (max_trait_bounds: u64 = 3),
 +    /// Lint: STRUCT_EXCESSIVE_BOOLS.
 +    ///
 +    /// The maximum number of bool fields a struct can have
 +    (max_struct_bools: u64 = 3),
 +    /// Lint: FN_PARAMS_EXCESSIVE_BOOLS.
 +    ///
 +    /// The maximum number of bool parameters a function can have
 +    (max_fn_params_bools: u64 = 3),
 +    /// Lint: WILDCARD_IMPORTS.
 +    ///
 +    /// Whether to allow certain wildcard imports (prelude, super in tests).
 +    (warn_on_all_wildcard_imports: bool = false),
 +    /// Lint: DISALLOWED_METHOD.
 +    ///
 +    /// The list of disallowed methods, written as fully qualified paths.
 +    (disallowed_methods: Vec<String> = Vec::new()),
 +    /// Lint: DISALLOWED_TYPE.
 +    ///
 +    /// The list of disallowed types, written as fully qualified paths.
 +    (disallowed_types: Vec<String> = Vec::new()),
 +    /// Lint: UNREADABLE_LITERAL.
 +    ///
 +    /// Should the fraction of a decimal be linted to include separators.
 +    (unreadable_literal_lint_fractions: bool = true),
 +    /// Lint: UPPER_CASE_ACRONYMS.
 +    ///
 +    /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other
 +    (upper_case_acronyms_aggressive: bool = false),
 +    /// Lint: _CARGO_COMMON_METADATA.
 +    ///
 +    /// For internal testing only, ignores the current `publish` settings in the Cargo manifest.
 +    (cargo_ignore_publish: bool = false),
 +    /// Lint: NONSTANDARD_MACRO_BRACES.
 +    ///
 +    /// Enforce the named macros always use the braces specified.
 +    ///
 +    /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
 +    /// is could be used with a full path two `MacroMatcher`s have to be added one with the full path
 +    /// `crate_name::macro_name` and one with just the macro name.
 +    (standard_macro_braces: Vec<crate::nonstandard_macro_braces::MacroMatcher> = Vec::new()),
 +    /// Lint: MISSING_ENFORCED_IMPORT_RENAMES.
 +    ///
 +    /// The list of imports to always rename, a fully qualified path followed by the rename.
 +    (enforced_import_renames: Vec<crate::utils::conf::Rename> = Vec::new()),
 +    /// Lint: RESTRICTED_SCRIPTS.
 +    ///
 +    /// The list of unicode scripts allowed to be used in the scope.
 +    (allowed_scripts: Vec<String> = vec!["Latin".to_string()]),
 +}
 +
 +/// Search for the configuration file.
 +pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
 +    /// Possible filename to search for.
 +    const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
 +
 +    // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
 +    // If neither of those exist, use ".".
 +    let mut current = env::var_os("CLIPPY_CONF_DIR")
 +        .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
 +        .map_or_else(|| PathBuf::from("."), PathBuf::from);
 +    loop {
 +        for config_file_name in &CONFIG_FILE_NAMES {
 +            if let Ok(config_file) = current.join(config_file_name).canonicalize() {
 +                match fs::metadata(&config_file) {
 +                    Err(e) if e.kind() == io::ErrorKind::NotFound => {},
 +                    Err(e) => return Err(e),
 +                    Ok(md) if md.is_dir() => {},
 +                    Ok(_) => return Ok(Some(config_file)),
 +                }
 +            }
 +        }
 +
 +        // If the current directory has no parent, we're done searching.
 +        if !current.pop() {
 +            return Ok(None);
 +        }
 +    }
 +}
 +
 +/// Read the `toml` configuration file.
 +///
 +/// In case of error, the function tries to continue as much as possible.
 +pub fn read(path: &Path) -> TryConf {
 +    let content = match fs::read_to_string(path) {
 +        Err(e) => return TryConf::from_error(e),
 +        Ok(content) => content,
 +    };
 +    toml::from_str(&content).unwrap_or_else(TryConf::from_error)
 +}
index e97983a2e1451d373e77609e8f13f456f7dbbfde,0000000000000000000000000000000000000000..43590cc786236f2640c751f69877b3ee4f96ca6b
mode 100644,000000..100644
--- /dev/null
@@@ -1,563 -1,0 +1,563 @@@
-         hir::ExprKind::Let(ref pat, ref expr, _) => {
 +//! checks for attributes
 +
 +use clippy_utils::get_attr;
 +use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece};
 +use rustc_hir as hir;
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_session::Session;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Dumps every ast/hir node which has the `#[clippy::dump]`
 +    /// attribute
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// #[clippy::dump]
 +    /// extern crate foo;
 +    /// ```
 +    ///
 +    /// prints
 +    ///
 +    /// ```text
 +    /// item `foo`
 +    /// visibility inherited from outer item
 +    /// extern crate dylib source: "/path/to/foo.so"
 +    /// ```
 +    pub DEEP_CODE_INSPECTION,
 +    internal_warn,
 +    "helper to dump info about code"
 +}
 +
 +declare_lint_pass!(DeepCodeInspector => [DEEP_CODE_INSPECTION]);
 +
 +impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) {
 +            return;
 +        }
 +        print_item(cx, item);
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
 +        if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) {
 +            return;
 +        }
 +        println!("impl item `{}`", item.ident.name);
 +        match item.vis.node {
 +            hir::VisibilityKind::Public => println!("public"),
 +            hir::VisibilityKind::Crate(_) => println!("visible crate wide"),
 +            hir::VisibilityKind::Restricted { path, .. } => println!(
 +                "visible in module `{}`",
 +                rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false))
 +            ),
 +            hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"),
 +        }
 +        if item.defaultness.is_default() {
 +            println!("default");
 +        }
 +        match item.kind {
 +            hir::ImplItemKind::Const(_, body_id) => {
 +                println!("associated constant");
 +                print_expr(cx, &cx.tcx.hir().body(body_id).value, 1);
 +            },
 +            hir::ImplItemKind::Fn(..) => println!("method"),
 +            hir::ImplItemKind::TyAlias(_) => println!("associated type"),
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if !has_attr(cx.sess(), cx.tcx.hir().attrs(expr.hir_id)) {
 +            return;
 +        }
 +        print_expr(cx, expr, 0);
 +    }
 +
 +    fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) {
 +        if !has_attr(cx.sess(), cx.tcx.hir().attrs(arm.hir_id)) {
 +            return;
 +        }
 +        print_pat(cx, arm.pat, 1);
 +        if let Some(ref guard) = arm.guard {
 +            println!("guard:");
 +            print_guard(cx, guard, 1);
 +        }
 +        println!("body:");
 +        print_expr(cx, arm.body, 1);
 +    }
 +
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
 +        if !has_attr(cx.sess(), cx.tcx.hir().attrs(stmt.hir_id)) {
 +            return;
 +        }
 +        match stmt.kind {
 +            hir::StmtKind::Local(local) => {
 +                println!("local variable of type {}", cx.typeck_results().node_type(local.hir_id));
 +                println!("pattern:");
 +                print_pat(cx, local.pat, 0);
 +                if let Some(e) = local.init {
 +                    println!("init expression:");
 +                    print_expr(cx, e, 0);
 +                }
 +            },
 +            hir::StmtKind::Item(_) => println!("item decl"),
 +            hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) => print_expr(cx, e, 0),
 +        }
 +    }
 +}
 +
 +fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool {
 +    get_attr(sess, attrs, "dump").count() > 0
 +}
 +
 +#[allow(clippy::similar_names)]
 +#[allow(clippy::too_many_lines)]
 +fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) {
 +    let ind = "  ".repeat(indent);
 +    println!("{}+", ind);
 +    println!("{}ty: {}", ind, cx.typeck_results().expr_ty(expr));
 +    println!(
 +        "{}adjustments: {:?}",
 +        ind,
 +        cx.typeck_results().adjustments().get(expr.hir_id)
 +    );
 +    match expr.kind {
 +        hir::ExprKind::Box(e) => {
 +            println!("{}Box", ind);
 +            print_expr(cx, e, indent + 1);
 +        },
 +        hir::ExprKind::Array(v) => {
 +            println!("{}Array", ind);
 +            for e in v {
 +                print_expr(cx, e, indent + 1);
 +            }
 +        },
 +        hir::ExprKind::Call(func, args) => {
 +            println!("{}Call", ind);
 +            println!("{}function:", ind);
 +            print_expr(cx, func, indent + 1);
 +            println!("{}arguments:", ind);
 +            for arg in args {
 +                print_expr(cx, arg, indent + 1);
 +            }
 +        },
++        hir::ExprKind::Let(pat, expr, _) => {
 +            print_pat(cx, pat, indent + 1);
 +            print_expr(cx, expr, indent + 1);
 +        },
 +        hir::ExprKind::MethodCall(path, _, args, _) => {
 +            println!("{}MethodCall", ind);
 +            println!("{}method name: {}", ind, path.ident.name);
 +            for arg in args {
 +                print_expr(cx, arg, indent + 1);
 +            }
 +        },
 +        hir::ExprKind::Tup(v) => {
 +            println!("{}Tup", ind);
 +            for e in v {
 +                print_expr(cx, e, indent + 1);
 +            }
 +        },
 +        hir::ExprKind::Binary(op, lhs, rhs) => {
 +            println!("{}Binary", ind);
 +            println!("{}op: {:?}", ind, op.node);
 +            println!("{}lhs:", ind);
 +            print_expr(cx, lhs, indent + 1);
 +            println!("{}rhs:", ind);
 +            print_expr(cx, rhs, indent + 1);
 +        },
 +        hir::ExprKind::Unary(op, inner) => {
 +            println!("{}Unary", ind);
 +            println!("{}op: {:?}", ind, op);
 +            print_expr(cx, inner, indent + 1);
 +        },
 +        hir::ExprKind::Lit(ref lit) => {
 +            println!("{}Lit", ind);
 +            println!("{}{:?}", ind, lit);
 +        },
 +        hir::ExprKind::Cast(e, target) => {
 +            println!("{}Cast", ind);
 +            print_expr(cx, e, indent + 1);
 +            println!("{}target type: {:?}", ind, target);
 +        },
 +        hir::ExprKind::Type(e, target) => {
 +            println!("{}Type", ind);
 +            print_expr(cx, e, indent + 1);
 +            println!("{}target type: {:?}", ind, target);
 +        },
 +        hir::ExprKind::Loop(..) => {
 +            println!("{}Loop", ind);
 +        },
 +        hir::ExprKind::If(cond, _, ref else_opt) => {
 +            println!("{}If", ind);
 +            println!("{}condition:", ind);
 +            print_expr(cx, cond, indent + 1);
 +            if let Some(els) = *else_opt {
 +                println!("{}else:", ind);
 +                print_expr(cx, els, indent + 1);
 +            }
 +        },
 +        hir::ExprKind::Match(cond, _, ref source) => {
 +            println!("{}Match", ind);
 +            println!("{}condition:", ind);
 +            print_expr(cx, cond, indent + 1);
 +            println!("{}source: {:?}", ind, source);
 +        },
 +        hir::ExprKind::Closure(ref clause, _, _, _, _) => {
 +            println!("{}Closure", ind);
 +            println!("{}clause: {:?}", ind, clause);
 +        },
 +        hir::ExprKind::Yield(sub, _) => {
 +            println!("{}Yield", ind);
 +            print_expr(cx, sub, indent + 1);
 +        },
 +        hir::ExprKind::Block(_, _) => {
 +            println!("{}Block", ind);
 +        },
 +        hir::ExprKind::Assign(lhs, rhs, _) => {
 +            println!("{}Assign", ind);
 +            println!("{}lhs:", ind);
 +            print_expr(cx, lhs, indent + 1);
 +            println!("{}rhs:", ind);
 +            print_expr(cx, rhs, indent + 1);
 +        },
 +        hir::ExprKind::AssignOp(ref binop, lhs, rhs) => {
 +            println!("{}AssignOp", ind);
 +            println!("{}op: {:?}", ind, binop.node);
 +            println!("{}lhs:", ind);
 +            print_expr(cx, lhs, indent + 1);
 +            println!("{}rhs:", ind);
 +            print_expr(cx, rhs, indent + 1);
 +        },
 +        hir::ExprKind::Field(e, ident) => {
 +            println!("{}Field", ind);
 +            println!("{}field name: {}", ind, ident.name);
 +            println!("{}struct expr:", ind);
 +            print_expr(cx, e, indent + 1);
 +        },
 +        hir::ExprKind::Index(arr, idx) => {
 +            println!("{}Index", ind);
 +            println!("{}array expr:", ind);
 +            print_expr(cx, arr, indent + 1);
 +            println!("{}index expr:", ind);
 +            print_expr(cx, idx, indent + 1);
 +        },
 +        hir::ExprKind::Path(hir::QPath::Resolved(ref ty, path)) => {
 +            println!("{}Resolved Path, {:?}", ind, ty);
 +            println!("{}path: {:?}", ind, path);
 +        },
 +        hir::ExprKind::Path(hir::QPath::TypeRelative(ty, seg)) => {
 +            println!("{}Relative Path, {:?}", ind, ty);
 +            println!("{}seg: {:?}", ind, seg);
 +        },
 +        hir::ExprKind::Path(hir::QPath::LangItem(lang_item, ..)) => {
 +            println!("{}Lang Item Path, {:?}", ind, lang_item.name());
 +        },
 +        hir::ExprKind::AddrOf(kind, ref muta, e) => {
 +            println!("{}AddrOf", ind);
 +            println!("kind: {:?}", kind);
 +            println!("mutability: {:?}", muta);
 +            print_expr(cx, e, indent + 1);
 +        },
 +        hir::ExprKind::Break(_, ref e) => {
 +            println!("{}Break", ind);
 +            if let Some(e) = *e {
 +                print_expr(cx, e, indent + 1);
 +            }
 +        },
 +        hir::ExprKind::Continue(_) => println!("{}Again", ind),
 +        hir::ExprKind::Ret(ref e) => {
 +            println!("{}Ret", ind);
 +            if let Some(e) = *e {
 +                print_expr(cx, e, indent + 1);
 +            }
 +        },
 +        hir::ExprKind::InlineAsm(asm) => {
 +            println!("{}InlineAsm", ind);
 +            println!("{}template: {}", ind, InlineAsmTemplatePiece::to_string(asm.template));
 +            println!("{}options: {:?}", ind, asm.options);
 +            println!("{}operands:", ind);
 +            for (op, _op_sp) in asm.operands {
 +                match op {
 +                    hir::InlineAsmOperand::In { expr, .. }
 +                    | hir::InlineAsmOperand::InOut { expr, .. }
 +                    | hir::InlineAsmOperand::Sym { expr } => print_expr(cx, expr, indent + 1),
 +                    hir::InlineAsmOperand::Out { expr, .. } => {
 +                        if let Some(expr) = expr {
 +                            print_expr(cx, expr, indent + 1);
 +                        }
 +                    },
 +                    hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
 +                        print_expr(cx, in_expr, indent + 1);
 +                        if let Some(out_expr) = out_expr {
 +                            print_expr(cx, out_expr, indent + 1);
 +                        }
 +                    },
 +                    hir::InlineAsmOperand::Const { anon_const } => {
 +                        println!("{}anon_const:", ind);
 +                        print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
 +                    },
 +                }
 +            }
 +        },
 +        hir::ExprKind::LlvmInlineAsm(asm) => {
 +            let inputs = &asm.inputs_exprs;
 +            let outputs = &asm.outputs_exprs;
 +            println!("{}LlvmInlineAsm", ind);
 +            println!("{}inputs:", ind);
 +            for e in inputs.iter() {
 +                print_expr(cx, e, indent + 1);
 +            }
 +            println!("{}outputs:", ind);
 +            for e in outputs.iter() {
 +                print_expr(cx, e, indent + 1);
 +            }
 +        },
 +        hir::ExprKind::Struct(path, fields, ref base) => {
 +            println!("{}Struct", ind);
 +            println!("{}path: {:?}", ind, path);
 +            for field in fields {
 +                println!("{}field \"{}\":", ind, field.ident.name);
 +                print_expr(cx, field.expr, indent + 1);
 +            }
 +            if let Some(base) = *base {
 +                println!("{}base:", ind);
 +                print_expr(cx, base, indent + 1);
 +            }
 +        },
 +        hir::ExprKind::ConstBlock(ref anon_const) => {
 +            println!("{}ConstBlock", ind);
 +            println!("{}anon_const:", ind);
 +            print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
 +        },
 +        hir::ExprKind::Repeat(val, ref anon_const) => {
 +            println!("{}Repeat", ind);
 +            println!("{}value:", ind);
 +            print_expr(cx, val, indent + 1);
 +            println!("{}repeat count:", ind);
 +            print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
 +        },
 +        hir::ExprKind::Err => {
 +            println!("{}Err", ind);
 +        },
 +        hir::ExprKind::DropTemps(e) => {
 +            println!("{}DropTemps", ind);
 +            print_expr(cx, e, indent + 1);
 +        },
 +    }
 +}
 +
 +fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
 +    let did = item.def_id;
 +    println!("item `{}`", item.ident.name);
 +    match item.vis.node {
 +        hir::VisibilityKind::Public => println!("public"),
 +        hir::VisibilityKind::Crate(_) => println!("visible crate wide"),
 +        hir::VisibilityKind::Restricted { path, .. } => println!(
 +            "visible in module `{}`",
 +            rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false))
 +        ),
 +        hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"),
 +    }
 +    match item.kind {
 +        hir::ItemKind::ExternCrate(ref _renamed_from) => {
 +            if let Some(crate_id) = cx.tcx.extern_mod_stmt_cnum(did) {
 +                let source = cx.tcx.used_crate_source(crate_id);
 +                if let Some(ref src) = source.dylib {
 +                    println!("extern crate dylib source: {:?}", src.0);
 +                }
 +                if let Some(ref src) = source.rlib {
 +                    println!("extern crate rlib source: {:?}", src.0);
 +                }
 +            } else {
 +                println!("weird extern crate without a crate id");
 +            }
 +        },
 +        hir::ItemKind::Use(path, ref kind) => println!("{:?}, {:?}", path, kind),
 +        hir::ItemKind::Static(..) => println!("static item of type {:#?}", cx.tcx.type_of(did)),
 +        hir::ItemKind::Const(..) => println!("const item of type {:#?}", cx.tcx.type_of(did)),
 +        hir::ItemKind::Fn(..) => {
 +            let item_ty = cx.tcx.type_of(did);
 +            println!("function of type {:#?}", item_ty);
 +        },
 +        hir::ItemKind::Macro(ref macro_def) => {
 +            if macro_def.macro_rules {
 +                println!("macro introduced by `macro_rules!`");
 +            } else {
 +                println!("macro introduced by `macro`");
 +            }
 +        },
 +        hir::ItemKind::Mod(..) => println!("module"),
 +        hir::ItemKind::ForeignMod { abi, .. } => println!("foreign module with abi: {}", abi),
 +        hir::ItemKind::GlobalAsm(asm) => println!("global asm: {:?}", asm),
 +        hir::ItemKind::TyAlias(..) => {
 +            println!("type alias for {:?}", cx.tcx.type_of(did));
 +        },
 +        hir::ItemKind::OpaqueTy(..) => {
 +            println!("existential type with real type {:?}", cx.tcx.type_of(did));
 +        },
 +        hir::ItemKind::Enum(..) => {
 +            println!("enum definition of type {:?}", cx.tcx.type_of(did));
 +        },
 +        hir::ItemKind::Struct(..) => {
 +            println!("struct definition of type {:?}", cx.tcx.type_of(did));
 +        },
 +        hir::ItemKind::Union(..) => {
 +            println!("union definition of type {:?}", cx.tcx.type_of(did));
 +        },
 +        hir::ItemKind::Trait(..) => {
 +            println!("trait decl");
 +            if cx.tcx.trait_is_auto(did.to_def_id()) {
 +                println!("trait is auto");
 +            } else {
 +                println!("trait is not auto");
 +            }
 +        },
 +        hir::ItemKind::TraitAlias(..) => {
 +            println!("trait alias");
 +        },
 +        hir::ItemKind::Impl(hir::Impl {
 +            of_trait: Some(ref _trait_ref),
 +            ..
 +        }) => {
 +            println!("trait impl");
 +        },
 +        hir::ItemKind::Impl(hir::Impl { of_trait: None, .. }) => {
 +            println!("impl");
 +        },
 +    }
 +}
 +
 +#[allow(clippy::similar_names)]
 +#[allow(clippy::too_many_lines)]
 +fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) {
 +    let ind = "  ".repeat(indent);
 +    println!("{}+", ind);
 +    match pat.kind {
 +        hir::PatKind::Wild => println!("{}Wild", ind),
 +        hir::PatKind::Binding(ref mode, .., ident, ref inner) => {
 +            println!("{}Binding", ind);
 +            println!("{}mode: {:?}", ind, mode);
 +            println!("{}name: {}", ind, ident.name);
 +            if let Some(inner) = *inner {
 +                println!("{}inner:", ind);
 +                print_pat(cx, inner, indent + 1);
 +            }
 +        },
 +        hir::PatKind::Or(fields) => {
 +            println!("{}Or", ind);
 +            for field in fields {
 +                print_pat(cx, field, indent + 1);
 +            }
 +        },
 +        hir::PatKind::Struct(ref path, fields, ignore) => {
 +            println!("{}Struct", ind);
 +            println!(
 +                "{}name: {}",
 +                ind,
 +                rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
 +            );
 +            println!("{}ignore leftover fields: {}", ind, ignore);
 +            println!("{}fields:", ind);
 +            for field in fields {
 +                println!("{}  field name: {}", ind, field.ident.name);
 +                if field.is_shorthand {
 +                    println!("{}  in shorthand notation", ind);
 +                }
 +                print_pat(cx, field.pat, indent + 1);
 +            }
 +        },
 +        hir::PatKind::TupleStruct(ref path, fields, opt_dots_position) => {
 +            println!("{}TupleStruct", ind);
 +            println!(
 +                "{}path: {}",
 +                ind,
 +                rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
 +            );
 +            if let Some(dot_position) = opt_dots_position {
 +                println!("{}dot position: {}", ind, dot_position);
 +            }
 +            for field in fields {
 +                print_pat(cx, field, indent + 1);
 +            }
 +        },
 +        hir::PatKind::Path(hir::QPath::Resolved(ref ty, path)) => {
 +            println!("{}Resolved Path, {:?}", ind, ty);
 +            println!("{}path: {:?}", ind, path);
 +        },
 +        hir::PatKind::Path(hir::QPath::TypeRelative(ty, seg)) => {
 +            println!("{}Relative Path, {:?}", ind, ty);
 +            println!("{}seg: {:?}", ind, seg);
 +        },
 +        hir::PatKind::Path(hir::QPath::LangItem(lang_item, ..)) => {
 +            println!("{}Lang Item Path, {:?}", ind, lang_item.name());
 +        },
 +        hir::PatKind::Tuple(pats, opt_dots_position) => {
 +            println!("{}Tuple", ind);
 +            if let Some(dot_position) = opt_dots_position {
 +                println!("{}dot position: {}", ind, dot_position);
 +            }
 +            for field in pats {
 +                print_pat(cx, field, indent + 1);
 +            }
 +        },
 +        hir::PatKind::Box(inner) => {
 +            println!("{}Box", ind);
 +            print_pat(cx, inner, indent + 1);
 +        },
 +        hir::PatKind::Ref(inner, ref muta) => {
 +            println!("{}Ref", ind);
 +            println!("{}mutability: {:?}", ind, muta);
 +            print_pat(cx, inner, indent + 1);
 +        },
 +        hir::PatKind::Lit(e) => {
 +            println!("{}Lit", ind);
 +            print_expr(cx, e, indent + 1);
 +        },
 +        hir::PatKind::Range(ref l, ref r, ref range_end) => {
 +            println!("{}Range", ind);
 +            if let Some(expr) = l {
 +                print_expr(cx, expr, indent + 1);
 +            }
 +            if let Some(expr) = r {
 +                print_expr(cx, expr, indent + 1);
 +            }
 +            match *range_end {
 +                hir::RangeEnd::Included => println!("{} end included", ind),
 +                hir::RangeEnd::Excluded => println!("{} end excluded", ind),
 +            }
 +        },
 +        hir::PatKind::Slice(first_pats, ref range, last_pats) => {
 +            println!("{}Slice [a, b, ..i, y, z]", ind);
 +            println!("[a, b]:");
 +            for pat in first_pats {
 +                print_pat(cx, pat, indent + 1);
 +            }
 +            println!("i:");
 +            if let Some(pat) = *range {
 +                print_pat(cx, pat, indent + 1);
 +            }
 +            println!("[y, z]:");
 +            for pat in last_pats {
 +                print_pat(cx, pat, indent + 1);
 +            }
 +        },
 +    }
 +}
 +
 +fn print_guard(cx: &LateContext<'_>, guard: &hir::Guard<'_>, indent: usize) {
 +    let ind = "  ".repeat(indent);
 +    println!("{}+", ind);
 +    match guard {
 +        hir::Guard::If(expr) => {
 +            println!("{}If", ind);
 +            print_expr(cx, expr, indent + 1);
 +        },
 +        hir::Guard::IfLet(pat, expr) => {
 +            println!("{}IfLet", ind);
 +            print_pat(cx, pat, indent + 1);
 +            print_expr(cx, expr, indent + 1);
 +        },
 +    }
 +}
index d660008e7d18441d5249f19621cec9c450fc9c26,0000000000000000000000000000000000000000..756c33d70c26d319769a023da4125fadd10c4a59
mode 100644,000000..100644
--- /dev/null
@@@ -1,1236 -1,0 +1,1228 @@@
-     BinOpKind, Block, Crate, Expr, ExprKind, HirId, Item, Local, MatchSource, MutTy, Mutability, Node, Path, Stmt,
-     StmtKind, Ty, TyKind, UnOp,
 +use clippy_utils::consts::{constant_simple, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
++use clippy_utils::higher;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::match_type;
 +use clippy_utils::{
 +    is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, method_calls, path_to_res,
 +    paths, SpanlessEq,
 +};
 +use if_chain::if_chain;
 +use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId};
 +use rustc_ast::visit::FnKind;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::hir_id::CRATE_HIR_ID;
 +use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
 +use rustc_hir::{
-             if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
++    BinOpKind, Block, Crate, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty,
++    TyKind, UnOp,
 +};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::mir::interpret::ConstValue;
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Spanned;
 +use rustc_span::symbol::{Symbol, SymbolStr};
 +use rustc_span::{BytePos, Span};
 +use rustc_typeck::hir_ty_to_ty;
 +
 +use std::borrow::{Borrow, Cow};
 +
 +#[cfg(feature = "metadata-collector-lint")]
 +pub mod metadata_collector;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for various things we like to keep tidy in clippy.
 +    ///
 +    /// ### Why is this bad?
 +    /// We like to pretend we're an example of tidy code.
 +    ///
 +    /// ### Example
 +    /// Wrong ordering of the util::paths constants.
 +    pub CLIPPY_LINTS_INTERNAL,
 +    internal,
 +    "various things that will negatively affect your clippy experience"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Ensures every lint is associated to a `LintPass`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The compiler only knows lints via a `LintPass`. Without
 +    /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
 +    /// know the name of the lint.
 +    ///
 +    /// ### Known problems
 +    /// Only checks for lints associated using the
 +    /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// declare_lint! { pub LINT_1, ... }
 +    /// declare_lint! { pub LINT_2, ... }
 +    /// declare_lint! { pub FORGOTTEN_LINT, ... }
 +    /// // ...
 +    /// declare_lint_pass!(Pass => [LINT_1, LINT_2]);
 +    /// // missing FORGOTTEN_LINT
 +    /// ```
 +    pub LINT_WITHOUT_LINT_PASS,
 +    internal,
 +    "declaring a lint without associating it in a LintPass"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*`
 +    /// variant of the function.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `utils::*` variants also add a link to the Clippy documentation to the
 +    /// warning/error messages.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// cx.span_lint(LINT_NAME, "message");
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// utils::span_lint(cx, LINT_NAME, "message");
 +    /// ```
 +    pub COMPILER_LINT_FUNCTIONS,
 +    internal,
 +    "usage of the lint functions of the compiler instead of the utils::* variant"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `cx.outer().expn_data()` and suggests to use
 +    /// the `cx.outer_expn_data()`
 +    ///
 +    /// ### Why is this bad?
 +    /// `cx.outer_expn_data()` is faster and more concise.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// expr.span.ctxt().outer().expn_data()
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// expr.span.ctxt().outer_expn_data()
 +    /// ```
 +    pub OUTER_EXPN_EXPN_DATA,
 +    internal,
 +    "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Not an actual lint. This lint is only meant for testing our customized internal compiler
 +    /// error message by calling `panic`.
 +    ///
 +    /// ### Why is this bad?
 +    /// ICE in large quantities can damage your teeth
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// 🍦🍦🍦🍦🍦
 +    /// ```
 +    pub PRODUCE_ICE,
 +    internal,
 +    "this message should not appear anywhere as we ICE before and don't emit the lint"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for cases of an auto-generated lint without an updated description,
 +    /// i.e. `default lint description`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Indicates that the lint is not finished.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// declare_lint! { pub COOL_LINT, nursery, "default lint description" }
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" }
 +    /// ```
 +    pub DEFAULT_LINT,
 +    internal,
 +    "found 'default lint description' in a lint declaration"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Lints `span_lint_and_then` function calls, where the
 +    /// closure argument has only one statement and that statement is a method
 +    /// call to `span_suggestion`, `span_help`, `span_note` (using the same
 +    /// span), `help` or `note`.
 +    ///
 +    /// These usages of `span_lint_and_then` should be replaced with one of the
 +    /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or
 +    /// `span_lint_and_note`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the wrapper `span_lint_and_*` functions, is more
 +    /// convenient, readable and less error prone.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.span_suggestion(
 +    ///         expr.span,
 +    ///         help_msg,
 +    ///         sugg.to_string(),
 +    ///         Applicability::MachineApplicable,
 +    ///     );
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.span_help(expr.span, help_msg);
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.help(help_msg);
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.span_note(expr.span, note_msg);
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.note(note_msg);
 +    /// });
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// span_lint_and_sugg(
 +    ///     cx,
 +    ///     TEST_LINT,
 +    ///     expr.span,
 +    ///     lint_msg,
 +    ///     help_msg,
 +    ///     sugg.to_string(),
 +    ///     Applicability::MachineApplicable,
 +    /// );
 +    /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg);
 +    /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg);
 +    /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg);
 +    /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg);
 +    /// ```
 +    pub COLLAPSIBLE_SPAN_LINT_CALLS,
 +    internal,
 +    "found collapsible `span_lint_and_then` calls"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `utils::match_type()` on a type diagnostic item
 +    /// and suggests to use `utils::is_type_diagnostic_item()` instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// `utils::is_type_diagnostic_item()` does not require hardcoded paths.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// utils::match_type(cx, ty, &paths::VEC)
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// utils::is_type_diagnostic_item(cx, ty, sym::vec_type)
 +    /// ```
 +    pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +    internal,
 +    "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks the paths module for invalid paths.
 +    ///
 +    /// ### Why is this bad?
 +    /// It indicates a bug in the code.
 +    ///
 +    /// ### Example
 +    /// None.
 +    pub INVALID_PATHS,
 +    internal,
 +    "invalid path"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for interning symbols that have already been pre-interned and defined as constants.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's faster and easier to use the symbol constant.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// let _ = sym!(f32);
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// let _ = sym::f32;
 +    /// ```
 +    pub INTERNING_DEFINED_SYMBOL,
 +    internal,
 +    "interning a symbol that is pre-interned and defined as a constant"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary conversion from Symbol to a string.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's faster use symbols directly intead of strings.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// symbol.as_str() == "clippy";
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// symbol == sym::clippy;
 +    /// ```
 +    pub UNNECESSARY_SYMBOL_STR,
 +    internal,
 +    "unnecessary conversion between Symbol and string"
 +}
 +
 +declare_clippy_lint! {
 +    /// Finds unidiomatic usage of `if_chain!`
 +    pub IF_CHAIN_STYLE,
 +    internal,
 +    "non-idiomatic `if_chain!` usage"
 +}
 +
 +declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
 +
 +impl EarlyLintPass for ClippyLintsInternal {
 +    fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &AstCrate) {
 +        if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") {
 +            if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind {
 +                if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") {
 +                    if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind {
 +                        let mut last_name: Option<SymbolStr> = None;
 +                        for item in items {
 +                            let name = item.ident.as_str();
 +                            if let Some(ref last_name) = last_name {
 +                                if **last_name > *name {
 +                                    span_lint(
 +                                        cx,
 +                                        CLIPPY_LINTS_INTERNAL,
 +                                        item.span,
 +                                        "this constant should be before the previous constant due to lexical \
 +                                         ordering",
 +                                    );
 +                                }
 +                            }
 +                            last_name = Some(name);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Debug, Default)]
 +pub struct LintWithoutLintPass {
 +    declared_lints: FxHashMap<Symbol, Span>,
 +    registered_lints: FxHashSet<Symbol>,
 +}
 +
 +impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) {
 +            return;
 +        }
 +
 +        if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
 +            if is_lint_ref_type(cx, ty) {
 +                let expr = &cx.tcx.hir().body(body_id).value;
 +                if_chain! {
 +                    if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind;
 +                    if let ExprKind::Struct(_, fields, _) = inner_exp.kind;
 +                    let field = fields
 +                        .iter()
 +                        .find(|f| f.ident.as_str() == "desc")
 +                        .expect("lints must have a description field");
 +                    if let ExprKind::Lit(Spanned {
 +                        node: LitKind::Str(ref sym, _),
 +                        ..
 +                    }) = field.expr.kind;
 +                    if sym.as_str() == "default lint description";
 +
 +                    then {
 +                        span_lint(
 +                            cx,
 +                            DEFAULT_LINT,
 +                            item.span,
 +                            &format!("the lint `{}` has the default lint description", item.ident.name),
 +                        );
 +                    }
 +                }
 +                self.declared_lints.insert(item.ident.name, item.span);
 +            }
 +        } else if is_expn_of(item.span, "impl_lint_pass").is_some()
 +            || is_expn_of(item.span, "declare_lint_pass").is_some()
 +        {
 +            if let hir::ItemKind::Impl(hir::Impl {
 +                of_trait: None,
 +                items: impl_item_refs,
 +                ..
 +            }) = item.kind
 +            {
 +                let mut collector = LintCollector {
 +                    output: &mut self.registered_lints,
 +                    cx,
 +                };
 +                let body_id = cx.tcx.hir().body_owned_by(
 +                    impl_item_refs
 +                        .iter()
 +                        .find(|iiref| iiref.ident.as_str() == "get_lints")
 +                        .expect("LintPass needs to implement get_lints")
 +                        .id
 +                        .hir_id(),
 +                );
 +                collector.visit_expr(&cx.tcx.hir().body(body_id).value);
 +            }
 +        }
 +    }
 +
 +    fn check_crate_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Crate<'_>) {
 +        if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) {
 +            return;
 +        }
 +
 +        for (lint_name, &lint_span) in &self.declared_lints {
 +            // When using the `declare_tool_lint!` macro, the original `lint_span`'s
 +            // file points to "<rustc macros>".
 +            // `compiletest-rs` thinks that's an error in a different file and
 +            // just ignores it. This causes the test in compile-fail/lint_pass
 +            // not able to capture the error.
 +            // Therefore, we need to climb the macro expansion tree and find the
 +            // actual span that invoked `declare_tool_lint!`:
 +            let lint_span = lint_span.ctxt().outer_expn_data().call_site;
 +
 +            if !self.registered_lints.contains(lint_name) {
 +                span_lint(
 +                    cx,
 +                    LINT_WITHOUT_LINT_PASS,
 +                    lint_span,
 +                    &format!("the lint `{}` is not added to any `LintPass`", lint_name),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool {
 +    if let TyKind::Rptr(
 +        _,
 +        MutTy {
 +            ty: inner,
 +            mutbl: Mutability::Not,
 +        },
 +    ) = ty.kind
 +    {
 +        if let TyKind::Path(ref path) = inner.kind {
 +            if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) {
 +                return match_def_path(cx, def_id, &paths::LINT);
 +            }
 +        }
 +    }
 +
 +    false
 +}
 +
 +struct LintCollector<'a, 'tcx> {
 +    output: &'a mut FxHashSet<Symbol>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
 +        if path.segments.len() == 1 {
 +            self.output.insert(path.segments[0].ident.name);
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::All(self.cx.tcx.hir())
 +    }
 +}
 +
 +#[derive(Clone, Default)]
 +pub struct CompilerLintFunctions {
 +    map: FxHashMap<&'static str, &'static str>,
 +}
 +
 +impl CompilerLintFunctions {
 +    #[must_use]
 +    pub fn new() -> Self {
 +        let mut map = FxHashMap::default();
 +        map.insert("span_lint", "utils::span_lint");
 +        map.insert("struct_span_lint", "utils::span_lint");
 +        map.insert("lint", "utils::span_lint");
 +        map.insert("span_lint_note", "utils::span_lint_and_note");
 +        map.insert("span_lint_help", "utils::span_lint_and_help");
 +        Self { map }
 +    }
 +}
 +
 +impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
-             let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
++            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 (cond, then, els) = match expr.kind {
-             ExprKind::If(cond, then, els) => (Some(cond), then, els.is_some()),
-             ExprKind::Match(
-                 _,
-                 [arm, ..],
-                 MatchSource::IfLetDesugar {
-                     contains_else_clause: els,
-                 },
-             ) => (None, arm.body, els),
-             _ => return,
++            let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
 +            if match_type(cx, ty, &paths::EARLY_CONTEXT)
 +                || match_type(cx, ty, &paths::LATE_CONTEXT);
 +            then {
 +                span_lint_and_help(
 +                    cx,
 +                    COMPILER_LINT_FUNCTIONS,
 +                    path.ident.span,
 +                    "usage of a compiler lint function",
 +                    None,
 +                    &format!("please use the Clippy variant of this function: `{}`", sugg),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
 +
 +impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) {
 +            return;
 +        }
 +
 +        let (method_names, arg_lists, spans) = method_calls(expr, 2);
 +        let method_names: Vec<SymbolStr> = method_names.iter().map(|s| s.as_str()).collect();
 +        let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect();
 +        if_chain! {
 +            if let ["expn_data", "outer_expn"] = method_names.as_slice();
 +            let args = arg_lists[1];
 +            if args.len() == 1;
 +            let self_arg = &args[0];
 +            let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
 +            if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    OUTER_EXPN_EXPN_DATA,
 +                    spans[1].with_hi(expr.span.hi()),
 +                    "usage of `outer_expn().expn_data()`",
 +                    "try",
 +                    "outer_expn_data()".to_string(),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +declare_lint_pass!(ProduceIce => [PRODUCE_ICE]);
 +
 +impl EarlyLintPass for ProduceIce {
 +    fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
 +        if is_trigger_fn(fn_kind) {
 +            panic!("Would you like some help with that?");
 +        }
 +    }
 +}
 +
 +fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
 +    match fn_kind {
 +        FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy",
 +        FnKind::Closure(..) => false,
 +    }
 +}
 +
 +declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::Call(func, and_then_args) = expr.kind;
 +            if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
 +            if and_then_args.len() == 5;
 +            if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind;
 +            let body = cx.tcx.hir().body(*body_id);
 +            if let ExprKind::Block(block, _) = &body.value.kind;
 +            let stmts = &block.stmts;
 +            if stmts.len() == 1 && block.expr.is_none();
 +            if let StmtKind::Semi(only_expr) = &stmts[0].kind;
 +            if let ExprKind::MethodCall(ps, _, span_call_args, _) = &only_expr.kind;
 +            then {
 +                let and_then_snippets = get_and_then_snippets(cx, and_then_args);
 +                let mut sle = SpanlessEq::new(cx).deny_side_effects();
 +                match &*ps.ident.as_str() {
 +                    "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
 +                        suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args));
 +                    },
 +                    "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
 +                        let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
 +                        suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
 +                    },
 +                    "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
 +                        let note_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
 +                        suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
 +                    },
 +                    "help" => {
 +                        let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
 +                        suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
 +                    }
 +                    "note" => {
 +                        let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
 +                        suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false);
 +                    }
 +                    _  => (),
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +struct AndThenSnippets<'a> {
 +    cx: Cow<'a, str>,
 +    lint: Cow<'a, str>,
 +    span: Cow<'a, str>,
 +    msg: Cow<'a, str>,
 +}
 +
 +fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> {
 +    let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx");
 +    let lint_snippet = snippet(cx, and_then_snippets[1].span, "..");
 +    let span_snippet = snippet(cx, and_then_snippets[2].span, "span");
 +    let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#);
 +
 +    AndThenSnippets {
 +        cx: cx_snippet,
 +        lint: lint_snippet,
 +        span: span_snippet,
 +        msg: msg_snippet,
 +    }
 +}
 +
 +struct SpanSuggestionSnippets<'a> {
 +    help: Cow<'a, str>,
 +    sugg: Cow<'a, str>,
 +    applicability: Cow<'a, str>,
 +}
 +
 +fn span_suggestion_snippets<'a, 'hir>(
 +    cx: &LateContext<'_>,
 +    span_call_args: &'hir [Expr<'hir>],
 +) -> SpanSuggestionSnippets<'a> {
 +    let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
 +    let sugg_snippet = snippet(cx, span_call_args[3].span, "..");
 +    let applicability_snippet = snippet(cx, span_call_args[4].span, "Applicability::MachineApplicable");
 +
 +    SpanSuggestionSnippets {
 +        help: help_snippet,
 +        sugg: sugg_snippet,
 +        applicability: applicability_snippet,
 +    }
 +}
 +
 +fn suggest_suggestion(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    span_suggestion_snippets: &SpanSuggestionSnippets<'_>,
 +) {
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
 +        "this call is collapsible",
 +        "collapse into",
 +        format!(
 +            "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})",
 +            and_then_snippets.cx,
 +            and_then_snippets.lint,
 +            and_then_snippets.span,
 +            and_then_snippets.msg,
 +            span_suggestion_snippets.help,
 +            span_suggestion_snippets.sugg,
 +            span_suggestion_snippets.applicability
 +        ),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn suggest_help(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    help: &str,
 +    with_span: bool,
 +) {
 +    let option_span = if with_span {
 +        format!("Some({})", and_then_snippets.span)
 +    } else {
 +        "None".to_string()
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
 +        "this call is collapsible",
 +        "collapse into",
 +        format!(
 +            "span_lint_and_help({}, {}, {}, {}, {}, {})",
 +            and_then_snippets.cx,
 +            and_then_snippets.lint,
 +            and_then_snippets.span,
 +            and_then_snippets.msg,
 +            &option_span,
 +            help
 +        ),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn suggest_note(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    note: &str,
 +    with_span: bool,
 +) {
 +    let note_span = if with_span {
 +        format!("Some({})", and_then_snippets.span)
 +    } else {
 +        "None".to_string()
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
 +        "this call is collspible",
 +        "collapse into",
 +        format!(
 +            "span_lint_and_note({}, {}, {}, {}, {}, {})",
 +            and_then_snippets.cx,
 +            and_then_snippets.lint,
 +            and_then_snippets.span,
 +            and_then_snippets.msg,
 +            note_span,
 +            note
 +        ),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            // Check if this is a call to utils::match_type()
 +            if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind;
 +            if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]);
 +            // Extract the path to the matched type
 +            if let Some(segments) = path_to_matched_type(cx, ty_path);
 +            let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
 +            if let Some(ty_did) = path_to_res(cx, &segments[..]).opt_def_id();
 +            // Check if the matched type is a diagnostic item
 +            let diag_items = cx.tcx.diagnostic_items(ty_did.krate);
 +            if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None });
 +            then {
 +                // TODO: check paths constants from external crates.
 +                let cx_snippet = snippet(cx, context.span, "_");
 +                let ty_snippet = snippet(cx, ty.span, "_");
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +                    expr.span,
 +                    "usage of `clippy_utils::ty::match_type()` on a type diagnostic item",
 +                    "try",
 +                    format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
 +                    Applicability::MaybeIncorrect,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<SymbolStr>> {
 +    use rustc_hir::ItemKind;
 +
 +    match &expr.kind {
 +        ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr),
 +        ExprKind::Path(qpath) => match cx.qpath_res(qpath, expr.hir_id) {
 +            Res::Local(hir_id) => {
 +                let parent_id = cx.tcx.hir().get_parent_node(hir_id);
 +                if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) {
 +                    if let Some(init) = local.init {
 +                        return path_to_matched_type(cx, init);
 +                    }
 +                }
 +            },
 +            Res::Def(DefKind::Const | DefKind::Static, def_id) => {
 +                if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
 +                    if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
 +                        let body = cx.tcx.hir().body(body_id);
 +                        return path_to_matched_type(cx, &body.value);
 +                    }
 +                }
 +            },
 +            _ => {},
 +        },
 +        ExprKind::Array(exprs) => {
 +            let segments: Vec<SymbolStr> = exprs
 +                .iter()
 +                .filter_map(|expr| {
 +                    if let ExprKind::Lit(lit) = &expr.kind {
 +                        if let LitKind::Str(sym, _) = lit.node {
 +                            return Some(sym.as_str());
 +                        }
 +                    }
 +
 +                    None
 +                })
 +                .collect();
 +
 +            if segments.len() == exprs.len() {
 +                return Some(segments);
 +            }
 +        },
 +        _ => {},
 +    }
 +
 +    None
 +}
 +
 +// This is not a complete resolver for paths. It works on all the paths currently used in the paths
 +// module.  That's all it does and all it needs to do.
 +pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
 +    if path_to_res(cx, path) != Res::Err {
 +        return true;
 +    }
 +
 +    // Some implementations can't be found by `path_to_res`, particularly inherent
 +    // implementations of native types. Check lang items.
 +    let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
 +    let lang_items = cx.tcx.lang_items();
 +    for item_def_id in lang_items.items().iter().flatten() {
 +        let lang_item_path = cx.get_def_path(*item_def_id);
 +        if path_syms.starts_with(&lang_item_path) {
 +            if let [item] = &path_syms[lang_item_path.len()..] {
 +                for child in cx.tcx.item_children(*item_def_id) {
 +                    if child.ident.name == *item {
 +                        return true;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    false
 +}
 +
 +declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        let local_def_id = &cx.tcx.parent_module(item.hir_id());
 +        let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
 +        if_chain! {
 +            if mod_name.as_str() == "paths";
 +            if let hir::ItemKind::Const(ty, body_id) = item.kind;
 +            let ty = hir_ty_to_ty(cx.tcx, ty);
 +            if let ty::Array(el_ty, _) = &ty.kind();
 +            if let ty::Ref(_, el_ty, _) = &el_ty.kind();
 +            if el_ty.is_str();
 +            let body = cx.tcx.hir().body(body_id);
 +            let typeck_results = cx.tcx.typeck_body(body_id);
 +            if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value);
 +            let path: Vec<&str> = path.iter().map(|x| {
 +                    if let Constant::Str(s) = x {
 +                        s.as_str()
 +                    } else {
 +                        // We checked the type of the constant above
 +                        unreachable!()
 +                    }
 +                }).collect();
 +            if !check_path(cx, &path[..]);
 +            then {
 +                span_lint(cx, CLIPPY_LINTS_INTERNAL, 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<'_>, _: &Crate<'_>) {
 +        if !self.symbol_map.is_empty() {
 +            return;
 +        }
 +
 +        for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
 +            if let Some(def_id) = path_to_res(cx, module).opt_def_id() {
 +                for item in cx.tcx.item_children(def_id).iter() {
 +                    if_chain! {
 +                        if let Res::Def(DefKind::Const, item_def_id) = item.res;
 +                        let ty = cx.tcx.type_of(item_def_id);
 +                        if match_type(cx, ty, &paths::SYMBOL);
 +                        if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
 +                        if let Ok(value) = value.to_u32();
 +                        then {
 +                            self.symbol_map.insert(value, item_def_id);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::Call(func, [arg]) = &expr.kind;
 +            if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind();
 +            if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN);
 +            if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg);
 +            let value = Symbol::intern(&arg).as_u32();
 +            if let Some(&def_id) = self.symbol_map.get(&value);
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    INTERNING_DEFINED_SYMBOL,
 +                    is_expn_of(expr.span, "sym").unwrap_or(expr.span),
 +                    "interning a defined symbol",
 +                    "try",
 +                    cx.tcx.def_path_str(def_id),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +        if let ExprKind::Binary(op, left, right) = expr.kind {
 +            if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) {
 +                let data = [
 +                    (left, self.symbol_str_expr(left, cx)),
 +                    (right, self.symbol_str_expr(right, cx)),
 +                ];
 +                match data {
 +                    // both operands are a symbol string
 +                    [(_, Some(left)), (_, Some(right))] => {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            UNNECESSARY_SYMBOL_STR,
 +                            expr.span,
 +                            "unnecessary `Symbol` to string conversion",
 +                            "try",
 +                            format!(
 +                                "{} {} {}",
 +                                left.as_symbol_snippet(cx),
 +                                op.node.as_str(),
 +                                right.as_symbol_snippet(cx),
 +                            ),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                    // one of the operands is a symbol string
 +                    [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => {
 +                        // creating an owned string for comparison
 +                        if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) {
 +                            span_lint_and_sugg(
 +                                cx,
 +                                UNNECESSARY_SYMBOL_STR,
 +                                expr.span,
 +                                "unnecessary string allocation",
 +                                "try",
 +                                format!("{}.as_str()", symbol.as_symbol_snippet(cx)),
 +                                Applicability::MachineApplicable,
 +                            );
 +                        }
 +                    },
 +                    // nothing found
 +                    [(_, None), (_, None)] => {},
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +impl InterningDefinedSymbol {
 +    fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option<SymbolStrExpr<'tcx>> {
 +        static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD];
 +        static SYMBOL_STR_PATHS: &[&[&str]] = &[
 +            &paths::SYMBOL_AS_STR,
 +            &paths::SYMBOL_TO_IDENT_STRING,
 +            &paths::TO_STRING_METHOD,
 +        ];
 +        // SymbolStr might be de-referenced: `&*symbol.as_str()`
 +        let call = if_chain! {
 +            if let ExprKind::AddrOf(_, _, e) = expr.kind;
 +            if let ExprKind::Unary(UnOp::Deref, e) = e.kind;
 +            then { e } else { expr }
 +        };
 +        if_chain! {
 +            // is a method call
 +            if let ExprKind::MethodCall(_, _, [item], _) = call.kind;
 +            if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id);
 +            let ty = cx.typeck_results().expr_ty(item);
 +            // ...on either an Ident or a Symbol
 +            if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) {
 +                Some(false)
 +            } else if match_type(cx, ty, &paths::IDENT) {
 +                Some(true)
 +            } else {
 +                None
 +            };
 +            // ...which converts it to a string
 +            let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS };
 +            if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path));
 +            then {
 +                let is_to_owned = path.last().unwrap().ends_with("string");
 +                return Some(SymbolStrExpr::Expr {
 +                    item,
 +                    is_ident,
 +                    is_to_owned,
 +                });
 +            }
 +        }
 +        // is a string constant
 +        if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) {
 +            let value = Symbol::intern(&s).as_u32();
 +            // ...which matches a symbol constant
 +            if let Some(&def_id) = self.symbol_map.get(&value) {
 +                return Some(SymbolStrExpr::Const(def_id));
 +            }
 +        }
 +        None
 +    }
 +}
 +
 +enum SymbolStrExpr<'tcx> {
 +    /// a string constant with a corresponding symbol constant
 +    Const(DefId),
 +    /// a "symbol to string" expression like `symbol.as_str()`
 +    Expr {
 +        /// part that evaluates to `Symbol` or `Ident`
 +        item: &'tcx Expr<'tcx>,
 +        is_ident: bool,
 +        /// whether an owned `String` is created like `to_ident_string()`
 +        is_to_owned: bool,
 +    },
 +}
 +
 +impl<'tcx> SymbolStrExpr<'tcx> {
 +    /// Returns a snippet that evaluates to a `Symbol` and is const if possible
 +    fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> {
 +        match *self {
 +            Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(),
 +            Self::Expr { item, is_ident, .. } => {
 +                let mut snip = snippet(cx, item.span.source_callsite(), "..");
 +                if is_ident {
 +                    // get `Ident.name`
 +                    snip.to_mut().push_str(".name");
 +                }
 +                snip
 +            },
 +        }
 +    }
 +}
 +
 +declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for IfChainStyle {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
 +        let (local, after, if_chain_span) = if_chain! {
 +            if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts;
 +            if let Some(if_chain_span) = is_expn_of(block.span, "if_chain");
 +            then { (local, after, if_chain_span) } else { return }
 +        };
 +        if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) {
 +            span_lint(
 +                cx,
 +                IF_CHAIN_STYLE,
 +                if_chain_local_span(cx, local, if_chain_span),
 +                "`let` expression should be above the `if_chain!`",
 +            );
 +        } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) {
 +            span_lint(
 +                cx,
 +                IF_CHAIN_STYLE,
 +                if_chain_local_span(cx, local, if_chain_span),
 +                "`let` expression should be inside `then { .. }`",
 +            );
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-             if let Some(cond) = cond;
++        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 matches!(tail.kind,
-             ExprKind::If(_, _, None)
-             | ExprKind::Match(.., MatchSource::IfLetDesugar { contains_else_clause: false }));
 +            if let ExprKind::Binary(op, _, _) = cond.kind;
 +            if op.node == BinOpKind::And;
 +            if cx.sess().source_map().is_multiline(cond.span);
 +            then {
 +                span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`");
 +            }
 +        }
 +        if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span)
 +            && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span)
 +        {
 +            span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`");
 +        }
 +    }
 +}
 +
 +fn check_nested_if_chains(
 +    cx: &LateContext<'_>,
 +    if_expr: &Expr<'_>,
 +    then_block: &Block<'_>,
 +    if_chain_span: Option<Span>,
 +) {
 +    #[rustfmt::skip]
 +    let (head, tail) = match *then_block {
 +        Block { stmts, expr: Some(tail), .. } => (stmts, tail),
 +        Block {
 +            stmts: &[
 +                ref head @ ..,
 +                Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. }
 +            ],
 +            ..
 +        } => (head, tail),
 +        _ => return,
 +    };
 +    if_chain! {
++        if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail);
 +        let sm = cx.sess().source_map();
 +        if head
 +            .iter()
 +            .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span));
 +        if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr);
 +        then {} else { return }
 +    }
 +    let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) {
 +        (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"),
 +        (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"),
 +        (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"),
 +        _ => return,
 +    };
 +    span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| {
 +        let (span, msg) = match head {
 +            [] => return,
 +            [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"),
 +            [a, .., b] => (
 +                a.span.to(b.span),
 +                "these `let` statements can also be in the `if_chain!`",
 +            ),
 +        };
 +        diag.span_help(span, msg);
 +    });
 +}
 +
 +fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool {
 +    cx.tcx
 +        .hir()
 +        .parent_iter(hir_id)
 +        .find(|(_, node)| {
 +            #[rustfmt::skip]
 +            !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_))
 +        })
 +        .map_or(false, |(id, _)| {
 +            is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span)
 +        })
 +}
 +
 +/// Checks a trailing slice of statements and expression of a `Block` to see if they are part
 +/// of the `then {..}` portion of an `if_chain!`
 +fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool {
 +    let span = if let [stmt, ..] = stmts {
 +        stmt.span
 +    } else if let Some(expr) = expr {
 +        expr.span
 +    } else {
 +        // empty `then {}`
 +        return true;
 +    };
 +    is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span)
 +}
 +
 +/// Creates a `Span` for `let x = ..;` in an `if_chain!` call.
 +fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span {
 +    let mut span = local.pat.span;
 +    if let Some(init) = local.init {
 +        span = span.to(init.span);
 +    }
 +    span.adjust(if_chain_span.ctxt().outer_expn());
 +    let sm = cx.sess().source_map();
 +    let span = sm.span_extend_to_prev_str(span, "let", false);
 +    let span = sm.span_extend_to_next_char(span, ';', false);
 +    Span::new(span.lo() - BytePos(3), span.hi() + BytePos(1), span.ctxt())
 +}
index a48a53850830f3e41dbd6f61a3ab59a66bab8ff2,0000000000000000000000000000000000000000..188d0419c3993c9589b872ece99c6ee2e575a7e8
mode 100644,000000..100644
--- /dev/null
@@@ -1,890 -1,0 +1,888 @@@
-         "* {name}: `{ty}`: {doc} (defaults to `{default}`)\n"
 +//! 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 therefor the entire lint is guarded by a feature flag called
 +//! `metadata-collector-lint`
 +//!
 +//! 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 if_chain::if_chain;
 +use rustc_ast as ast;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::{
 +    self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath,
 +};
 +use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
 +use rustc_middle::hir::map::Map;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{sym, Loc, Span, Symbol};
 +use serde::{ser::SerializeStruct, Serialize, Serializer};
 +use std::collections::BinaryHeap;
 +use std::fmt;
 +use std::fs::{self, OpenOptions};
 +use std::io::prelude::*;
 +use std::path::Path;
 +
 +use crate::utils::internal_lints::is_lint_ref_type;
 +use clippy_utils::{
 +    diagnostics::span_lint, last_path_segment, match_def_path, match_function_call, match_path, paths, ty::match_type,
 +    ty::walk_ptrs_ty_depth,
 +};
 +
 +/// 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; 3] = ["lint_author", "deep_code_inspection", "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 {
 +    () => {
-         // TODO xFrednet 2021-03-01: support function arguments?
++        "* `{name}`: `{ty}`: {doc} (defaults to `{default}`)\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";
 +
 +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. [...] "
 +    /// }
 +    /// ```
 +    pub INTERNAL_METADATA_COLLECTOR,
 +    internal_warn,
 +    "A busy bee collection metadata about lints"
 +}
 +
 +impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]);
 +
 +#[allow(clippy::module_name_repetitions)]
 +#[derive(Debug, Clone)]
 +pub struct MetadataCollector {
 +    /// All collected lints
 +    ///
 +    /// We use a Heap here to have the lints added in alphabetic order in the export
 +    lints: BinaryHeap<LintMetadata>,
 +    applicability_info: FxHashMap<String, ApplicabilityInfo>,
 +    config: Vec<ClippyConfiguration>,
 +}
 +
 +impl MetadataCollector {
 +    pub fn new() -> Self {
 +        Self {
 +            lints: BinaryHeap::<LintMetadata>::default(),
 +            applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(),
 +            config: collect_configs(),
 +        }
 +    }
 +
 +    fn get_lint_configs(&self, lint_name: &str) -> Option<String> {
 +        self.config
 +            .iter()
 +            .filter(|config| config.lints.iter().any(|lint| lint == lint_name))
 +            .map(ToString::to_string)
 +            .reduce(|acc, x| acc + &x)
 +            .map(|configurations| format!(CONFIGURATION_SECTION_TEMPLATE!(), configurations = configurations))
 +    }
 +}
 +
 +impl Drop for MetadataCollector {
 +    /// You might ask: How hacky is this?
 +    /// My answer:     YES
 +    fn drop(&mut self) {
 +        // The metadata collector gets dropped twice, this makes sure that we only write
 +        // when the list is full
 +        if self.lints.is_empty() {
 +            return;
 +        }
 +
 +        let mut applicability_info = std::mem::take(&mut self.applicability_info);
 +
 +        // Mapping the final data
 +        let mut lints = std::mem::take(&mut self.lints).into_sorted_vec();
 +        lints
 +            .iter_mut()
 +            .for_each(|x| x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default()));
 +
 +        // Outputting
 +        if Path::new(OUTPUT_FILE).exists() {
 +            fs::remove_file(OUTPUT_FILE).unwrap();
 +        }
 +        let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap();
 +        writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap();
 +    }
 +}
 +
 +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
 +struct LintMetadata {
 +    id: String,
 +    id_span: SerializableSpan,
 +    group: String,
 +    level: String,
 +    docs: String,
 +    /// 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, docs: String) -> Self {
 +        Self {
 +            id,
 +            id_span,
 +            group,
 +            level: level.to_string(),
 +            docs,
 +            applicability: None,
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
 +struct SerializableSpan {
 +    path: String,
 +    line: usize,
 +}
 +
 +impl std::fmt::Display for SerializableSpan {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 +        write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line)
 +    }
 +}
 +
 +impl SerializableSpan {
 +    fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self {
 +        Self::from_span(cx, item.ident.span)
 +    }
 +
 +    fn from_span(cx: &LateContext<'_>, span: Span) -> Self {
 +        let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo());
 +
 +        Self {
 +            path: format!("{}", loc.file.name.prefer_remapped()),
 +            line: loc.line,
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
 +struct ApplicabilityInfo {
 +    /// Indicates if any of the lint emissions uses multiple spans. This is related to
 +    /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can
 +    /// currently not be applied automatically.
 +    is_multi_part_suggestion: bool,
 +    applicability: Option<usize>,
 +}
 +
 +impl Serialize for ApplicabilityInfo {
 +    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 +    where
 +        S: Serializer,
 +    {
 +        let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?;
 +        s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?;
 +        if let Some(index) = self.applicability {
 +            s.serialize_field(
 +                "applicability",
 +                &paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX],
 +            )?;
 +        } else {
 +            s.serialize_field("applicability", APPLICABILITY_UNRESOLVED_STR)?;
 +        }
 +        s.end()
 +    }
 +}
 +
 +// ==================================================================
 +// Configuration
 +// ==================================================================
 +#[derive(Debug, Clone, Default)]
 +pub struct ClippyConfiguration {
 +    name: String,
 +    config_type: &'static str,
 +    default: String,
 +    lints: Vec<String>,
 +    doc: String,
 +    deprecation_reason: Option<&'static str>,
 +}
 +
 +impl ClippyConfiguration {
 +    pub fn new(
 +        name: &'static str,
 +        config_type: &'static str,
 +        default: String,
 +        doc_comment: &'static str,
 +        deprecation_reason: Option<&'static str>,
 +    ) -> Self {
 +        let (lints, doc) = parse_config_field_doc(doc_comment)
 +            .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
 +
 +        Self {
 +            name: to_kebab(name),
 +            lints,
 +            doc,
 +            config_type,
 +            default,
 +            deprecation_reason,
 +        }
 +    }
 +}
 +
 +fn collect_configs() -> Vec<ClippyConfiguration> {
 +    crate::utils::conf::metadata::get_configuration_metadata()
 +}
 +
 +/// This parses the field documentation of the config struct.
 +///
 +/// ```rust, ignore
 +/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
 +/// ```
 +///
 +/// Would yield:
 +/// ```rust, ignore
 +/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
 +/// ```
 +fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
 +    const DOC_START: &str = " Lint: ";
 +    if_chain! {
 +        if doc_comment.starts_with(DOC_START);
 +        if let Some(split_pos) = doc_comment.find('.');
 +        then {
 +            let mut doc_comment = doc_comment.to_string();
 +            let mut documentation = doc_comment.split_off(split_pos);
 +
 +            // Extract lints
 +            doc_comment.make_ascii_lowercase();
 +            let lints: Vec<String> = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect();
 +
 +            // Format documentation correctly
 +            // split off leading `.` from lint name list and indent for correct formatting
 +            documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n    ");
 +
 +            Some((lints, documentation))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
 +fn to_kebab(config_name: &str) -> String {
 +    config_name.replace('_', "-")
 +}
 +
 +impl fmt::Display for ClippyConfiguration {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
 +        write!(
 +            f,
 +            CONFIGURATION_VALUE_TEMPLATE!(),
 +            name = self.name,
 +            ty = self.config_type,
 +            doc = self.doc,
 +            default = self.default
 +        )
 +    }
 +}
 +
 +// ==================================================================
 +// Lint pass
 +// ==================================================================
 +impl<'hir> LateLintPass<'hir> for MetadataCollector {
 +    /// Collecting lint declarations like:
 +    /// ```rust, ignore
 +    /// declare_clippy_lint! {
 +    ///     /// ### What it does
 +    ///     /// Something IDK.
 +    ///     pub SOME_LINT,
 +    ///     internal,
 +    ///     "Who am I?"
 +    /// }
 +    /// ```
 +    fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) {
 +        if let ItemKind::Static(ty, Mutability::Not, _) = item.kind {
 +            // Normal lint
 +            if_chain! {
 +                // item validation
 +                if is_lint_ref_type(cx, ty);
 +                // blacklist check
 +                let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
 +                if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
 +                // metadata extraction
 +                if let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item);
 +                if let Some(mut docs) = extract_attr_docs_or_lint(cx, item);
 +                then {
 +                    if let Some(configuration_section) = self.get_lint_configs(&lint_name) {
 +                        docs.push_str(&configuration_section);
 +                    }
 +
 +                    self.lints.push(LintMetadata::new(
 +                        lint_name,
 +                        SerializableSpan::from_item(cx, item),
 +                        group,
 +                        level,
 +                        docs,
 +                    ));
 +                }
 +            }
 +
 +            if_chain! {
 +                if is_deprecated_lint(cx, ty);
 +                // blacklist check
 +                let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
 +                if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
 +                // Metadata the little we can get from a deprecated lint
 +                if let Some(docs) = extract_attr_docs_or_lint(cx, item);
 +                then {
 +                    self.lints.push(LintMetadata::new(
 +                        lint_name,
 +                        SerializableSpan::from_item(cx, item),
 +                        DEPRECATED_LINT_GROUP_STR.to_string(),
 +                        DEPRECATED_LINT_LEVEL,
 +                        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 mut 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.drain(..) {
 +                let app_info = self.applicability_info.entry(lint_name).or_default();
 +                app_info.applicability = applicability;
 +                app_info.is_multi_part_suggestion = is_multi_part;
 +            }
 +        }
 +    }
 +}
 +
 +// ==================================================================
 +// Lint definition extraction
 +// ==================================================================
 +fn sym_to_string(sym: Symbol) -> String {
 +    sym.as_str().to_string()
 +}
 +
 +fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
 +    extract_attr_docs(cx, item).or_else(|| {
 +        lint_collection_error_item(cx, item, "could not collect the lint documentation");
 +        None
 +    })
 +}
 +
 +/// This function collects all documentation that has been added to an item using
 +/// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks
 +///
 +/// ```ignore
 +/// #[doc = r"Hello world!"]
 +/// #[doc = r"=^.^="]
 +/// struct SomeItem {}
 +/// ```
 +///
 +/// Would result in `Hello world!\n=^.^=\n`
 +///
 +/// ---
 +///
 +/// This function may modify the doc comment to ensure that the string can be displayed using a
 +/// markdown viewer in Clippy's lint list. The following modifications could be applied:
 +/// * Removal of leading space after a new line. (Important to display tables)
 +/// * Ensures that code blocks only contain language information
 +fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
 +    let attrs = cx.tcx.hir().attrs(item.hir_id());
 +    let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str);
 +    let mut docs = String::from(&*lines.next()?.as_str());
 +    let mut in_code_block = false;
 +    for line in lines {
 +        docs.push('\n');
 +        let line = line.as_str();
 +        let line = &*line;
 +        if let Some(info) = line.trim_start().strip_prefix("```") {
 +            in_code_block = !in_code_block;
 +            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);
 +                continue;
 +            }
 +        }
 +        // This removes the leading space that the macro translation introduces
 +        if let Some(stripped_doc) = line.strip_prefix(' ') {
 +            docs.push_str(stripped_doc);
 +        } else if !line.is_empty() {
 +            docs.push_str(line);
 +        }
 +    }
 +    Some(docs)
 +}
 +
 +fn get_lint_group_and_level_or_lint(
 +    cx: &LateContext<'_>,
 +    lint_name: &str,
 +    item: &'hir Item<'_>,
 +) -> Option<(String, &'static str)> {
 +    let result = cx
 +        .lint_store
 +        .check_lint_name(cx.sess(), lint_name, Some(sym::clippy), &[]);
 +    if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
 +        if let Some(group) = get_lint_group(cx, lint_lst[0]) {
 +            if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
 +                return None;
 +            }
 +
 +            if let Some(level) = get_lint_level_from_group(&group) {
 +                Some((group, level))
 +            } else {
 +                lint_collection_error_item(
 +                    cx,
 +                    item,
 +                    &format!("Unable to determine lint level for found group `{}`", group),
 +                );
 +                None
 +            }
 +        } else {
 +            lint_collection_error_item(cx, item, "Unable to determine lint group");
 +            None
 +        }
 +    } else {
 +        lint_collection_error_item(cx, item, "Unable to find lint in lint_store");
 +        None
 +    }
 +}
 +
 +fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
 +    for (group_name, lints, _) in &cx.lint_store.get_lint_groups() {
 +        if IGNORED_LINT_GROUPS.contains(group_name) {
 +            continue;
 +        }
 +
 +        if lints.iter().any(|group_lint| *group_lint == lint_id) {
 +            let group = group_name.strip_prefix(CLIPPY_LINT_GROUP_PREFIX).unwrap_or(group_name);
 +            return Some((*group).to_string());
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
 +    DEFAULT_LINT_LEVELS
 +        .iter()
 +        .find_map(|(group_name, group_level)| (*group_name == lint_group).then(|| *group_level))
 +}
 +
 +fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
 +    if let hir::TyKind::Path(ref path) = ty.kind {
 +        if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) {
 +            return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE);
 +        }
 +    }
 +
 +    false
 +}
 +
 +// ==================================================================
 +// Lint emission
 +// ==================================================================
 +fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) {
 +    span_lint(
 +        cx,
 +        INTERNAL_METADATA_COLLECTOR,
 +        item.ident.span,
 +        &format!("metadata collection error for `{}`: {}", item.ident.name, message),
 +    );
 +}
 +
 +// ==================================================================
 +// Applicability
 +// ==================================================================
 +/// This function checks if a given expression is equal to a simple lint emission function call.
 +/// It will return the function arguments if the emission matched any function.
 +fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) -> Option<&'hir [hir::Expr<'hir>]> {
 +    LINT_EMISSION_FUNCTIONS
 +        .iter()
 +        .find_map(|emission_fn| match_function_call(cx, expr, emission_fn))
 +}
 +
 +fn take_higher_applicability(a: Option<usize>, b: Option<usize>) -> Option<usize> {
 +    a.map_or(b, |a| a.max(b.unwrap_or_default()).into())
 +}
 +
 +fn extract_emission_info<'hir>(
 +    cx: &LateContext<'hir>,
 +    args: &'hir [hir::Expr<'hir>],
 +) -> Vec<(String, Option<usize>, bool)> {
 +    let mut lints = Vec::new();
 +    let mut applicability = None;
 +    let mut multi_part = false;
 +
 +    for arg in args {
 +        let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(arg));
 +
 +        if match_type(cx, arg_ty, &paths::LINT) {
 +            // If we found the lint arg, extract the lint name
 +            let mut resolved_lints = resolve_lints(cx, arg);
 +            lints.append(&mut resolved_lints);
 +        } else if match_type(cx, arg_ty, &paths::APPLICABILITY) {
 +            applicability = resolve_applicability(cx, arg);
 +        } else if arg_ty.is_closure() {
 +            multi_part |= check_is_multi_part(cx, arg);
 +            // TODO xFrednet 2021-03-01: don't use or_else but rather a comparison
 +            applicability = applicability.or_else(|| resolve_applicability(cx, arg));
 +        }
 +    }
 +
 +    lints
 +        .drain(..)
 +        .map(|lint_name| (lint_name, applicability, multi_part))
 +        .collect()
 +}
 +
 +/// Resolves the possible lints that this expression could reference
 +fn resolve_lints(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(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(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
 +    if let ExprKind::Closure(_, _, body_id, _, _) = closure_expr.kind {
 +        let mut scanner = IsMultiSpanScanner::new(cx);
 +        intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body_id));
 +        return scanner.is_multi_part();
 +    } else if let Some(local) = get_parent_local(cx, closure_expr) {
 +        if let Some(local_init) = local.init {
 +            return check_is_multi_part(cx, local_init);
 +        }
 +    }
 +
 +    false
 +}
 +
 +struct LintResolver<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    lints: Vec<String>,
 +}
 +
 +impl<'a, 'hir> LintResolver<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            lints: Vec::<String>::default(),
 +        }
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> {
 +    type Map = Map<'hir>;
 +
 +    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
 +        intravisit::NestedVisitorMap::All(self.cx.tcx.hir())
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        if_chain! {
 +            if let ExprKind::Path(qpath) = &expr.kind;
 +            if let QPath::Resolved(_, path) = qpath;
 +
 +            let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
 +            if match_type(self.cx, expr_ty, &paths::LINT);
 +            then {
 +                if let hir::def::Res::Def(DefKind::Static, _) = path.res {
 +                    let lint_name = last_path_segment(qpath).ident.name;
 +                    self.lints.push(sym_to_string(lint_name).to_ascii_lowercase());
 +                } else if let Some(local) = get_parent_local(self.cx, expr) {
 +                    if let Some(local_init) = local.init {
 +                        intravisit::walk_expr(self, local_init);
 +                    }
 +                }
 +            }
 +        }
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
 +
 +/// This visitor finds the highest applicability value in the visited expressions
 +struct ApplicabilityResolver<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    /// This is the index of hightest `Applicability` for `paths::APPLICABILITY_VALUES`
 +    applicability_index: Option<usize>,
 +}
 +
 +impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            applicability_index: None,
 +        }
 +    }
 +
 +    fn add_new_index(&mut self, new_index: usize) {
 +        self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index));
 +    }
 +
 +    fn complete(self) -> Option<usize> {
 +        self.applicability_index
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> {
 +    type Map = Map<'hir>;
 +
 +    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
 +        intravisit::NestedVisitorMap::All(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(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(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 spanns
 +    fn is_multi_part(&self) -> bool {
 +        self.suggestion_count > 1
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> {
 +    type Map = Map<'hir>;
 +
 +    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
 +        intravisit::NestedVisitorMap::All(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, _path_span, 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 e76d5f81c9640403a75295f93c55bdb7ba6c78fa,0000000000000000000000000000000000000000..d124d948b5e69d0b57210f5ffc1e588b786d6b41
mode 100644,000000..100644
--- /dev/null
@@@ -1,168 -1,0 +1,168 @@@
-             if let ExprKind::AddrOf(BorrowKind::Ref, mutability, ref addressee) = expr.kind;
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::is_copy;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::layout::LayoutOf;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +
 +#[allow(clippy::module_name_repetitions)]
 +#[derive(Copy, Clone)]
 +pub struct UselessVec {
 +    pub too_large_for_stack: u64,
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `&vec![..]` when using `&[..]` would
 +    /// be possible.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is less efficient.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn foo(my_vec: &[u8]) {}
 +    ///
 +    /// // Bad
 +    /// foo(&vec![1, 2]);
 +    ///
 +    /// // Good
 +    /// foo(&[1, 2]);
 +    /// ```
 +    pub USELESS_VEC,
 +    perf,
 +    "useless `vec!`"
 +}
 +
 +impl_lint_pass!(UselessVec => [USELESS_VEC]);
 +
 +impl<'tcx> LateLintPass<'tcx> for UselessVec {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        // search for `&vec![_]` expressions where the adjusted type is `&[_]`
 +        if_chain! {
 +            if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
 +            if let ty::Slice(..) = ty.kind();
++            if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind;
 +            if let Some(vec_args) = higher::VecArgs::hir(cx, addressee);
 +            then {
 +                self.check_vec_macro(cx, &vec_args, mutability, expr.span);
 +            }
 +        }
 +
 +        // search for `for _ in vec![…]`
 +        if_chain! {
 +            if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr);
 +            if let Some(vec_args) = higher::VecArgs::hir(cx, arg);
 +            if is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(arg)));
 +            then {
 +                // report the error around the `vec!` not inside `<std macros>:`
 +                let span = arg.span
 +                    .ctxt()
 +                    .outer_expn_data()
 +                    .call_site
 +                    .ctxt()
 +                    .outer_expn_data()
 +                    .call_site;
 +                self.check_vec_macro(cx, &vec_args, Mutability::Not, span);
 +            }
 +        }
 +    }
 +}
 +
 +impl UselessVec {
 +    fn check_vec_macro<'tcx>(
 +        self,
 +        cx: &LateContext<'tcx>,
 +        vec_args: &higher::VecArgs<'tcx>,
 +        mutability: Mutability,
 +        span: Span,
 +    ) {
 +        let mut applicability = Applicability::MachineApplicable;
 +        let snippet = match *vec_args {
 +            higher::VecArgs::Repeat(elem, len) => {
 +                if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) {
 +                    #[allow(clippy::cast_possible_truncation)]
 +                    if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack {
 +                        return;
 +                    }
 +
 +                    match mutability {
 +                        Mutability::Mut => {
 +                            format!(
 +                                "&mut [{}; {}]",
 +                                snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
 +                                snippet_with_applicability(cx, len.span, "len", &mut applicability)
 +                            )
 +                        },
 +                        Mutability::Not => {
 +                            format!(
 +                                "&[{}; {}]",
 +                                snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
 +                                snippet_with_applicability(cx, len.span, "len", &mut applicability)
 +                            )
 +                        },
 +                    }
 +                } else {
 +                    return;
 +                }
 +            },
 +            higher::VecArgs::Vec(args) => {
 +                if let Some(last) = args.iter().last() {
 +                    #[allow(clippy::cast_possible_truncation)]
 +                    if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack {
 +                        return;
 +                    }
 +                    let span = args[0].span.to(last.span);
 +
 +                    match mutability {
 +                        Mutability::Mut => {
 +                            format!(
 +                                "&mut [{}]",
 +                                snippet_with_applicability(cx, span, "..", &mut applicability)
 +                            )
 +                        },
 +                        Mutability::Not => {
 +                            format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability))
 +                        },
 +                    }
 +                } else {
 +                    match mutability {
 +                        Mutability::Mut => "&mut []".into(),
 +                        Mutability::Not => "&[]".into(),
 +                    }
 +                }
 +            },
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            USELESS_VEC,
 +            span,
 +            "useless use of `vec!`",
 +            "you can use a slice directly",
 +            snippet,
 +            applicability,
 +        );
 +    }
 +}
 +
 +fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 {
 +    let ty = cx.typeck_results().expr_ty_adjusted(expr);
 +    cx.layout_of(ty).map_or(0, |l| l.size.bytes())
 +}
 +
 +/// Returns the item type of the vector (i.e., the `T` in `Vec<T>`).
 +fn vec_type(ty: Ty<'_>) -> Ty<'_> {
 +    if let ty::Adt(_, substs) = ty.kind() {
 +        substs.type_at(0)
 +    } else {
 +        panic!("The type of `vec!` is a not a struct?");
 +    }
 +}
index c65b2958ec56d7f02673e6a74e141ba77c111e9a,0000000000000000000000000000000000000000..4c038a997952aba3ff2fd6a14384e17d077badba
mode 100644,000000..100644
--- /dev/null
@@@ -1,22 -1,0 +1,18 @@@
- version = "0.1.56"
 +[package]
 +name = "clippy_utils"
- itertools = "0.9"
- regex-syntax = "0.6"
- serde = { version = "1.0", features = ["derive"] }
- unicode-normalization = "0.1"
++version = "0.1.57"
 +edition = "2018"
 +publish = false
 +
 +[dependencies]
 +if_chain = "1.0.0"
 +rustc-semver="1.1.0"
 +
 +[features]
 +deny-warnings = []
 +internal-lints = []
 +metadata-collector-lint = []
 +
 +[package.metadata.rust-analyzer]
 +# This crate uses #[feature(rustc_private)]
 +rustc_private = true
index 71cfa196fc335f93d7b62e5d0234e08a08f33780,0000000000000000000000000000000000000000..9302e5c21faa4f7606d485726bcbf256aba1cfb8
mode 100644,000000..100644
--- /dev/null
@@@ -1,243 -1,0 +1,243 @@@
-                     format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap())
 +//! Clippy wrappers around rustc's diagnostic functions.
 +//!
 +//! These functions are used by the `INTERNAL_METADATA_COLLECTOR` lint to collect the corresponding
 +//! lint applicability. Please make sure that you update the `LINT_EMISSION_FUNCTIONS` variable in
 +//! `clippy_lints::utils::internal_lints::metadata_collector` when a new function is added
 +//! or renamed.
 +//!
 +//! Thank you!
 +//! ~The `INTERNAL_METADATA_COLLECTOR` lint
 +
 +use rustc_errors::{Applicability, DiagnosticBuilder};
 +use rustc_hir::HirId;
 +use rustc_lint::{LateContext, Lint, LintContext};
 +use rustc_span::source_map::{MultiSpan, Span};
 +use std::env;
 +
 +fn docs_link(diag: &mut DiagnosticBuilder<'_>, lint: &'static Lint) {
 +    if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() {
 +        if let Some(lint) = lint.name_lower().strip_prefix("clippy::") {
 +            diag.help(&format!(
 +                "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}",
 +                &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
 +                    // extract just major + minor version and ignore patch versions
++                    format!("rust-{}", n.rsplit_once('.').unwrap().1)
 +                }),
 +                lint
 +            ));
 +        }
 +    }
 +}
 +
 +/// Emit a basic lint message with a `msg` and a `span`.
 +///
 +/// This is the most primitive of our lint emission methods and can
 +/// be a good way to get a new lint started.
 +///
 +/// Usually it's nicer to provide more context for lint messages.
 +/// Be sure the output is understandable when you use this method.
 +///
 +/// # Example
 +///
 +/// ```ignore
 +/// error: usage of mem::forget on Drop type
 +///   --> $DIR/mem_forget.rs:17:5
 +///    |
 +/// 17 |     std::mem::forget(seven);
 +///    |     ^^^^^^^^^^^^^^^^^^^^^^^
 +/// ```
 +pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<MultiSpan>, msg: &str) {
 +    cx.struct_span_lint(lint, sp, |diag| {
 +        let mut diag = diag.build(msg);
 +        docs_link(&mut diag, lint);
 +        diag.emit();
 +    });
 +}
 +
 +/// Same as `span_lint` but with an extra `help` message.
 +///
 +/// Use this if you want to provide some general help but
 +/// can't provide a specific machine applicable suggestion.
 +///
 +/// The `help` message can be optionally attached to a `Span`.
 +///
 +/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
 +///
 +/// # Example
 +///
 +/// ```text
 +/// error: constant division of 0.0 with 0.0 will always result in NaN
 +///   --> $DIR/zero_div_zero.rs:6:25
 +///    |
 +/// 6  |     let other_f64_nan = 0.0f64 / 0.0;
 +///    |                         ^^^^^^^^^^^^
 +///    |
 +///    = help: Consider using `f64::NAN` if you would like a constant representing NaN
 +/// ```
 +pub fn span_lint_and_help<'a, T: LintContext>(
 +    cx: &'a T,
 +    lint: &'static Lint,
 +    span: Span,
 +    msg: &str,
 +    help_span: Option<Span>,
 +    help: &str,
 +) {
 +    cx.struct_span_lint(lint, span, |diag| {
 +        let mut diag = diag.build(msg);
 +        if let Some(help_span) = help_span {
 +            diag.span_help(help_span, help);
 +        } else {
 +            diag.help(help);
 +        }
 +        docs_link(&mut diag, lint);
 +        diag.emit();
 +    });
 +}
 +
 +/// Like `span_lint` but with a `note` section instead of a `help` message.
 +///
 +/// The `note` message is presented separately from the main lint message
 +/// and is attached to a specific span:
 +///
 +/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
 +///
 +/// # Example
 +///
 +/// ```text
 +/// error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
 +///   --> $DIR/drop_forget_ref.rs:10:5
 +///    |
 +/// 10 |     forget(&SomeStruct);
 +///    |     ^^^^^^^^^^^^^^^^^^^
 +///    |
 +///    = note: `-D clippy::forget-ref` implied by `-D warnings`
 +/// note: argument has type &SomeStruct
 +///   --> $DIR/drop_forget_ref.rs:10:12
 +///    |
 +/// 10 |     forget(&SomeStruct);
 +///    |            ^^^^^^^^^^^
 +/// ```
 +pub fn span_lint_and_note<'a, T: LintContext>(
 +    cx: &'a T,
 +    lint: &'static Lint,
 +    span: impl Into<MultiSpan>,
 +    msg: &str,
 +    note_span: Option<Span>,
 +    note: &str,
 +) {
 +    cx.struct_span_lint(lint, span, |diag| {
 +        let mut diag = diag.build(msg);
 +        if let Some(note_span) = note_span {
 +            diag.span_note(note_span, note);
 +        } else {
 +            diag.note(note);
 +        }
 +        docs_link(&mut diag, lint);
 +        diag.emit();
 +    });
 +}
 +
 +/// Like `span_lint` but allows to add notes, help and suggestions using a closure.
 +///
 +/// If you need to customize your lint output a lot, use this function.
 +/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
 +pub fn span_lint_and_then<C, S, F>(cx: &C, lint: &'static Lint, sp: S, msg: &str, f: F)
 +where
 +    C: LintContext,
 +    S: Into<MultiSpan>,
 +    F: FnOnce(&mut DiagnosticBuilder<'_>),
 +{
 +    cx.struct_span_lint(lint, sp, |diag| {
 +        let mut diag = diag.build(msg);
 +        f(&mut diag);
 +        docs_link(&mut diag, lint);
 +        diag.emit();
 +    });
 +}
 +
 +pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) {
 +    cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| {
 +        let mut diag = diag.build(msg);
 +        docs_link(&mut diag, lint);
 +        diag.emit();
 +    });
 +}
 +
 +pub fn span_lint_hir_and_then(
 +    cx: &LateContext<'_>,
 +    lint: &'static Lint,
 +    hir_id: HirId,
 +    sp: impl Into<MultiSpan>,
 +    msg: &str,
 +    f: impl FnOnce(&mut DiagnosticBuilder<'_>),
 +) {
 +    cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| {
 +        let mut diag = diag.build(msg);
 +        f(&mut diag);
 +        docs_link(&mut diag, lint);
 +        diag.emit();
 +    });
 +}
 +
 +/// Add a span lint with a suggestion on how to fix it.
 +///
 +/// These suggestions can be parsed by rustfix to allow it to automatically fix your code.
 +/// In the example below, `help` is `"try"` and `sugg` is the suggested replacement `".any(|x| x >
 +/// 2)"`.
 +///
 +/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
 +///
 +/// # Example
 +///
 +/// ```text
 +/// error: This `.fold` can be more succinctly expressed as `.any`
 +/// --> $DIR/methods.rs:390:13
 +///     |
 +/// 390 |     let _ = (0..3).fold(false, |acc, x| acc || x > 2);
 +///     |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
 +///     |
 +///     = note: `-D fold-any` implied by `-D warnings`
 +/// ```
 +#[cfg_attr(feature = "internal-lints", allow(clippy::collapsible_span_lint_calls))]
 +pub fn span_lint_and_sugg<'a, T: LintContext>(
 +    cx: &'a T,
 +    lint: &'static Lint,
 +    sp: Span,
 +    msg: &str,
 +    help: &str,
 +    sugg: String,
 +    applicability: Applicability,
 +) {
 +    span_lint_and_then(cx, lint, sp, msg, |diag| {
 +        diag.span_suggestion(sp, help, sugg, applicability);
 +    });
 +}
 +
 +/// Create a suggestion made from several `span → replacement`.
 +///
 +/// Note: in the JSON format (used by `compiletest_rs`), the help message will
 +/// appear once per
 +/// replacement. In human-readable format though, it only appears once before
 +/// the whole suggestion.
 +pub fn multispan_sugg<I>(diag: &mut DiagnosticBuilder<'_>, help_msg: &str, sugg: I)
 +where
 +    I: IntoIterator<Item = (Span, String)>,
 +{
 +    multispan_sugg_with_applicability(diag, help_msg, Applicability::Unspecified, sugg);
 +}
 +
 +/// Create a suggestion made from several `span → replacement`.
 +///
 +/// rustfix currently doesn't support the automatic application of suggestions with
 +/// multiple spans. This is tracked in issue [rustfix#141](https://github.com/rust-lang/rustfix/issues/141).
 +/// Suggestions with multiple spans will be silently ignored.
 +pub fn multispan_sugg_with_applicability<I>(
 +    diag: &mut DiagnosticBuilder<'_>,
 +    help_msg: &str,
 +    applicability: Applicability,
 +    sugg: I,
 +) where
 +    I: IntoIterator<Item = (Span, String)>,
 +{
 +    diag.multipart_suggestion(help_msg, sugg.into_iter().collect(), applicability);
 +}
index c06b894a73871e22b092b464ddb9c994d384cb17,0000000000000000000000000000000000000000..05a4a0143195080328d5d06788bb9d1bd75ffdc8
mode 100644,000000..100644
--- /dev/null
@@@ -1,555 -1,0 +1,610 @@@
- use rustc_hir::{Block, BorrowKind, Expr, ExprKind, LoopSource, Node, Pat, StmtKind, UnOp};
 +//! This module contains functions that retrieves specifiec elements.
 +
 +#![deny(clippy::missing_docs_in_private_items)]
 +
 +use crate::{is_expn_of, match_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_ast::ast::{self, LitKind};
 +use rustc_hir as hir;
-             if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind;
++use rustc_hir::{Arm, Block, BorrowKind, Expr, ExprKind, LoopSource, MatchSource, Node, Pat, StmtKind, UnOp};
 +use rustc_lint::LateContext;
 +use rustc_span::{sym, ExpnKind, Span, Symbol};
 +
 +/// The essential nodes of a desugared for loop as well as the entire span:
 +/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`.
 +pub struct ForLoop<'tcx> {
++    /// `for` loop item
 +    pub pat: &'tcx hir::Pat<'tcx>,
++    /// `IntoIterator` argument
 +    pub arg: &'tcx hir::Expr<'tcx>,
++    /// `for` loop body
 +    pub body: &'tcx hir::Expr<'tcx>,
++    /// entire `for` loop span
 +    pub span: Span,
 +}
 +
 +impl<'tcx> ForLoop<'tcx> {
 +    #[inline]
++    /// Parses a desugared `for` loop
 +    pub fn hir(expr: &Expr<'tcx>) -> Option<Self> {
 +        if_chain! {
-             if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind;
++            if let hir::ExprKind::Match(iterexpr, arms, hir::MatchSource::ForLoopDesugar) = expr.kind;
 +            if let Some(first_arm) = arms.get(0);
-             if let hir::ExprKind::Loop(ref block, ..) = first_arm.body.kind;
++            if let hir::ExprKind::Call(_, iterargs) = iterexpr.kind;
 +            if let Some(first_arg) = iterargs.get(0);
 +            if iterargs.len() == 1 && arms.len() == 1 && first_arm.guard.is_none();
-             if let hir::StmtKind::Local(ref local) = let_stmt.kind;
-             if let hir::StmtKind::Expr(ref body_expr) = body.kind;
++            if let hir::ExprKind::Loop(block, ..) = first_arm.body.kind;
 +            if block.expr.is_none();
 +            if let [ _, _, ref let_stmt, ref body ] = *block.stmts;
-     pub r#else: Option<&'hir Expr<'hir>>,
++            if let hir::StmtKind::Local(local) = let_stmt.kind;
++            if let hir::StmtKind::Expr(body_expr) = body.kind;
 +            then {
 +                return Some(Self {
 +                    pat: &*local.pat,
 +                    arg: first_arg,
 +                    body: body_expr,
 +                    span: first_arm.span
 +                });
 +            }
 +        }
 +        None
 +    }
 +}
 +
++/// An `if` expression without `DropTemps`
 +pub struct If<'hir> {
++    /// `if` condition
 +    pub cond: &'hir Expr<'hir>,
-             Some(Self { cond, r#else, then })
++    /// `if` then expression
 +    pub then: &'hir Expr<'hir>,
++    /// `else` expression
++    pub r#else: Option<&'hir Expr<'hir>>,
 +}
 +
 +impl<'hir> If<'hir> {
 +    #[inline]
++    /// Parses an `if` expression
 +    pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
 +        if let ExprKind::If(
 +            Expr {
 +                kind: ExprKind::DropTemps(cond),
 +                ..
 +            },
 +            then,
 +            r#else,
 +        ) = expr.kind
 +        {
-                 if let Some((_, Node::Expr(Expr { kind: ExprKind::Loop(_, _, LoopSource::While, _), .. }))) = iter.next() {
++            Some(Self { cond, then, r#else })
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
++/// An `if let` expression
 +pub struct IfLet<'hir> {
++    /// `if let` pattern
 +    pub let_pat: &'hir Pat<'hir>,
++    /// `if let` scrutinee
 +    pub let_expr: &'hir Expr<'hir>,
++    /// `if let` then expression
 +    pub if_then: &'hir Expr<'hir>,
++    /// `if let` else expression
 +    pub if_else: Option<&'hir Expr<'hir>>,
 +}
 +
 +impl<'hir> IfLet<'hir> {
++    /// Parses an `if let` expression
 +    pub fn hir(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
 +        if let ExprKind::If(
 +            Expr {
 +                kind: ExprKind::Let(let_pat, let_expr, _),
 +                ..
 +            },
 +            if_then,
 +            if_else,
 +        ) = expr.kind
 +        {
 +            let hir = cx.tcx.hir();
 +            let mut iter = hir.parent_iter(expr.hir_id);
 +            if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() {
-     pub r#else: Option<&'hir Expr<'hir>>,
++                if let Some((
++                    _,
++                    Node::Expr(Expr {
++                        kind: ExprKind::Loop(_, _, LoopSource::While, _),
++                        ..
++                    }),
++                )) = iter.next()
++                {
 +                    // while loop desugar
 +                    return None;
 +                }
 +            }
 +            return Some(Self {
 +                let_pat,
 +                let_expr,
 +                if_then,
 +                if_else,
 +            });
 +        }
 +        None
 +    }
 +}
 +
++/// An `if let` or `match` expression. Useful for lints that trigger on one or the other.
++pub enum IfLetOrMatch<'hir> {
++    /// Any `match` expression
++    Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource),
++    /// scrutinee, pattern, then block, else block
++    IfLet(
++        &'hir Expr<'hir>,
++        &'hir Pat<'hir>,
++        &'hir Expr<'hir>,
++        Option<&'hir Expr<'hir>>,
++    ),
++}
++
++impl<'hir> IfLetOrMatch<'hir> {
++    /// Parses an `if let` or `match` expression
++    pub fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
++        match expr.kind {
++            ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)),
++            _ => IfLet::hir(cx, expr).map(
++                |IfLet {
++                     let_expr,
++                     let_pat,
++                     if_then,
++                     if_else,
++                 }| { Self::IfLet(let_expr, let_pat, if_then, if_else) },
++            ),
++        }
++    }
++}
++
++/// An `if` or `if let` expression
 +pub struct IfOrIfLet<'hir> {
++    /// `if` condition that is maybe a `let` expression
 +    pub cond: &'hir Expr<'hir>,
-                 return Some(Self { cond, r#else, then });
++    /// `if` then expression
 +    pub then: &'hir Expr<'hir>,
++    /// `else` expression
++    pub r#else: Option<&'hir Expr<'hir>>,
 +}
 +
 +impl<'hir> IfOrIfLet<'hir> {
 +    #[inline]
++    /// Parses an `if` or `if let` expression
 +    pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
 +        if let ExprKind::If(cond, then, r#else) = expr.kind {
 +            if let ExprKind::DropTemps(new_cond) = cond.kind {
 +                return Some(Self {
 +                    cond: new_cond,
 +                    r#else,
 +                    then,
 +                });
 +            }
 +            if let ExprKind::Let(..) = cond.kind {
-             hir::ExprKind::Call(ref path, ref args)
++                return Some(Self { cond, then, r#else });
 +            }
 +        }
 +        None
 +    }
 +}
 +
 +/// Represent a range akin to `ast::ExprKind::Range`.
 +#[derive(Debug, Copy, Clone)]
 +pub struct Range<'a> {
 +    /// The lower bound of the range, or `None` for ranges such as `..X`.
 +    pub start: Option<&'a hir::Expr<'a>>,
 +    /// The upper bound of the range, or `None` for ranges such as `X..`.
 +    pub end: Option<&'a hir::Expr<'a>>,
 +    /// Whether the interval is open or closed.
 +    pub limits: ast::RangeLimits,
 +}
 +
 +impl<'a> Range<'a> {
 +    /// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
 +    pub fn hir(expr: &'a hir::Expr<'_>) -> Option<Range<'a>> {
 +        /// Finds the field named `name` in the field. Always return `Some` for
 +        /// convenience.
 +        fn get_field<'c>(name: &str, fields: &'c [hir::ExprField<'_>]) -> Option<&'c hir::Expr<'c>> {
 +            let expr = &fields.iter().find(|field| field.ident.name.as_str() == name)?.expr;
 +            Some(expr)
 +        }
 +
 +        match expr.kind {
-             hir::ExprKind::Struct(ref path, ref fields, None) => match path {
++            hir::ExprKind::Call(path, args)
 +                if matches!(
 +                    path.kind,
 +                    hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _))
 +                ) =>
 +            {
 +                Some(Range {
 +                    start: Some(&args[0]),
 +                    end: Some(&args[1]),
 +                    limits: ast::RangeLimits::Closed,
 +                })
 +            },
-             if let hir::ExprKind::Call(ref fun, ref args) = expr.kind;
++            hir::ExprKind::Struct(path, fields, None) => match &path {
 +                hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range {
 +                    start: None,
 +                    end: None,
 +                    limits: ast::RangeLimits::HalfOpen,
 +                }),
 +                hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range {
 +                    start: Some(get_field("start", fields)?),
 +                    end: None,
 +                    limits: ast::RangeLimits::HalfOpen,
 +                }),
 +                hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range {
 +                    start: Some(get_field("start", fields)?),
 +                    end: Some(get_field("end", fields)?),
 +                    limits: ast::RangeLimits::HalfOpen,
 +                }),
 +                hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range {
 +                    start: None,
 +                    end: Some(get_field("end", fields)?),
 +                    limits: ast::RangeLimits::Closed,
 +                }),
 +                hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range {
 +                    start: None,
 +                    end: Some(get_field("end", fields)?),
 +                    limits: ast::RangeLimits::HalfOpen,
 +                }),
 +                _ => None,
 +            },
 +            _ => None,
 +        }
 +    }
 +}
 +
 +/// Represent the pre-expansion arguments of a `vec!` invocation.
 +pub enum VecArgs<'a> {
 +    /// `vec![elem; len]`
 +    Repeat(&'a hir::Expr<'a>, &'a hir::Expr<'a>),
 +    /// `vec![a, b, c]`
 +    Vec(&'a [hir::Expr<'a>]),
 +}
 +
 +impl<'a> VecArgs<'a> {
 +    /// Returns the arguments of the `vec!` macro if this expression was expanded
 +    /// from `vec!`.
 +    pub fn hir(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option<VecArgs<'a>> {
 +        if_chain! {
-                         if let hir::ExprKind::Box(ref boxed) = args[0].kind;
-                         if let hir::ExprKind::Array(ref args) = boxed.kind;
++            if let hir::ExprKind::Call(fun, args) = expr.kind;
 +            if let hir::ExprKind::Path(ref qpath) = fun.kind;
 +            if is_expn_of(fun.span, "vec").is_some();
 +            if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
 +            then {
 +                return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 {
 +                    // `vec![elem; size]` case
 +                    Some(VecArgs::Repeat(&args[0], &args[1]))
 +                }
 +                else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 {
 +                    // `vec![a, b, c]` case
 +                    if_chain! {
-                             return Some(VecArgs::Vec(&*args));
++                        if let hir::ExprKind::Box(boxed) = args[0].kind;
++                        if let hir::ExprKind::Array(args) = boxed.kind;
 +                        then {
-     pub if_cond: &'hir Expr<'hir>,
-     pub if_then: &'hir Expr<'hir>,
-     pub if_else: Option<&'hir Expr<'hir>>,
++                            return Some(VecArgs::Vec(args));
 +                        }
 +                    }
 +
 +                    None
 +                }
 +                else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() {
 +                    Some(VecArgs::Vec(&[]))
 +                }
 +                else {
 +                    None
 +                };
 +            }
 +        }
 +
 +        None
 +    }
 +}
 +
++/// A desugared `while` loop
 +pub struct While<'hir> {
-                                     kind: ExprKind::DropTemps(if_cond),
++    /// `while` loop condition
++    pub condition: &'hir Expr<'hir>,
++    /// `while` loop body
++    pub body: &'hir Expr<'hir>,
 +}
 +
 +impl<'hir> While<'hir> {
 +    #[inline]
++    /// Parses a desugared `while` loop
 +    pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
 +        if let ExprKind::Loop(
 +            Block {
 +                expr:
 +                    Some(Expr {
 +                        kind:
 +                            ExprKind::If(
 +                                Expr {
-                                 if_then,
-                                 if_else_ref,
++                                    kind: ExprKind::DropTemps(condition),
 +                                    ..
 +                                },
-             let if_else = *if_else_ref;
-             return Some(Self {
-                 if_cond,
-                 if_then,
-                 if_else,
-             });
++                                body,
++                                _,
 +                            ),
 +                        ..
 +                    }),
 +                ..
 +            },
 +            _,
 +            LoopSource::While,
 +            _,
 +        ) = expr.kind
 +        {
-     pub if_expr: &'hir Expr<'hir>,
++            return Some(Self { condition, body });
 +        }
 +        None
 +    }
 +}
 +
++/// A desugared `while let` loop
 +pub struct WhileLet<'hir> {
-     pub if_else: Option<&'hir Expr<'hir>>,
++    /// `while let` loop item pattern
 +    pub let_pat: &'hir Pat<'hir>,
++    /// `while let` loop scrutinee
 +    pub let_expr: &'hir Expr<'hir>,
++    /// `while let` loop body
 +    pub if_then: &'hir Expr<'hir>,
-                 expr: Some(if_expr), ..
 +}
 +
 +impl<'hir> WhileLet<'hir> {
 +    #[inline]
++    /// Parses a desugared `while let` loop
 +    pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
 +        if let ExprKind::Loop(
 +            Block {
-             if let Expr {
-                 kind:
-                     ExprKind::If(
-                         Expr {
-                             kind: ExprKind::Let(let_pat, let_expr, _),
-                             ..
-                         },
-                         if_then,
-                         if_else_ref,
-                     ),
-                 ..
-             } = if_expr
-             {
-                 let if_else = *if_else_ref;
-                 return Some(Self {
-                     if_expr,
-                     let_pat,
-                     let_expr,
-                     if_then,
-                     if_else,
-                 });
-             }
++                expr:
++                    Some(Expr {
++                        kind:
++                            ExprKind::If(
++                                Expr {
++                                    kind: ExprKind::Let(let_pat, let_expr, _),
++                                    ..
++                                },
++                                if_then,
++                                _,
++                            ),
++                        ..
++                    }),
++                ..
 +            },
 +            _,
 +            LoopSource::While,
 +            _,
 +        ) = expr.kind
 +        {
-         if let Some(ref expr) = local.init;
++            return Some(Self {
++                let_pat,
++                let_expr,
++                if_then,
++            });
 +        }
 +        None
 +    }
 +}
 +
 +/// Converts a hir binary operator to the corresponding `ast` type.
 +#[must_use]
 +pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind {
 +    match op {
 +        hir::BinOpKind::Eq => ast::BinOpKind::Eq,
 +        hir::BinOpKind::Ge => ast::BinOpKind::Ge,
 +        hir::BinOpKind::Gt => ast::BinOpKind::Gt,
 +        hir::BinOpKind::Le => ast::BinOpKind::Le,
 +        hir::BinOpKind::Lt => ast::BinOpKind::Lt,
 +        hir::BinOpKind::Ne => ast::BinOpKind::Ne,
 +        hir::BinOpKind::Or => ast::BinOpKind::Or,
 +        hir::BinOpKind::Add => ast::BinOpKind::Add,
 +        hir::BinOpKind::And => ast::BinOpKind::And,
 +        hir::BinOpKind::BitAnd => ast::BinOpKind::BitAnd,
 +        hir::BinOpKind::BitOr => ast::BinOpKind::BitOr,
 +        hir::BinOpKind::BitXor => ast::BinOpKind::BitXor,
 +        hir::BinOpKind::Div => ast::BinOpKind::Div,
 +        hir::BinOpKind::Mul => ast::BinOpKind::Mul,
 +        hir::BinOpKind::Rem => ast::BinOpKind::Rem,
 +        hir::BinOpKind::Shl => ast::BinOpKind::Shl,
 +        hir::BinOpKind::Shr => ast::BinOpKind::Shr,
 +        hir::BinOpKind::Sub => ast::BinOpKind::Sub,
 +    }
 +}
 +
 +/// Extract args from an assert-like macro.
 +/// Currently working with:
 +/// - `assert!`, `assert_eq!` and `assert_ne!`
 +/// - `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!`
 +/// For example:
 +/// `assert!(expr)` will return `Some([expr])`
 +/// `debug_assert_eq!(a, b)` will return `Some([a, b])`
 +pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option<Vec<&'tcx Expr<'tcx>>> {
 +    /// Try to match the AST for a pattern that contains a match, for example when two args are
 +    /// compared
 +    fn ast_matchblock(matchblock_expr: &'tcx Expr<'tcx>) -> Option<Vec<&Expr<'_>>> {
 +        if_chain! {
 +            if let ExprKind::Match(headerexpr, _, _) = &matchblock_expr.kind;
 +            if let ExprKind::Tup([lhs, rhs]) = &headerexpr.kind;
 +            if let ExprKind::AddrOf(BorrowKind::Ref, _, lhs) = lhs.kind;
 +            if let ExprKind::AddrOf(BorrowKind::Ref, _, rhs) = rhs.kind;
 +            then {
 +                return Some(vec![lhs, rhs]);
 +            }
 +        }
 +        None
 +    }
 +
 +    if let ExprKind::Block(block, _) = e.kind {
 +        if block.stmts.len() == 1 {
 +            if let StmtKind::Semi(matchexpr) = block.stmts.get(0)?.kind {
 +                // macros with unique arg: `{debug_}assert!` (e.g., `debug_assert!(some_condition)`)
 +                if_chain! {
 +                    if let Some(If { cond, .. }) = If::hir(matchexpr);
 +                    if let ExprKind::Unary(UnOp::Not, condition) = cond.kind;
 +                    then {
 +                        return Some(vec![condition]);
 +                    }
 +                }
 +
 +                // debug macros with two args: `debug_assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
 +                if_chain! {
 +                    if let ExprKind::Block(matchblock,_) = matchexpr.kind;
 +                    if let Some(matchblock_expr) = matchblock.expr;
 +                    then {
 +                        return ast_matchblock(matchblock_expr);
 +                    }
 +                }
 +            }
 +        } else if let Some(matchblock_expr) = block.expr {
 +            // macros with two args: `assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
 +            return ast_matchblock(matchblock_expr);
 +        }
 +    }
 +    None
 +}
 +
 +/// A parsed `format!` expansion
 +pub struct FormatExpn<'tcx> {
 +    /// Span of `format!(..)`
 +    pub call_site: Span,
 +    /// Inner `format_args!` expansion
 +    pub format_args: FormatArgsExpn<'tcx>,
 +}
 +
 +impl FormatExpn<'tcx> {
 +    /// Parses an expanded `format!` invocation
 +    pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
 +        if_chain! {
 +            if let ExprKind::Block(block, _) = expr.kind;
 +            if let [stmt] = block.stmts;
 +            if let StmtKind::Local(local) = stmt.kind;
 +            if let Some(init) = local.init;
 +            if let ExprKind::Call(_, [format_args]) = init.kind;
 +            let expn_data = expr.span.ctxt().outer_expn_data();
 +            if let ExpnKind::Macro(_, sym::format) = expn_data.kind;
 +            if let Some(format_args) = FormatArgsExpn::parse(format_args);
 +            then {
 +                Some(FormatExpn {
 +                    call_site: expn_data.call_site,
 +                    format_args,
 +                })
 +            } else {
 +                None
 +            }
 +        }
 +    }
 +}
 +
 +/// A parsed `format_args!` expansion
 +pub struct FormatArgsExpn<'tcx> {
 +    /// Span of the first argument, the format string
 +    pub format_string_span: Span,
 +    /// Values passed after the format string
 +    pub value_args: Vec<&'tcx Expr<'tcx>>,
 +
 +    /// String literal expressions which represent the format string split by "{}"
 +    pub format_string_parts: &'tcx [Expr<'tcx>],
 +    /// Symbols corresponding to [`Self::format_string_parts`]
 +    pub format_string_symbols: Vec<Symbol>,
 +    /// Expressions like `ArgumentV1::new(arg0, Debug::fmt)`
 +    pub args: &'tcx [Expr<'tcx>],
 +    /// The final argument passed to `Arguments::new_v1_formatted`, if applicable
 +    pub fmt_expr: Option<&'tcx Expr<'tcx>>,
 +}
 +
 +impl FormatArgsExpn<'tcx> {
 +    /// Parses an expanded `format_args!` or `format_args_nl!` invocation
 +    pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
 +        if_chain! {
 +            if let ExpnKind::Macro(_, name) = expr.span.ctxt().outer_expn_data().kind;
 +            let name = name.as_str();
 +            if name.ends_with("format_args") || name.ends_with("format_args_nl");
 +
 +            if let ExprKind::Match(inner_match, [arm], _) = expr.kind;
 +
 +            // `match match`, if you will
 +            if let ExprKind::Match(args, [inner_arm], _) = inner_match.kind;
 +            if let ExprKind::Tup(value_args) = args.kind;
 +            if let Some(value_args) = value_args
 +                .iter()
 +                .map(|e| match e.kind {
 +                    ExprKind::AddrOf(_, _, e) => Some(e),
 +                    _ => None,
 +                })
 +                .collect();
 +            if let ExprKind::Array(args) = inner_arm.body.kind;
 +
 +            if let ExprKind::Block(Block { stmts: [], expr: Some(expr), .. }, _) = arm.body.kind;
 +            if let ExprKind::Call(_, call_args) = expr.kind;
 +            if let Some((strs_ref, fmt_expr)) = match call_args {
 +                // Arguments::new_v1
 +                [strs_ref, _] => Some((strs_ref, None)),
 +                // Arguments::new_v1_formatted
 +                [strs_ref, _, fmt_expr] => Some((strs_ref, Some(fmt_expr))),
 +                _ => None,
 +            };
 +            if let ExprKind::AddrOf(BorrowKind::Ref, _, strs_arr) = strs_ref.kind;
 +            if let ExprKind::Array(format_string_parts) = strs_arr.kind;
 +            if let Some(format_string_symbols) = format_string_parts
 +                .iter()
 +                .map(|e| {
 +                    if let ExprKind::Lit(lit) = &e.kind {
 +                        if let LitKind::Str(symbol, _style) = lit.node {
 +                            return Some(symbol);
 +                        }
 +                    }
 +                    None
 +                })
 +                .collect();
 +            then {
 +                Some(FormatArgsExpn {
 +                    format_string_span: strs_ref.span,
 +                    value_args,
 +                    format_string_parts,
 +                    format_string_symbols,
 +                    args,
 +                    fmt_expr,
 +                })
 +            } else {
 +                None
 +            }
 +        }
 +    }
 +}
 +
 +/// Checks if a `let` statement is from a `for` loop desugaring.
 +pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool {
 +    // This will detect plain for-loops without an actual variable binding:
 +    //
 +    // ```
 +    // for x in some_vec {
 +    //     // do stuff
 +    // }
 +    // ```
 +    if_chain! {
++        if let Some(expr) = local.init;
 +        if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind;
 +        then {
 +            return true;
 +        }
 +    }
 +
 +    // This detects a variable binding in for loop to avoid `let_unit_value`
 +    // lint (see issue #1964).
 +    //
 +    // ```
 +    // for _ in vec![()] {
 +    //     // anything
 +    // }
 +    // ```
 +    if let hir::LocalSource::ForLoopDesugar = local.source {
 +        return true;
 +    }
 +
 +    false
 +}
index a44f2df2fd631e9793dd022cc50517a48caa7e07,0000000000000000000000000000000000000000..6e9a1de21eef5cb010d32f668a82565ba1713a35
mode 100644,000000..100644
--- /dev/null
@@@ -1,968 -1,0 +1,966 @@@
-             (&ExprKind::Let(ref lp, ref le, _), &ExprKind::Let(ref rp, ref re, _)) => {
-                 self.eq_pat(lp, rp) && self.eq_expr(le, re)
-             },
 +use crate::consts::{constant_context, constant_simple};
 +use crate::differing_macro_contexts;
 +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::{
 +    BinOpKind, Block, BodyId, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId,
 +    InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, PathSegment, QPath, Stmt,
 +    StmtKind, Ty, TyKind, TypeBinding,
 +};
 +use rustc_lexer::{tokenize, TokenKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::TypeckResults;
 +use rustc_span::Symbol;
 +use std::hash::{Hash, Hasher};
 +
 +/// Type used to check whether two ast are the same. This is different from the
 +/// operator
 +/// `==` on ast types as this operator would compare true equality with ID and
 +/// span.
 +///
 +/// 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>>,
 +    allow_side_effects: bool,
 +    expr_fallback: Option<Box<dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a>>,
 +}
 +
 +impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
 +    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            cx,
 +            maybe_typeck_results: cx.maybe_typeck_results(),
 +            allow_side_effects: true,
 +            expr_fallback: None,
 +        }
 +    }
 +
 +    /// Consider expressions containing potential side effects as not equal.
 +    pub fn deny_side_effects(self) -> Self {
 +        Self {
 +            allow_side_effects: false,
 +            ..self
 +        }
 +    }
 +
 +    pub fn expr_fallback(self, expr_fallback: impl FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self {
 +        Self {
 +            expr_fallback: Some(Box::new(expr_fallback)),
 +            ..self
 +        }
 +    }
 +
 +    /// Use this method to wrap comparisons that may involve inter-expression context.
 +    /// See `self.locals`.
 +    pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> {
 +        HirEqInterExpr {
 +            inner: self,
 +            locals: HirIdMap::default(),
 +        }
 +    }
 +
 +    #[allow(dead_code)]
 +    pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
 +        self.inter_expr().eq_block(left, right)
 +    }
 +
 +    pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +        self.inter_expr().eq_expr(left, right)
 +    }
 +
 +    pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
 +        self.inter_expr().eq_path_segment(left, right)
 +    }
 +
 +    pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
 +        self.inter_expr().eq_path_segments(left, right)
 +    }
 +}
 +
 +pub struct HirEqInterExpr<'a, 'b, 'tcx> {
 +    inner: &'a mut SpanlessEq<'b, 'tcx>,
 +
 +    // When binding are declared, the binding ID in the left expression is mapped to the one on the
 +    // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`,
 +    // these blocks are considered equal since `x` is mapped to `y`.
 +    locals: HirIdMap<HirId>,
 +}
 +
 +impl HirEqInterExpr<'_, '_, '_> {
 +    pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
 +        match (&left.kind, &right.kind) {
 +            (&StmtKind::Local(l), &StmtKind::Local(r)) => {
 +                // This additional check ensures that the type of the locals are equivalent even if the init
 +                // expression or type have some inferred parts.
 +                if let Some(typeck) = self.inner.maybe_typeck_results {
 +                    let l_ty = typeck.pat_ty(l.pat);
 +                    let r_ty = typeck.pat_ty(r.pat);
 +                    if !rustc_middle::ty::TyS::same_type(l_ty, r_ty) {
 +                        return false;
 +                    }
 +                }
 +
 +                // eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that
 +                // these only get added if the init and type is equal.
 +                both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
 +                    && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r))
 +                    && self.eq_pat(l.pat, r.pat)
 +            },
 +            (&StmtKind::Expr(l), &StmtKind::Expr(r)) | (&StmtKind::Semi(l), &StmtKind::Semi(r)) => self.eq_expr(l, r),
 +            _ => false,
 +        }
 +    }
 +
 +    /// Checks whether two blocks are the same.
 +    fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
 +        match (left.stmts, left.expr, right.stmts, right.expr) {
 +            ([], None, [], None) => {
 +                // For empty blocks, check to see if the tokens are equal. This will catch the case where a macro
 +                // expanded to nothing, or the cfg attribute was used.
 +                let (left, right) = match (
 +                    snippet_opt(self.inner.cx, left.span),
 +                    snippet_opt(self.inner.cx, right.span),
 +                ) {
 +                    (Some(left), Some(right)) => (left, right),
 +                    _ => return true,
 +                };
 +                let mut left_pos = 0;
 +                let left = tokenize(&left)
 +                    .map(|t| {
 +                        let end = left_pos + t.len;
 +                        let s = &left[left_pos..end];
 +                        left_pos = end;
 +                        (t, s)
 +                    })
 +                    .filter(|(t, _)| {
 +                        !matches!(
 +                            t.kind,
 +                            TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
 +                        )
 +                    })
 +                    .map(|(_, s)| s);
 +                let mut right_pos = 0;
 +                let right = tokenize(&right)
 +                    .map(|t| {
 +                        let end = right_pos + t.len;
 +                        let s = &right[right_pos..end];
 +                        right_pos = end;
 +                        (t, s)
 +                    })
 +                    .filter(|(t, _)| {
 +                        !matches!(
 +                            t.kind,
 +                            TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
 +                        )
 +                    })
 +                    .map(|(_, s)| s);
 +                left.eq(right)
 +            },
 +            _ => {
 +                over(left.stmts, right.stmts, |l, r| self.eq_stmt(l, r))
 +                    && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r))
 +            },
 +        }
 +    }
 +
 +    pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool {
 +        let cx = self.inner.cx;
 +        let eval_const = |body| constant_context(cx, cx.tcx.typeck_body(body)).expr(&cx.tcx.hir().body(body).value);
 +        eval_const(left) == eval_const(right)
 +    }
 +
 +    #[allow(clippy::similar_names)]
 +    pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +        if !self.inner.allow_side_effects && differing_macro_contexts(left.span, right.span) {
 +            return false;
 +        }
 +
 +        if let Some(typeck_results) = self.inner.maybe_typeck_results {
 +            if let (Some(l), Some(r)) = (
 +                constant_simple(self.inner.cx, typeck_results, left),
 +                constant_simple(self.inner.cx, typeck_results, right),
 +            ) {
 +                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(ref pat, ref expr, _) => {
++            (&ExprKind::Let(lp, le, _), &ExprKind::Let(rp, re, _)) => self.eq_pat(lp, rp) && self.eq_expr(le, re),
 +            (&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, ref ll_id), &ExprKind::Repeat(re, ref rl_id)) => {
 +                self.eq_expr(le, re) && self.eq_body(ll_id.body, rl_id.body)
 +            },
 +            (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)),
 +            (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r),
 +            (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => {
 +                self.eq_qpath(l_path, r_path)
 +                    && both(lo, ro, |l, r| self.eq_expr(l, r))
 +                    && over(lf, rf, |l, r| self.eq_expr_field(l, r))
 +            },
 +            (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
 +            (&ExprKind::Unary(l_op, le), &ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re),
 +            (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
 +            (&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re),
 +            _ => false,
 +        };
 +        is_eq || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right))
 +    }
 +
 +    fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
 +        over(left, right, |l, r| self.eq_expr(l, r))
 +    }
 +
 +    fn eq_expr_field(&mut self, left: &ExprField<'_>, right: &ExprField<'_>) -> bool {
 +        left.ident.name == right.ident.name && self.eq_expr(left.expr, right.expr)
 +    }
 +
 +    fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool {
 +        match (left, right) {
 +            (Guard::If(l), Guard::If(r)) => self.eq_expr(l, r),
 +            (Guard::IfLet(lp, le), Guard::IfLet(rp, re)) => self.eq_pat(lp, rp) && self.eq_expr(le, re),
 +            _ => false,
 +        }
 +    }
 +
 +    fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool {
 +        match (left, right) {
 +            (GenericArg::Const(l), GenericArg::Const(r)) => self.eq_body(l.value.body, r.value.body),
 +            (GenericArg::Lifetime(l_lt), GenericArg::Lifetime(r_lt)) => Self::eq_lifetime(l_lt, r_lt),
 +            (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty, r_ty),
 +            (GenericArg::Infer(l_inf), GenericArg::Infer(r_inf)) => self.eq_ty(&l_inf.to_ty(), &r_inf.to_ty()),
 +            _ => false,
 +        }
 +    }
 +
 +    fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool {
 +        left.name == right.name
 +    }
 +
 +    fn eq_pat_field(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool {
 +        let (PatField { ident: li, pat: lp, .. }, PatField { ident: ri, pat: rp, .. }) = (&left, &right);
 +        li.name == ri.name && self.eq_pat(lp, rp)
 +    }
 +
 +    /// Checks whether two patterns are the same.
 +    fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
 +        match (&left.kind, &right.kind) {
 +            (&PatKind::Box(l), &PatKind::Box(r)) => self.eq_pat(l, r),
 +            (&PatKind::Struct(ref lp, la, ..), &PatKind::Struct(ref rp, ra, ..)) => {
 +                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r))
 +            },
 +            (&PatKind::TupleStruct(ref lp, la, ls), &PatKind::TupleStruct(ref rp, ra, rs)) => {
 +                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
 +            },
 +            (&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => {
 +                let eq = lb == rb && both(lp, rp, |l, r| self.eq_pat(l, r));
 +                if eq {
 +                    self.locals.insert(li, ri);
 +                }
 +                eq
 +            },
 +            (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r),
 +            (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r),
 +            (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)),
 +            (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => {
 +                both(ls, rs, |a, b| self.eq_expr(a, b)) && both(le, re, |a, b| self.eq_expr(a, b)) && (li == ri)
 +            },
 +            (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re),
 +            (&PatKind::Slice(ls, ref li, le), &PatKind::Slice(rs, ref ri, re)) => {
 +                over(ls, rs, |l, r| self.eq_pat(l, r))
 +                    && over(le, re, |l, r| self.eq_pat(l, r))
 +                    && both(li, ri, |l, r| self.eq_pat(l, r))
 +            },
 +            (&PatKind::Wild, &PatKind::Wild) => true,
 +            _ => false,
 +        }
 +    }
 +
 +    #[allow(clippy::similar_names)]
 +    fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool {
 +        match (left, right) {
 +            (&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => {
 +                both(lty, rty, |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath)
 +            },
 +            (&QPath::TypeRelative(lty, lseg), &QPath::TypeRelative(rty, rseg)) => {
 +                self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
 +            },
 +            (&QPath::LangItem(llang_item, _), &QPath::LangItem(rlang_item, _)) => llang_item == rlang_item,
 +            _ => false,
 +        }
 +    }
 +
 +    fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
 +        match (left.res, right.res) {
 +            (Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r),
 +            (Res::Local(_), _) | (_, Res::Local(_)) => false,
 +            _ => over(left.segments, right.segments, |l, r| self.eq_path_segment(l, r)),
 +        }
 +    }
 +
 +    fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool {
 +        if !(left.parenthesized || right.parenthesized) {
 +            over(left.args, right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work
 +                && over(left.bindings, right.bindings, |l, r| self.eq_type_binding(l, r))
 +        } else if left.parenthesized && right.parenthesized {
 +            over(left.inputs(), right.inputs(), |l, r| self.eq_ty(l, r))
 +                && both(&Some(&left.bindings[0].ty()), &Some(&right.bindings[0].ty()), |l, r| {
 +                    self.eq_ty(l, r)
 +                })
 +        } else {
 +            false
 +        }
 +    }
 +
 +    pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
 +        left.len() == right.len() && left.iter().zip(right).all(|(l, r)| self.eq_path_segment(l, r))
 +    }
 +
 +    pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
 +        // The == of idents doesn't work with different contexts,
 +        // we have to be explicit about hygiene
 +        left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
 +    }
 +
 +    #[allow(clippy::similar_names)]
 +    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, ref ll_id), &TyKind::Array(rt, ref rl_id)) => {
 +                self.eq_ty(lt, rt) && self.eq_body(ll_id.body, rl_id.body)
 +            },
 +            (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => {
 +                l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty)
 +            },
 +            (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => {
 +                l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(&*l_rmut.ty, &*r_rmut.ty)
 +            },
 +            (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r),
 +            (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)),
 +            (&TyKind::Infer, &TyKind::Infer) => true,
 +            _ => false,
 +        }
 +    }
 +
 +    fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool {
 +        left.ident.name == right.ident.name && self.eq_ty(left.ty(), right.ty())
 +    }
 +}
 +
 +/// Some simple reductions like `{ return }` => `return`
 +fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'hir ExprKind<'hir> {
 +    if let ExprKind::Block(block, _) = kind {
 +        match (block.stmts, block.expr) {
 +            // From an `if let` expression without an `else` block. The arm for the implicit wild pattern is an empty
 +            // block with an empty span.
 +            ([], None) if block.span.is_empty() => &ExprKind::Tup(&[]),
 +            // `{}` => `()`
 +            ([], None) => match snippet_opt(cx, block.span) {
 +                // Don't reduce if there are any tokens contained in the braces
 +                Some(snip)
 +                    if tokenize(&snip)
 +                        .map(|t| t.kind)
 +                        .filter(|t| {
 +                            !matches!(
 +                                t,
 +                                TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
 +                            )
 +                        })
 +                        .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) =>
 +                {
 +                    kind
 +                },
 +                _ => &ExprKind::Tup(&[]),
 +            },
 +            ([], Some(expr)) => match expr.kind {
 +                // `{ return .. }` => `return ..`
 +                ExprKind::Ret(..) => &expr.kind,
 +                _ => kind,
 +            },
 +            ([stmt], None) => match stmt.kind {
 +                StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind {
 +                    // `{ return ..; }` => `return ..`
 +                    ExprKind::Ret(..) => &expr.kind,
 +                    _ => kind,
 +                },
 +                _ => kind,
 +            },
 +            _ => kind,
 +        }
 +    } else {
 +        kind
 +    }
 +}
 +
 +fn swap_binop<'a>(
 +    binop: BinOpKind,
 +    lhs: &'a Expr<'a>,
 +    rhs: &'a Expr<'a>,
 +) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> {
 +    match binop {
 +        BinOpKind::Add | BinOpKind::Eq | BinOpKind::Ne | BinOpKind::BitAnd | BinOpKind::BitXor | BinOpKind::BitOr => {
 +            Some((binop, rhs, lhs))
 +        },
 +        BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)),
 +        BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)),
 +        BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)),
 +        BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)),
 +        BinOpKind::Mul // Not always commutative, e.g. with matrices. See issue #5698
 +        | BinOpKind::Shl
 +        | BinOpKind::Shr
 +        | BinOpKind::Rem
 +        | BinOpKind::Sub
 +        | BinOpKind::Div
 +        | BinOpKind::And
 +        | BinOpKind::Or => None,
 +    }
 +}
 +
 +/// Checks if the two `Option`s are both `None` or some equal values as per
 +/// `eq_fn`.
 +pub fn both<X>(l: &Option<X>, r: &Option<X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
 +    l.as_ref()
 +        .map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y)))
 +}
 +
 +/// Checks if two slices are equal as per `eq_fn`.
 +pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
 +    left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
 +}
 +
 +/// Counts how many elements of the slices are equal as per `eq_fn`.
 +pub fn count_eq<X: Sized>(
 +    left: &mut dyn Iterator<Item = X>,
 +    right: &mut dyn Iterator<Item = X>,
 +    mut eq_fn: impl FnMut(&X, &X) -> bool,
 +) -> usize {
 +    left.zip(right).take_while(|(l, r)| eq_fn(l, r)).count()
 +}
 +
 +/// Checks if two expressions evaluate to the same value, and don't contain any side effects.
 +pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +    SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right)
 +}
 +
 +/// Type used to hash an ast element. This is different from the `Hash` trait
 +/// on ast types as this
 +/// trait would consider IDs and spans.
 +///
 +/// All expressions kind are hashed, but some might have a weaker hash.
 +pub struct SpanlessHash<'a, 'tcx> {
 +    /// Context used to evaluate constant expressions.
 +    cx: &'a LateContext<'tcx>,
 +    maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
 +    s: FxHasher,
 +}
 +
 +impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
 +    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            cx,
 +            maybe_typeck_results: cx.maybe_typeck_results(),
 +            s: FxHasher::default(),
 +        }
 +    }
 +
 +    pub fn finish(self) -> u64 {
 +        self.s.finish()
 +    }
 +
 +    pub fn hash_block(&mut self, b: &Block<'_>) {
 +        for s in b.stmts {
 +            self.hash_stmt(s);
 +        }
 +
 +        if let Some(e) = b.expr {
 +            self.hash_expr(e);
 +        }
 +
 +        std::mem::discriminant(&b.rules).hash(&mut self.s);
 +    }
 +
 +    #[allow(clippy::many_single_char_names, clippy::too_many_lines)]
 +    pub fn hash_expr(&mut self, e: &Expr<'_>) {
 +        let simple_const = self
 +            .maybe_typeck_results
 +            .and_then(|typeck_results| constant_simple(self.cx, typeck_results, e));
 +
 +        // const hashing may result in the same hash as some unrelated node, so add a sort of
 +        // discriminant depending on which path we're choosing next
 +        simple_const.hash(&mut self.s);
 +        if simple_const.is_some() {
 +            return;
 +        }
 +
 +        std::mem::discriminant(&e.kind).hash(&mut self.s);
 +
 +        match e.kind {
 +            ExprKind::AddrOf(kind, m, e) => {
 +                std::mem::discriminant(&kind).hash(&mut self.s);
 +                m.hash(&mut self.s);
 +                self.hash_expr(e);
 +            },
 +            ExprKind::Continue(i) => {
 +                if let Some(i) = i.label {
 +                    self.hash_name(i.ident.name);
 +                }
 +            },
 +            ExprKind::Assign(l, r, _) => {
 +                self.hash_expr(l);
 +                self.hash_expr(r);
 +            },
 +            ExprKind::AssignOp(ref o, l, r) => {
 +                std::mem::discriminant(&o.node).hash(&mut self.s);
 +                self.hash_expr(l);
 +                self.hash_expr(r);
 +            },
 +            ExprKind::Block(b, _) => {
 +                self.hash_block(b);
 +            },
 +            ExprKind::Binary(op, l, r) => {
 +                std::mem::discriminant(&op.node).hash(&mut self.s);
 +                self.hash_expr(l);
 +                self.hash_expr(r);
 +            },
 +            ExprKind::Break(i, ref j) => {
 +                if let Some(i) = i.label {
 +                    self.hash_name(i.ident.name);
 +                }
 +                if let Some(j) = *j {
 +                    self.hash_expr(&*j);
 +                }
 +            },
 +            ExprKind::Box(e) | ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => {
 +                self.hash_expr(e);
 +            },
 +            ExprKind::Call(fun, args) => {
 +                self.hash_expr(fun);
 +                self.hash_exprs(args);
 +            },
 +            ExprKind::Cast(e, ty) | ExprKind::Type(e, ty) => {
 +                self.hash_expr(e);
 +                self.hash_ty(ty);
 +            },
 +            ExprKind::Closure(cap, _, eid, _, _) => {
 +                std::mem::discriminant(&cap).hash(&mut self.s);
 +                // closures inherit TypeckResults
 +                self.hash_expr(&self.cx.tcx.hir().body(eid).value);
 +            },
 +            ExprKind::Field(e, ref f) => {
 +                self.hash_expr(e);
 +                self.hash_name(f.name);
 +            },
 +            ExprKind::Index(a, i) => {
 +                self.hash_expr(a);
 +                self.hash_expr(i);
 +            },
 +            ExprKind::InlineAsm(asm) => {
 +                for piece in asm.template {
 +                    match piece {
 +                        InlineAsmTemplatePiece::String(s) => s.hash(&mut self.s),
 +                        InlineAsmTemplatePiece::Placeholder {
 +                            operand_idx,
 +                            modifier,
 +                            span: _,
 +                        } => {
 +                            operand_idx.hash(&mut self.s);
 +                            modifier.hash(&mut self.s);
 +                        },
 +                    }
 +                }
 +                asm.options.hash(&mut self.s);
 +                for (op, _op_sp) in asm.operands {
 +                    match op {
 +                        InlineAsmOperand::In { reg, expr } => {
 +                            reg.hash(&mut self.s);
 +                            self.hash_expr(expr);
 +                        },
 +                        InlineAsmOperand::Out { reg, late, expr } => {
 +                            reg.hash(&mut self.s);
 +                            late.hash(&mut self.s);
 +                            if let Some(expr) = expr {
 +                                self.hash_expr(expr);
 +                            }
 +                        },
 +                        InlineAsmOperand::InOut { reg, late, expr } => {
 +                            reg.hash(&mut self.s);
 +                            late.hash(&mut self.s);
 +                            self.hash_expr(expr);
 +                        },
 +                        InlineAsmOperand::SplitInOut {
 +                            reg,
 +                            late,
 +                            in_expr,
 +                            out_expr,
 +                        } => {
 +                            reg.hash(&mut self.s);
 +                            late.hash(&mut self.s);
 +                            self.hash_expr(in_expr);
 +                            if let Some(out_expr) = out_expr {
 +                                self.hash_expr(out_expr);
 +                            }
 +                        },
 +                        InlineAsmOperand::Const { anon_const } => self.hash_body(anon_const.body),
 +                        InlineAsmOperand::Sym { expr } => self.hash_expr(expr),
 +                    }
 +                }
 +            },
++            ExprKind::Let(pat, expr, _) => {
 +                self.hash_expr(expr);
 +                self.hash_pat(pat);
 +            },
 +            ExprKind::LlvmInlineAsm(..) | 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, ref _tys, 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, ref l_id) => {
 +                self.hash_expr(e);
 +                self.hash_body(l_id.body);
 +            },
 +            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
 +            Res::Local(_) => 1_usize.hash(&mut self.s),
 +            _ => {
 +                for seg in path.segments {
 +                    self.hash_name(seg.ident.name);
 +                    self.hash_generic_args(seg.args().args);
 +                }
 +            },
 +        }
 +    }
 +
 +    pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
 +        std::mem::discriminant(&b.kind).hash(&mut self.s);
 +
 +        match &b.kind {
 +            StmtKind::Local(local) => {
 +                self.hash_pat(local.pat);
 +                if let Some(init) = local.init {
 +                    self.hash_expr(init);
 +                }
 +            },
 +            StmtKind::Item(..) => {},
 +            StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
 +                self.hash_expr(expr);
 +            },
 +        }
 +    }
 +
 +    pub fn hash_guard(&mut self, g: &Guard<'_>) {
 +        match g {
 +            Guard::If(expr) | Guard::IfLet(_, expr) => {
 +                self.hash_expr(expr);
 +            },
 +        }
 +    }
 +
 +    pub fn hash_lifetime(&mut self, lifetime: Lifetime) {
 +        std::mem::discriminant(&lifetime.name).hash(&mut self.s);
 +        if let LifetimeName::Param(ref name) = lifetime.name {
 +            std::mem::discriminant(name).hash(&mut self.s);
 +            match name {
 +                ParamName::Plain(ref ident) => {
 +                    ident.name.hash(&mut self.s);
 +                },
 +                ParamName::Fresh(ref size) => {
 +                    size.hash(&mut self.s);
 +                },
 +                ParamName::Error => {},
 +            }
 +        }
 +    }
 +
 +    pub fn hash_ty(&mut self, ty: &Ty<'_>) {
 +        std::mem::discriminant(&ty.kind).hash(&mut self.s);
 +        self.hash_tykind(&ty.kind);
 +    }
 +
 +    pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
 +        match ty {
 +            TyKind::Slice(ty) => {
 +                self.hash_ty(ty);
 +            },
 +            TyKind::Array(ty, anon_const) => {
 +                self.hash_ty(ty);
 +                self.hash_body(anon_const.body);
 +            },
 +            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_body(&mut self, body_id: BodyId) {
 +        // swap out TypeckResults when hashing a body
 +        let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body_id));
 +        self.hash_expr(&self.cx.tcx.hir().body(body_id).value);
 +        self.maybe_typeck_results = old_maybe_typeck_results;
 +    }
 +
 +    fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) {
 +        for arg in arg_list {
 +            match *arg {
 +                GenericArg::Lifetime(l) => self.hash_lifetime(l),
 +                GenericArg::Type(ref ty) => self.hash_ty(ty),
 +                GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
 +                GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()),
 +            }
 +        }
 +    }
 +}
index ddff1686ba2ce99f9ac3f22de1abe04cd50997f8,0000000000000000000000000000000000000000..757485d19d25ad6c01fa9cc9b93e80d129cf54de
mode 100644,000000..100644
--- /dev/null
@@@ -1,1818 -1,0 +1,2115 @@@
- use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind};
 +#![feature(box_patterns)]
 +#![feature(in_band_lifetimes)]
 +#![feature(iter_zip)]
 +#![feature(rustc_private)]
++#![feature(control_flow_enum)]
 +#![recursion_limit = "512"]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
 +// warn on the same lints as `clippy_lints`
 +#![warn(trivial_casts, trivial_numeric_casts)]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_ast;
 +extern crate rustc_ast_pretty;
 +extern crate rustc_attr;
 +extern crate rustc_data_structures;
 +extern crate rustc_errors;
 +extern crate rustc_hir;
 +extern crate rustc_infer;
 +extern crate rustc_lexer;
 +extern crate rustc_lint;
 +extern crate rustc_middle;
 +extern crate rustc_mir;
 +extern crate rustc_session;
 +extern crate rustc_span;
 +extern crate rustc_target;
 +extern crate rustc_trait_selection;
 +extern crate rustc_typeck;
 +
 +#[macro_use]
 +pub mod sym_helper;
 +
 +#[allow(clippy::module_name_repetitions)]
 +pub mod ast_utils;
 +pub mod attrs;
 +pub mod camel_case;
 +pub mod comparisons;
 +pub mod consts;
 +pub mod diagnostics;
 +pub mod eager_or_lazy;
 +pub mod higher;
 +mod hir_utils;
 +pub mod msrvs;
 +pub mod numeric_literal;
 +pub mod paths;
 +pub mod ptr;
 +pub mod qualify_min_const_fn;
 +pub mod source;
 +pub mod sugg;
 +pub mod ty;
 +pub mod usage;
 +pub mod visitors;
 +
 +pub use self::attrs::*;
 +pub use self::hir_utils::{both, count_eq, eq_expr_value, over, SpanlessEq, SpanlessHash};
 +
 +use std::collections::hash_map::Entry;
 +use std::hash::BuildHasherDefault;
 +
 +use if_chain::if_chain;
- use rustc_hir::LangItem::{ResultErr, ResultOk};
++use rustc_ast::ast::{self, Attribute, LitKind};
 +use rustc_data_structures::unhash::UnhashMap;
 +use rustc_hir as hir;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::DefId;
++use rustc_hir::hir_id::{HirIdMap, HirIdSet};
 +use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor};
-     ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path,
-     PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
++use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
 +use rustc_hir::{
 +    def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl,
- use rustc_middle::ty::{layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable};
++    ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, Param, Pat,
++    PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
 +};
 +use rustc_lint::{LateContext, Level, Lint, LintContext};
 +use rustc_middle::hir::exports::Export;
 +use rustc_middle::hir::map::Map;
++use rustc_middle::hir::place::PlaceBase;
 +use rustc_middle::ty as rustc_ty;
- use crate::ty::{can_partially_move_ty, is_recursively_primitive_type};
++use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
++use rustc_middle::ty::binding::BindingMode;
++use rustc_middle::ty::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable, UpvarCapture};
 +use rustc_semver::RustcVersion;
 +use rustc_session::Session;
 +use rustc_span::hygiene::{ExpnKind, MacroKind};
 +use rustc_span::source_map::original_sp;
 +use rustc_span::sym;
 +use rustc_span::symbol::{kw, Symbol};
 +use rustc_span::{Span, DUMMY_SP};
 +use rustc_target::abi::Integer;
 +
 +use crate::consts::{constant, Constant};
-     matches!(expr.kind, ExprKind::Block(Block { stmts: [], expr: None, .. }, _) | ExprKind::Tup([]))
++use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type};
 +
 +pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
 +    if let Ok(version) = RustcVersion::parse(msrv) {
 +        return Some(version);
 +    } else if let Some(sess) = sess {
 +        if let Some(span) = span {
 +            sess.span_err(span, &format!("`{}` is not a valid Rust version", msrv));
 +        }
 +    }
 +    None
 +}
 +
 +pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool {
 +    msrv.map_or(true, |msrv| msrv.meets(*lint_msrv))
 +}
 +
 +#[macro_export]
 +macro_rules! extract_msrv_attr {
 +    (LateContext) => {
 +        extract_msrv_attr!(@LateContext, ());
 +    };
 +    (EarlyContext) => {
 +        extract_msrv_attr!(@EarlyContext);
 +    };
 +    (@$context:ident$(, $call:tt)?) => {
 +        fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'tcx>, attrs: &'tcx [rustc_ast::ast::Attribute]) {
 +            use $crate::get_unique_inner_attr;
 +            match get_unique_inner_attr(cx.sess$($call)?, attrs, "msrv") {
 +                Some(msrv_attr) => {
 +                    if let Some(msrv) = msrv_attr.value_str() {
 +                        self.msrv = $crate::parse_msrv(
 +                            &msrv.to_string(),
 +                            Some(cx.sess$($call)?),
 +                            Some(msrv_attr.span),
 +                        );
 +                    } else {
 +                        cx.sess$($call)?.span_err(msrv_attr.span, "bad clippy attribute");
 +                    }
 +                },
 +                _ => (),
 +            }
 +        }
 +    };
 +}
 +
 +/// Returns `true` if the two spans come from differing expansions (i.e., one is
 +/// from a macro and one isn't).
 +#[must_use]
 +pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
 +    rhs.ctxt() != lhs.ctxt()
 +}
 +
 +/// If the given expression is a local binding, find the initializer expression.
 +/// If that initializer expression is another local binding, find its initializer again.
 +/// This process repeats as long as possible (but usually no more than once). Initializer
 +/// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
 +/// instead.
 +///
 +/// Examples:
 +/// ```ignore
 +/// let abc = 1;
 +/// //        ^ output
 +/// let def = abc;
 +/// dbg!(def)
 +/// //   ^^^ input
 +///
 +/// // or...
 +/// let abc = 1;
 +/// let def = abc + 2;
 +/// //        ^^^^^^^ output
 +/// dbg!(def)
 +/// //   ^^^ input
 +/// ```
 +pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
 +    while let Some(init) = path_to_local(expr)
 +        .and_then(|id| find_binding_init(cx, id))
 +        .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
 +    {
 +        expr = init;
 +    }
 +    expr
 +}
 +
 +/// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
 +/// By only considering immutable bindings, we guarantee that the returned expression represents the
 +/// value of the binding wherever it is referenced.
 +///
 +/// Example: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned.
 +/// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the
 +/// canonical binding `HirId`.
 +pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
 +    let hir = cx.tcx.hir();
 +    if_chain! {
 +        if let Some(Node::Binding(pat)) = hir.find(hir_id);
 +        if matches!(pat.kind, PatKind::Binding(BindingAnnotation::Unannotated, ..));
 +        let parent = hir.get_parent_node(hir_id);
 +        if let Some(Node::Local(local)) = hir.find(parent);
 +        then {
 +            return local.init;
 +        }
 +    }
 +    None
 +}
 +
 +/// Returns `true` if the given `NodeId` is inside a constant context
 +///
 +/// # Example
 +///
 +/// ```rust,ignore
 +/// if in_constant(cx, expr.hir_id) {
 +///     // Do something
 +/// }
 +/// ```
 +pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
 +    let parent_id = cx.tcx.hir().get_parent_item(id);
 +    match cx.tcx.hir().get(parent_id) {
 +        Node::Item(&Item {
 +            kind: ItemKind::Const(..) | ItemKind::Static(..),
 +            ..
 +        })
 +        | Node::TraitItem(&TraitItem {
 +            kind: TraitItemKind::Const(..),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Const(..),
 +            ..
 +        })
 +        | Node::AnonConst(_) => true,
 +        Node::Item(&Item {
 +            kind: ItemKind::Fn(ref sig, ..),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Fn(ref sig, _),
 +            ..
 +        }) => sig.header.constness == Constness::Const,
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if a `QPath` resolves to a constructor of a `LangItem`.
 +/// For example, use this to check whether a function call or a pattern is `Some(..)`.
 +pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem) -> bool {
 +    if let QPath::Resolved(_, path) = qpath {
 +        if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
 +            if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) {
 +                return cx.tcx.parent(ctor_id) == Some(item_id);
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns `true` if this `span` was expanded by any macro.
 +#[must_use]
 +pub fn in_macro(span: Span) -> bool {
 +    if span.from_expansion() {
 +        !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
- pub fn can_move_expr_to_closure_no_visit(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, jump_targets: &[HirId]) -> bool {
++    matches!(
++        expr.kind,
++        ExprKind::Block(
++            Block {
++                stmts: [],
++                expr: None,
++                ..
++            },
++            _
++        ) | ExprKind::Tup([])
++    )
 +}
 +
 +/// Checks if given pattern is a wildcard (`_`)
 +pub fn is_wild(pat: &Pat<'_>) -> bool {
 +    matches!(pat.kind, PatKind::Wild)
 +}
 +
 +/// Checks if the first type parameter is a lang item.
 +pub fn is_ty_param_lang_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: LangItem) -> Option<&'tcx hir::Ty<'tcx>> {
 +    let ty = get_qpath_generic_tys(qpath).next()?;
 +
 +    if let TyKind::Path(qpath) = &ty.kind {
 +        cx.qpath_res(qpath, ty.hir_id)
 +            .opt_def_id()
 +            .map_or(false, |id| {
 +                cx.tcx.lang_items().require(item).map_or(false, |lang_id| id == lang_id)
 +            })
 +            .then(|| ty)
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Checks if the first type parameter is a diagnostic item.
 +pub fn is_ty_param_diagnostic_item(
 +    cx: &LateContext<'_>,
 +    qpath: &QPath<'tcx>,
 +    item: Symbol,
 +) -> Option<&'tcx hir::Ty<'tcx>> {
 +    let ty = get_qpath_generic_tys(qpath).next()?;
 +
 +    if let TyKind::Path(qpath) = &ty.kind {
 +        cx.qpath_res(qpath, ty.hir_id)
 +            .opt_def_id()
 +            .map_or(false, |id| cx.tcx.is_diagnostic_item(item, id))
 +            .then(|| ty)
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Checks if the method call given in `expr` belongs to the given trait.
 +/// This is a deprecated function, consider using [`is_trait_method`].
 +pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
 +    let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
 +    let trt_id = cx.tcx.trait_of_item(def_id);
 +    trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
 +}
 +
 +/// Checks if a method is defined in an impl of a diagnostic item
 +pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
 +    if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
 +        if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
 +            return cx.tcx.is_diagnostic_item(diag_item, adt.did);
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks if a method is in a diagnostic item trait
 +pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
 +    if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
 +        return cx.tcx.is_diagnostic_item(diag_item, trait_did);
 +    }
 +    false
 +}
 +
 +/// Checks if the method call given in `expr` belongs to the given trait.
 +pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
 +    cx.typeck_results()
 +        .type_dependent_def_id(expr.hir_id)
 +        .map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
 +}
 +
 +/// Checks if the given expression is a path referring an item on the trait
 +/// that is marked with the given diagnostic item.
 +///
 +/// For checking method call expressions instead of path expressions, use
 +/// [`is_trait_method`].
 +///
 +/// For example, this can be used to find if an expression like `u64::default`
 +/// refers to an item of the trait `Default`, which is associated with the
 +/// `diag_item` of `sym::Default`.
 +pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
 +    if let hir::ExprKind::Path(ref qpath) = expr.kind {
 +        cx.qpath_res(qpath, expr.hir_id)
 +            .opt_def_id()
 +            .map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item))
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
 +    match *path {
 +        QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
 +        QPath::TypeRelative(_, seg) => seg,
 +        QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
 +    }
 +}
 +
 +pub fn get_qpath_generics(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> {
 +    match path {
 +        QPath::Resolved(_, p) => p.segments.last().and_then(|s| s.args),
 +        QPath::TypeRelative(_, s) => s.args,
 +        QPath::LangItem(..) => None,
 +    }
 +}
 +
 +pub fn get_qpath_generic_tys(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
 +    get_qpath_generics(path)
 +        .map_or([].as_ref(), |a| a.args)
 +        .iter()
 +        .filter_map(|a| {
 +            if let hir::GenericArg::Type(ty) = a {
 +                Some(ty)
 +            } else {
 +                None
 +            }
 +        })
 +}
 +
 +pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
 +    match *path {
 +        QPath::Resolved(_, path) => path.segments.get(0),
 +        QPath::TypeRelative(_, seg) => Some(seg),
 +        QPath::LangItem(..) => None,
 +    }
 +}
 +
 +/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
 +/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
 +/// `QPath::Resolved.1.res.opt_def_id()`.
 +///
 +/// Matches a `QPath` against a slice of segment string literals.
 +///
 +/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
 +/// `rustc_hir::QPath`.
 +///
 +/// # Examples
 +/// ```rust,ignore
 +/// match_qpath(path, &["std", "rt", "begin_unwind"])
 +/// ```
 +pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
 +    match *path {
 +        QPath::Resolved(_, path) => match_path(path, segments),
 +        QPath::TypeRelative(ty, segment) => match ty.kind {
 +            TyKind::Path(ref inner_path) => {
 +                if let [prefix @ .., end] = segments {
 +                    if match_qpath(inner_path, prefix) {
 +                        return segment.ident.name.as_str() == *end;
 +                    }
 +                }
 +                false
 +            },
 +            _ => false,
 +        },
 +        QPath::LangItem(..) => false,
 +    }
 +}
 +
 +/// If the expression is a path, resolve it. Otherwise, return `Res::Err`.
 +pub fn expr_path_res(cx: &LateContext<'_>, expr: &Expr<'_>) -> Res {
 +    if let ExprKind::Path(p) = &expr.kind {
 +        cx.qpath_res(p, expr.hir_id)
 +    } else {
 +        Res::Err
 +    }
 +}
 +
 +/// Resolves the path to a `DefId` and checks if it matches the given path.
 +pub fn is_qpath_def_path(cx: &LateContext<'_>, path: &QPath<'_>, hir_id: HirId, segments: &[&str]) -> bool {
 +    cx.qpath_res(path, hir_id)
 +        .opt_def_id()
 +        .map_or(false, |id| match_def_path(cx, id, segments))
 +}
 +
 +/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
 +///
 +/// Please use `is_expr_diagnostic_item` if the target is a diagnostic item.
 +pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
 +    expr_path_res(cx, expr)
 +        .opt_def_id()
 +        .map_or(false, |id| match_def_path(cx, id, segments))
 +}
 +
 +/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given
 +/// diagnostic item.
 +pub fn is_expr_diagnostic_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
 +    expr_path_res(cx, expr)
 +        .opt_def_id()
 +        .map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
 +}
 +
 +/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
 +/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
 +/// `QPath::Resolved.1.res.opt_def_id()`.
 +///
 +/// Matches a `Path` against a slice of segment string literals.
 +///
 +/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
 +/// `rustc_hir::Path`.
 +///
 +/// # Examples
 +///
 +/// ```rust,ignore
 +/// if match_path(&trait_ref.path, &paths::HASH) {
 +///     // This is the `std::hash::Hash` trait.
 +/// }
 +///
 +/// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
 +///     // This is a `rustc_middle::lint::Lint`.
 +/// }
 +/// ```
 +pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
 +    path.segments
 +        .iter()
 +        .rev()
 +        .zip(segments.iter().rev())
 +        .all(|(a, b)| a.ident.name.as_str() == *b)
 +}
 +
 +/// If the expression is a path to a local, returns the canonical `HirId` of the local.
 +pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
 +    if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
 +        if let Res::Local(id) = path.res {
 +            return Some(id);
 +        }
 +    }
 +    None
 +}
 +
 +/// Returns true if the expression is a path to a local with the specified `HirId`.
 +/// Use this function to see if an expression matches a function argument or a match binding.
 +pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
 +    path_to_local(expr) == Some(id)
 +}
 +
 +/// Gets the definition associated to a path.
 +#[allow(clippy::shadow_unrelated)] // false positive #6563
 +pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
 +    macro_rules! try_res {
 +        ($e:expr) => {
 +            match $e {
 +                Some(e) => e,
 +                None => return Res::Err,
 +            }
 +        };
 +    }
 +    fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export<HirId>> {
 +        tcx.item_children(def_id)
 +            .iter()
 +            .find(|item| item.ident.name.as_str() == name)
 +    }
 +
 +    let (krate, first, path) = match *path {
 +        [krate, first, ref path @ ..] => (krate, first, path),
 +        [primitive] => {
 +            return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy);
 +        },
 +        _ => return Res::Err,
 +    };
 +    let tcx = cx.tcx;
 +    let crates = tcx.crates(());
 +    let krate = try_res!(crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate));
 +    let first = try_res!(item_child_by_name(tcx, krate.as_def_id(), first));
 +    let last = path
 +        .iter()
 +        .copied()
 +        // `get_def_path` seems to generate these empty segments for extern blocks.
 +        // We can just ignore them.
 +        .filter(|segment| !segment.is_empty())
 +        // for each segment, find the child item
 +        .try_fold(first, |item, segment| {
 +            let def_id = item.res.def_id();
 +            if let Some(item) = item_child_by_name(tcx, def_id, segment) {
 +                Some(item)
 +            } else if matches!(item.res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
 +                // it is not a child item so check inherent impl items
 +                tcx.inherent_impls(def_id)
 +                    .iter()
 +                    .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment))
 +            } else {
 +                None
 +            }
 +        });
 +    try_res!(last).res
 +}
 +
 +/// Convenience function to get the `DefId` of a trait by path.
 +/// It could be a trait or trait alias.
 +pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
 +    match path_to_res(cx, path) {
 +        Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
 +        _ => None,
 +    }
 +}
 +
 +/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
 +///
 +/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
 +///
 +/// ```rust
 +/// struct Point(isize, isize);
 +///
 +/// impl std::ops::Add for Point {
 +///     type Output = Self;
 +///
 +///     fn add(self, other: Self) -> Self {
 +///         Point(0, 0)
 +///     }
 +/// }
 +/// ```
 +pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx TraitRef<'tcx>> {
 +    // Get the implemented trait for the current function
 +    let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
 +    if_chain! {
 +        if parent_impl != hir::CRATE_HIR_ID;
 +        if let hir::Node::Item(item) = cx.tcx.hir().get(parent_impl);
 +        if let hir::ItemKind::Impl(impl_) = &item.kind;
 +        then { return impl_.of_trait.as_ref(); }
 +    }
 +    None
 +}
 +
 +/// This method will return tuple of projection stack and root of the expression,
 +/// used in `can_mut_borrow_both`.
 +///
 +/// For example, if `e` represents the `v[0].a.b[x]`
 +/// this method will return a tuple, composed of a `Vec`
 +/// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]`
 +/// and an `Expr` for root of them, `v`
 +fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
 +    let mut result = vec![];
 +    let root = loop {
 +        match e.kind {
 +            ExprKind::Index(ep, _) | ExprKind::Field(ep, _) => {
 +                result.push(e);
 +                e = ep;
 +            },
 +            _ => break e,
 +        };
 +    };
 +    result.reverse();
 +    (result, root)
 +}
 +
 +/// Checks if two expressions can be mutably borrowed simultaneously
 +/// and they aren't dependent on borrowing same thing twice
 +pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
 +    let (s1, r1) = projection_stack(e1);
 +    let (s2, r2) = projection_stack(e2);
 +    if !eq_expr_value(cx, r1, r2) {
 +        return true;
 +    }
 +    for (x1, x2) in s1.iter().zip(s2.iter()) {
 +        match (&x1.kind, &x2.kind) {
 +            (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
 +                if i1 != i2 {
 +                    return true;
 +                }
 +            },
 +            (ExprKind::Index(_, i1), ExprKind::Index(_, i2)) => {
 +                if !eq_expr_value(cx, i1, i2) {
 +                    return false;
 +                }
 +            },
 +            _ => return false,
 +        }
 +    }
 +    false
 +}
 +
++/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"
++/// constructor from the std library
++fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
++    let std_types_symbols = &[
++        sym::string_type,
++        sym::vec_type,
++        sym::vecdeque_type,
++        sym::LinkedList,
++        sym::hashmap_type,
++        sym::BTreeMap,
++        sym::hashset_type,
++        sym::BTreeSet,
++        sym::BinaryHeap,
++    ];
++
++    if let QPath::TypeRelative(_, method) = path {
++        if method.ident.name == sym::new {
++            if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
++                if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
++                    return std_types_symbols
++                        .iter()
++                        .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did));
++                }
++            }
++        }
++    }
++    false
++}
++
++/// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
++/// It doesn't cover all cases, for example indirect function calls (some of std
++/// functions are supported) but it is the best we have.
++pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
++    match &e.kind {
++        ExprKind::Lit(lit) => match lit.node {
++            LitKind::Bool(false) | LitKind::Int(0, _) => true,
++            LitKind::Str(s, _) => s.is_empty(),
++            _ => false,
++        },
++        ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
++        ExprKind::Repeat(x, _) => is_default_equivalent(cx, x),
++        ExprKind::Call(repl_func, _) => if_chain! {
++            if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
++            if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
++            if is_diag_trait_item(cx, repl_def_id, sym::Default)
++                || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
++            then {
++                true
++            }
++            else {
++                false
++            }
++        },
++        ExprKind::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone),
++        ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
++        _ => false,
++    }
++}
++
 +/// Checks if the top level expression can be moved into a closure as is.
-             if jump_targets.contains(&id) =>
++/// Currently checks for:
++/// * Break/Continue outside the given loop HIR ids.
++/// * Yield/Return statments.
++/// * Inline assembly.
++/// * Usages of a field of a local where the type of the local can be partially moved.
++///
++/// For example, given the following function:
++///
++/// ```
++/// fn f<'a>(iter: &mut impl Iterator<Item = (usize, &'a mut String)>) {
++///     for item in iter {
++///         let s = item.1;
++///         if item.0 > 10 {
++///             continue;
++///         } else {
++///             s.clear();
++///         }
++///     }
++/// }
++/// ```
++///
++/// When called on the expression `item.0` this will return false unless the local `item` is in the
++/// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it
++/// isn't always safe to move into a closure when only a single field is needed.
++///
++/// When called on the `continue` expression this will return false unless the outer loop expression
++/// is in the `loop_ids` set.
++///
++/// Note that this check is not recursive, so passing the `if` expression will always return true
++/// even though sub-expressions might return false.
++pub fn can_move_expr_to_closure_no_visit(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    loop_ids: &[HirId],
++    ignore_locals: &HirIdSet,
++) -> bool {
 +    match expr.kind {
 +        ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
 +        | ExprKind::Continue(Destination { target_id: Ok(id), .. })
-         ExprKind::Field(base_expr, _)
-             if matches!(
-                 base_expr.kind,
-                 ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. }))
-             ) && can_partially_move_ty(cx, cx.typeck_results().expr_ty(base_expr)) =>
-         {
++            if loop_ids.contains(&id) =>
 +        {
 +            true
 +        },
 +        ExprKind::Break(..)
 +        | ExprKind::Continue(_)
 +        | ExprKind::Ret(_)
 +        | ExprKind::Yield(..)
 +        | ExprKind::InlineAsm(_)
 +        | ExprKind::LlvmInlineAsm(_) => false,
 +        // Accessing a field of a local value can only be done if the type isn't
 +        // partially moved.
-         }
++        ExprKind::Field(
++            &Expr {
++                hir_id,
++                kind:
++                    ExprKind::Path(QPath::Resolved(
++                        _,
++                        Path {
++                            res: Res::Local(local_id),
++                            ..
++                        },
++                    )),
++                ..
++            },
++            _,
++        ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
 +            // TODO: check if the local has been partially moved. Assume it has for now.
 +            false
- /// Checks if the expression can be moved into a closure as is.
- pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
++        },
 +        _ => true,
 +    }
 +}
 +
-             if let ExprKind::Loop(b, ..) = e.kind {
-                 self.loops.push(e.hir_id);
-                 self.visit_block(b);
-                 self.loops.pop();
-             } else {
-                 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops);
-                 walk_expr(self, e);
++/// How a local is captured by a closure
++#[derive(Debug, Clone, Copy, PartialEq, Eq)]
++pub enum CaptureKind {
++    Value,
++    Ref(Mutability),
++}
++impl CaptureKind {
++    pub fn is_imm_ref(self) -> bool {
++        self == Self::Ref(Mutability::Not)
++    }
++}
++impl std::ops::BitOr for CaptureKind {
++    type Output = Self;
++    fn bitor(self, rhs: Self) -> Self::Output {
++        match (self, rhs) {
++            (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
++            (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
++            | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
++            (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
++        }
++    }
++}
++impl std::ops::BitOrAssign for CaptureKind {
++    fn bitor_assign(&mut self, rhs: Self) {
++        *self = *self | rhs;
++    }
++}
++
++/// Given an expression referencing a local, determines how it would be captured in a closure.
++/// Note as this will walk up to parent expressions until the capture can be determined it should
++/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
++/// function argument (other than a receiver).
++pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind {
++    fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
++        let mut capture = CaptureKind::Ref(Mutability::Not);
++        pat.each_binding_or_first(&mut |_, id, span, _| match cx
++            .typeck_results()
++            .extract_binding_mode(cx.sess(), id, span)
++            .unwrap()
++        {
++            BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => {
++                capture = CaptureKind::Value;
++            },
++            BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => {
++                capture = CaptureKind::Ref(Mutability::Mut);
++            },
++            _ => (),
++        });
++        capture
++    }
++
++    debug_assert!(matches!(
++        e.kind,
++        ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
++    ));
++
++    let map = cx.tcx.hir();
++    let mut child_id = e.hir_id;
++    let mut capture = CaptureKind::Value;
++    let mut capture_expr_ty = e;
++
++    for (parent_id, parent) in map.parent_iter(e.hir_id) {
++        if let [Adjustment {
++            kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
++            target,
++        }, ref adjust @ ..] = *cx
++            .typeck_results()
++            .adjustments()
++            .get(child_id)
++            .map_or(&[][..], |x| &**x)
++        {
++            if let rustc_ty::RawPtr(TypeAndMut { mutbl: mutability, .. }) | rustc_ty::Ref(_, _, mutability) =
++                *adjust.last().map_or(target, |a| a.target).kind()
++            {
++                return CaptureKind::Ref(mutability);
++            }
++        }
++
++        match parent {
++            Node::Expr(e) => match e.kind {
++                ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
++                ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
++                ExprKind::Assign(lhs, ..) | ExprKind::Assign(_, lhs, _) if lhs.hir_id == child_id => {
++                    return CaptureKind::Ref(Mutability::Mut);
++                },
++                ExprKind::Field(..) => {
++                    if capture == CaptureKind::Value {
++                        capture_expr_ty = e;
++                    }
++                },
++                ExprKind::Let(pat, ..) => {
++                    let mutability = match pat_capture_kind(cx, pat) {
++                        CaptureKind::Value => Mutability::Not,
++                        CaptureKind::Ref(m) => m,
++                    };
++                    return CaptureKind::Ref(mutability);
++                },
++                ExprKind::Match(_, arms, _) => {
++                    let mut mutability = Mutability::Not;
++                    for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
++                        match capture {
++                            CaptureKind::Value => break,
++                            CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
++                            CaptureKind::Ref(Mutability::Not) => (),
++                        }
++                    }
++                    return CaptureKind::Ref(mutability);
++                },
++                _ => break,
++            },
++            Node::Local(l) => match pat_capture_kind(cx, l.pat) {
++                CaptureKind::Value => break,
++                capture @ CaptureKind::Ref(_) => return capture,
++            },
++            _ => break,
++        }
++
++        child_id = parent_id;
++    }
++
++    if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
++        // Copy types are never automatically captured by value.
++        CaptureKind::Ref(Mutability::Not)
++    } else {
++        capture
++    }
++}
++
++/// Checks if the expression can be moved into a closure as is. This will return a list of captures
++/// if so, otherwise, `None`.
++pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
 +    struct V<'cx, 'tcx> {
 +        cx: &'cx LateContext<'tcx>,
++        // Stack of potential break targets contained in the expression.
 +        loops: Vec<HirId>,
++        /// Local variables created in the expression. These don't need to be captured.
++        locals: HirIdSet,
++        /// Whether this expression can be turned into a closure.
 +        allow_closure: bool,
++        /// Locals which need to be captured, and whether they need to be by value, reference, or
++        /// mutable reference.
++        captures: HirIdMap<CaptureKind>,
 +    }
 +    impl Visitor<'tcx> for V<'_, 'tcx> {
 +        type Map = ErasedMap<'tcx>;
 +        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +            NestedVisitorMap::None
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if !self.allow_closure {
 +                return;
 +            }
-     v.allow_closure
++
++            match e.kind {
++                ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
++                    if !self.locals.contains(&l) {
++                        let cap = capture_local_usage(self.cx, e);
++                        self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
++                    }
++                },
++                ExprKind::Closure(..) => {
++                    let closure_id = self.cx.tcx.hir().local_def_id(e.hir_id).to_def_id();
++                    for capture in self.cx.typeck_results().closure_min_captures_flattened(closure_id) {
++                        let local_id = match capture.place.base {
++                            PlaceBase::Local(id) => id,
++                            PlaceBase::Upvar(var) => var.var_path.hir_id,
++                            _ => continue,
++                        };
++                        if !self.locals.contains(&local_id) {
++                            let capture = match capture.info.capture_kind {
++                                UpvarCapture::ByValue(_) => CaptureKind::Value,
++                                UpvarCapture::ByRef(borrow) => match borrow.kind {
++                                    BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
++                                    BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
++                                        CaptureKind::Ref(Mutability::Mut)
++                                    },
++                                },
++                            };
++                            self.captures
++                                .entry(local_id)
++                                .and_modify(|e| *e |= capture)
++                                .or_insert(capture);
++                        }
++                    }
++                },
++                ExprKind::Loop(b, ..) => {
++                    self.loops.push(e.hir_id);
++                    self.visit_block(b);
++                    self.loops.pop();
++                },
++                _ => {
++                    self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
++                    walk_expr(self, e);
++                },
 +            }
 +        }
++
++        fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
++            p.each_binding_or_first(&mut |_, id, _, _| {
++                self.locals.insert(id);
++            });
++        }
 +    }
 +
 +    let mut v = V {
 +        cx,
 +        allow_closure: true,
 +        loops: Vec::new(),
++        locals: HirIdSet::default(),
++        captures: HirIdMap::default(),
 +    };
 +    v.visit_expr(expr);
-         if let ExprKind::Block(ref block, _) = then.kind {
++    v.allow_closure.then(|| v.captures)
 +}
 +
 +/// Returns the method names and argument list of nested method call expressions that make up
 +/// `expr`. method/span lists are sorted with the most recent call first.
 +pub fn method_calls<'tcx>(
 +    expr: &'tcx Expr<'tcx>,
 +    max_depth: usize,
 +) -> (Vec<Symbol>, Vec<&'tcx [Expr<'tcx>]>, Vec<Span>) {
 +    let mut method_names = Vec::with_capacity(max_depth);
 +    let mut arg_lists = Vec::with_capacity(max_depth);
 +    let mut spans = Vec::with_capacity(max_depth);
 +
 +    let mut current = expr;
 +    for _ in 0..max_depth {
 +        if let ExprKind::MethodCall(path, span, args, _) = &current.kind {
 +            if args.iter().any(|e| e.span.from_expansion()) {
 +                break;
 +            }
 +            method_names.push(path.ident.name);
 +            arg_lists.push(&**args);
 +            spans.push(*span);
 +            current = &args[0];
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    (method_names, arg_lists, spans)
 +}
 +
 +/// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
 +///
 +/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
 +/// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec`
 +/// containing the `Expr`s for
 +/// `.bar()` and `.baz()`
 +pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<&'a [Expr<'a>]>> {
 +    let mut current = expr;
 +    let mut matched = Vec::with_capacity(methods.len());
 +    for method_name in methods.iter().rev() {
 +        // method chains are stored last -> first
 +        if let ExprKind::MethodCall(path, _, args, _) = current.kind {
 +            if path.ident.name.as_str() == *method_name {
 +                if args.iter().any(|e| e.span.from_expansion()) {
 +                    return None;
 +                }
 +                matched.push(args); // build up `matched` backwards
 +                current = &args[0]; // go to parent expression
 +            } else {
 +                return None;
 +            }
 +        } else {
 +            return None;
 +        }
 +    }
 +    // Reverse `matched` so that it is in the same order as `methods`.
 +    matched.reverse();
 +    Some(matched)
 +}
 +
 +/// Returns `true` if the provided `def_id` is an entrypoint to a program.
 +pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    cx.tcx
 +        .entry_fn(())
 +        .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id)
 +}
 +
 +/// Returns `true` if the expression is in the program's `#[panic_handler]`.
 +pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    let parent = cx.tcx.hir().get_parent_item(e.hir_id);
 +    let def_id = cx.tcx.hir().local_def_id(parent).to_def_id();
 +    Some(def_id) == cx.tcx.lang_items().panic_impl()
 +}
 +
 +/// Gets the name of the item the expression is in, if available.
 +pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
 +    let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
 +    match cx.tcx.hir().find(parent_id) {
 +        Some(
 +            Node::Item(Item { ident, .. })
 +            | Node::TraitItem(TraitItem { ident, .. })
 +            | Node::ImplItem(ImplItem { ident, .. }),
 +        ) => Some(ident.name),
 +        _ => None,
 +    }
 +}
 +
 +pub struct ContainsName {
 +    pub name: Symbol,
 +    pub result: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for ContainsName {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_name(&mut self, _: Span, name: Symbol) {
 +        if self.name == name {
 +            self.result = true;
 +        }
 +    }
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
 +/// Checks if an `Expr` contains a certain name.
 +pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
 +    let mut cn = ContainsName { name, result: false };
 +    cn.visit_expr(expr);
 +    cn.result
 +}
 +
 +/// Returns `true` if `expr` contains a return expression
 +pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
 +    struct RetCallFinder {
 +        found: bool,
 +    }
 +
 +    impl<'tcx> hir::intravisit::Visitor<'tcx> for RetCallFinder {
 +        type Map = Map<'tcx>;
 +
 +        fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
 +            if self.found {
 +                return;
 +            }
 +            if let hir::ExprKind::Ret(..) = &expr.kind {
 +                self.found = true;
 +            } else {
 +                hir::intravisit::walk_expr(self, expr);
 +            }
 +        }
 +
 +        fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
 +            hir::intravisit::NestedVisitorMap::None
 +        }
 +    }
 +
 +    let mut visitor = RetCallFinder { found: false };
 +    visitor.visit_expr(expr);
 +    visitor.found
 +}
 +
 +struct FindMacroCalls<'a, 'b> {
 +    names: &'a [&'b str],
 +    result: Vec<Span>,
 +}
 +
 +impl<'a, 'b, 'tcx> Visitor<'tcx> for FindMacroCalls<'a, 'b> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
 +            self.result.push(expr.span);
 +        }
 +        // and check sub-expressions
 +        intravisit::walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
 +/// Finds calls of the specified macros in a function body.
 +pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> {
 +    let mut fmc = FindMacroCalls {
 +        names,
 +        result: Vec::new(),
 +    };
 +    fmc.visit_expr(&body.value);
 +    fmc.result
 +}
 +
 +/// Extends the span to the beginning of the spans line, incl. whitespaces.
 +///
 +/// ```rust,ignore
 +///        let x = ();
 +/// //             ^^
 +/// // will be converted to
 +///        let x = ();
 +/// // ^^^^^^^^^^^^^^
 +/// ```
 +fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
 +    let span = original_sp(span, DUMMY_SP);
 +    let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap();
 +    let line_no = source_map_and_line.line;
 +    let line_start = source_map_and_line.sf.lines[line_no];
 +    span.with_lo(line_start)
 +}
 +
 +/// Gets the parent node, if any.
 +pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
 +    tcx.hir().parent_iter(id).next().map(|(_, node)| node)
 +}
 +
 +/// Gets the parent expression, if any –- this is useful to constrain a lint.
 +pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    get_parent_expr_for_hir(cx, e.hir_id)
 +}
 +
 +/// This retrieves the parent for the given `HirId` if it's an expression. This is useful for
 +/// constraint lints
 +pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> {
 +    match get_parent_node(cx.tcx, hir_id) {
 +        Some(Node::Expr(parent)) => Some(parent),
 +        _ => None,
 +    }
 +}
 +
 +pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
 +    let map = &cx.tcx.hir();
 +    let enclosing_node = map
 +        .get_enclosing_scope(hir_id)
 +        .and_then(|enclosing_id| map.find(enclosing_id));
 +    enclosing_node.and_then(|node| match node {
 +        Node::Block(block) => Some(block),
 +        Node::Item(&Item {
 +            kind: ItemKind::Fn(_, _, eid),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Fn(_, eid),
 +            ..
 +        }) => match cx.tcx.hir().body(eid).value.kind {
 +            ExprKind::Block(block, _) => Some(block),
 +            _ => None,
 +        },
 +        _ => None,
 +    })
 +}
 +
 +/// Gets the loop or closure enclosing the given expression, if any.
 +pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    let map = tcx.hir();
 +    for (_, node) in map.parent_iter(expr.hir_id) {
 +        match node {
 +            Node::Expr(
 +                e
 +                @
 +                Expr {
 +                    kind: ExprKind::Loop(..) | ExprKind::Closure(..),
 +                    ..
 +                },
 +            ) => return Some(e),
 +            Node::Expr(_) | Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
 +            _ => break,
 +        }
 +    }
 +    None
 +}
 +
 +/// Gets the parent node if it's an impl block.
 +pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
 +    let map = tcx.hir();
 +    match map.parent_iter(id).next() {
 +        Some((
 +            _,
 +            Node::Item(Item {
 +                kind: ItemKind::Impl(imp),
 +                ..
 +            }),
 +        )) => Some(imp),
 +        _ => None,
 +    }
 +}
 +
 +/// Checks if the given expression is the else clause of either an `if` or `if let` expression.
 +pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    let map = tcx.hir();
 +    let mut iter = map.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) {
 +        value == v
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Checks whether the given expression is a constant literal of the given value.
 +pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
 +    // FIXME: use constant folding
 +    if let ExprKind::Lit(ref spanned) = expr.kind {
 +        if let LitKind::Int(v, _) = spanned.node {
 +            return v == value;
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns `true` if the given `Expr` has been coerced before.
 +///
 +/// Examples of coercions can be found in the Nomicon at
 +/// <https://doc.rust-lang.org/nomicon/coercions.html>.
 +///
 +/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_typeck::check::coercion` for more
 +/// information on adjustments and coercions.
 +pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    cx.typeck_results().adjustments().get(e.hir_id).is_some()
 +}
 +
 +/// Returns the pre-expansion span if is this comes from an expansion of the
 +/// macro `name`.
 +/// 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,ignore
 +/// foo!(bar!(42));
 +/// ```
 +/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
 +/// `bar!` by
 +/// `is_direct_expn_of`.
 +#[must_use]
 +pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
 +    if span.from_expansion() {
 +        let data = span.ctxt().outer_expn_data();
 +        let new_span = data.call_site;
 +
 +        if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
 +            if mac_name.as_str() == name {
 +                return Some(new_span);
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +/// Convenience function to get the return type of a function.
 +pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
 +    let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
 +    let ret_ty = cx.tcx.fn_sig(fn_def_id).output();
 +    cx.tcx.erase_late_bound_regions(ret_ty)
 +}
 +
 +/// Checks if an expression is constructing a tuple-like enum variant or struct
 +pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Call(fun, _) = expr.kind {
 +        if let ExprKind::Path(ref qp) = fun.kind {
 +            let res = cx.qpath_res(qp, fun.hir_id);
 +            return match res {
 +                def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
 +                def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
 +                _ => false,
 +            };
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns `true` if a pattern is refutable.
 +// TODO: should be implemented using rustc/mir_build/thir machinery
 +pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
 +    fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
 +        matches!(
 +            cx.qpath_res(qpath, id),
 +            def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
 +        )
 +    }
 +
 +    fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
 +        i.into_iter().any(|pat| is_refutable(cx, pat))
 +    }
 +
 +    match pat.kind {
 +        PatKind::Wild => false,
 +        PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
 +        PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
 +        PatKind::Lit(..) | PatKind::Range(..) => true,
 +        PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
 +        PatKind::Or(pats) => {
 +            // TODO: should be the honest check, that pats is exhaustive set
 +            are_refutable(cx, pats)
 +        },
 +        PatKind::Tuple(pats, _) => are_refutable(cx, pats),
 +        PatKind::Struct(ref qpath, fields, _) => {
 +            is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat))
 +        },
 +        PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
 +        PatKind::Slice(head, middle, tail) => {
 +            match &cx.typeck_results().node_type(pat.hir_id).kind() {
 +                rustc_ty::Slice(..) => {
 +                    // [..] is the only irrefutable slice pattern.
 +                    !head.is_empty() || middle.is_none() || !tail.is_empty()
 +                },
 +                rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
 +                _ => {
 +                    // unreachable!()
 +                    true
 +                },
 +            }
 +        },
 +    }
 +}
 +
 +/// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call
 +/// the function once on the given pattern.
 +pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
 +    if let PatKind::Or(pats) = pat.kind {
 +        pats.iter().for_each(f);
 +    } else {
 +        f(pat);
 +    }
 +}
 +
 +/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
 +/// implementations have.
 +pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
 +    attrs.iter().any(|attr| attr.has_name(sym::automatically_derived))
 +}
 +
 +/// Remove blocks around an expression.
 +///
 +/// Ie. `x`, `{ x }` and `{{{{ x }}}}` all give `x`. `{ x; y }` and `{}` return
 +/// themselves.
 +pub fn remove_blocks<'tcx>(mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
 +    while let ExprKind::Block(block, ..) = expr.kind {
 +        match (block.stmts.is_empty(), block.expr.as_ref()) {
 +            (true, Some(e)) => expr = e,
 +            _ => break,
 +        }
 +    }
 +    expr
 +}
 +
 +pub fn is_self(slf: &Param<'_>) -> bool {
 +    if let PatKind::Binding(.., name, _) = slf.pat.kind {
 +        name.name == kw::SelfLower
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
 +    if_chain! {
 +        if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind;
 +        if let Res::SelfTy(..) = path.res;
 +        then {
 +            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 let MatchSource::TryDesugar = *source {
 +            return Some(expr);
 +        }
 +
 +        if_chain! {
 +            if arms.len() == 2;
 +            if arms[0].guard.is_none();
 +            if arms[1].guard.is_none();
 +            if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) ||
 +                (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
 +            then {
 +                return Some(expr);
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +/// Returns `true` if the lint is allowed in the current context
 +///
 +/// Useful for skipping long running code when it's unnecessary
 +pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
 +    cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
 +}
 +
 +pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
 +    while let PatKind::Ref(subpat, _) = pat.kind {
 +        pat = subpat;
 +    }
 +    pat
 +}
 +
 +pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 {
 +    Integer::from_int_ty(&tcx, ity).size().bits()
 +}
 +
 +#[allow(clippy::cast_possible_wrap)]
 +/// Turn a constant int byte representation into an i128
 +pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 {
 +    let amt = 128 - int_bits(tcx, ity);
 +    ((u as i128) << amt) >> amt
 +}
 +
 +#[allow(clippy::cast_sign_loss)]
 +/// clip unused bytes
 +pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 {
 +    let amt = 128 - int_bits(tcx, ity);
 +    ((u as u128) << amt) >> amt
 +}
 +
 +/// clip unused bytes
 +pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 {
 +    let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
 +    let amt = 128 - bits;
 +    (u << amt) >> amt
 +}
 +
 +pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
 +    let map = &tcx.hir();
 +    let mut prev_enclosing_node = None;
 +    let mut enclosing_node = node;
 +    while Some(enclosing_node) != prev_enclosing_node {
 +        if is_automatically_derived(map.attrs(enclosing_node)) {
 +            return true;
 +        }
 +        prev_enclosing_node = Some(enclosing_node);
 +        enclosing_node = map.get_parent_item(enclosing_node);
 +    }
 +    false
 +}
 +
 +/// Matches a function call with the given path and returns the arguments.
 +///
 +/// Usage:
 +///
 +/// ```rust,ignore
 +/// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX);
 +/// ```
 +pub fn match_function_call<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    path: &[&str],
 +) -> Option<&'tcx [Expr<'tcx>]> {
 +    if_chain! {
 +        if let ExprKind::Call(fun, args) = expr.kind;
 +        if let ExprKind::Path(ref qpath) = fun.kind;
 +        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
 +        if match_def_path(cx, fun_def_id, path);
 +        then {
 +            return Some(args)
 +        }
 +    };
 +    None
 +}
 +
 +/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
 +/// any.
 +///
 +/// Please use `match_any_diagnostic_items` if the targets are all diagnostic items.
 +pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
 +    let search_path = cx.get_def_path(did);
 +    paths
 +        .iter()
 +        .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
 +}
 +
 +/// Checks if the given `DefId` matches any of provided diagnostic items. Returns the index of
 +/// matching path, if any.
 +pub fn match_any_diagnostic_items(cx: &LateContext<'_>, def_id: DefId, diag_items: &[Symbol]) -> Option<usize> {
 +    diag_items
 +        .iter()
 +        .position(|item| cx.tcx.is_diagnostic_item(*item, def_id))
 +}
 +
 +/// Checks if the given `DefId` matches the path.
 +pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
 +    // We should probably move to Symbols in Clippy as well rather than interning every time.
 +    let path = cx.get_def_path(did);
 +    syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
 +}
 +
 +pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    if let ExprKind::Call(func, [arg]) = expr.kind {
 +        expr_path_res(cx, func)
 +            .opt_def_id()
 +            .map_or(false, |id| match_panic_def_id(cx, id))
 +            .then(|| arg)
 +    } else {
 +        None
 +    }
 +}
 +
 +pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool {
 +    match_any_def_paths(
 +        cx,
 +        did,
 +        &[
 +            &paths::BEGIN_PANIC,
 +            &paths::BEGIN_PANIC_FMT,
 +            &paths::PANIC_ANY,
 +            &paths::PANICKING_PANIC,
 +            &paths::PANICKING_PANIC_FMT,
 +            &paths::PANICKING_PANIC_STR,
 +        ],
 +    )
 +    .is_some()
 +}
 +
 +/// Returns the list of condition expressions and the list of blocks in a
 +/// sequence of `if/else`.
 +/// E.g., this returns `([a, b], [c, d, e])` for the expression
 +/// `if a { c } else if b { d } else { e }`.
 +pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
 +    let mut conds = Vec::new();
 +    let mut blocks: Vec<&Block<'_>> = Vec::new();
 +
 +    while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
 +        conds.push(&*cond);
-         if let Some(ref else_expr) = r#else {
++        if let ExprKind::Block(block, _) = then.kind {
 +            blocks.push(block);
 +        } else {
 +            panic!("ExprKind::If node is not an ExprKind::Block");
 +        }
 +
-         ExprKind::AddrOf(BorrowKind::Ref, _, e) if remaining != 0 => {
++        if let Some(else_expr) = r#else {
 +            expr = else_expr;
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    // final `else {..}`
 +    if !blocks.is_empty() {
 +        if let ExprKind::Block(block, _) = expr.kind {
 +            blocks.push(block);
 +        }
 +    }
 +
 +    (conds, blocks)
 +}
 +
 +/// Checks if the given function kind is an async function.
 +pub fn is_async_fn(kind: FnKind<'_>) -> bool {
 +    matches!(kind, FnKind::ItemFn(_, _, header, _) if header.asyncness == IsAsync::Async)
 +}
 +
 +/// Peels away all the compiler generated code surrounding the body of an async function,
 +pub fn get_async_fn_body(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    if let ExprKind::Call(
 +        _,
 +        &[Expr {
 +            kind: ExprKind::Closure(_, _, body, _, _),
 +            ..
 +        }],
 +    ) = body.value.kind
 +    {
 +        if let ExprKind::Block(
 +            Block {
 +                stmts: [],
 +                expr:
 +                    Some(Expr {
 +                        kind: ExprKind::DropTemps(expr),
 +                        ..
 +                    }),
 +                ..
 +            },
 +            _,
 +        ) = tcx.hir().body(body).value.kind
 +        {
 +            return Some(expr);
 +        }
 +    };
 +    None
 +}
 +
 +// Finds the `#[must_use]` attribute, if any
 +pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
 +    attrs.iter().find(|a| a.has_name(sym::must_use))
 +}
 +
 +// check if expr is calling method or function with #[must_use] attribute
 +pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    let did = match expr.kind {
 +        ExprKind::Call(path, _) => if_chain! {
 +            if let ExprKind::Path(ref qpath) = path.kind;
 +            if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id);
 +            then {
 +                Some(did)
 +            } else {
 +                None
 +            }
 +        },
 +        ExprKind::MethodCall(_, _, _, _) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
 +        _ => None,
 +    };
 +
 +    did.map_or(false, |did| must_use_attr(cx.tcx.get_attrs(did)).is_some())
 +}
 +
 +/// Checks if an expression represents the identity function
 +/// Only examines closures and `std::convert::identity`
 +pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    /// Checks if a function's body represents the identity function. Looks for bodies of the form:
 +    /// * `|x| x`
 +    /// * `|x| return x`
 +    /// * `|x| { return x }`
 +    /// * `|x| { return x; }`
 +    fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
 +        let id = if_chain! {
 +            if let [param] = func.params;
 +            if let PatKind::Binding(_, id, _, _) = param.pat.kind;
 +            then {
 +                id
 +            } else {
 +                return false;
 +            }
 +        };
 +
 +        let mut expr = &func.value;
 +        loop {
 +            match expr.kind {
 +                #[rustfmt::skip]
 +                ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
 +                | ExprKind::Ret(Some(e)) => expr = e,
 +                #[rustfmt::skip]
 +                ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
 +                    if_chain! {
 +                        if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
 +                        if let ExprKind::Ret(Some(ret_val)) = e.kind;
 +                        then {
 +                            expr = ret_val;
 +                        } else {
 +                            return false;
 +                        }
 +                    }
 +                },
 +                _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
 +            }
 +        }
 +    }
 +
 +    match expr.kind {
 +        ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
 +        ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
 +        _ => false,
 +    }
 +}
 +
 +/// Gets the node where an expression is either used, or it's type is unified with another branch.
 +pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
 +    let map = tcx.hir();
 +    let mut child_id = expr.hir_id;
 +    let mut iter = map.parent_iter(child_id);
 +    loop {
 +        match iter.next() {
 +            None => break None,
 +            Some((id, Node::Block(_))) => child_id = id,
 +            Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
 +            Some((_, Node::Expr(expr))) => match expr.kind {
 +                ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
 +                ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
 +                ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
 +                _ => break Some(Node::Expr(expr)),
 +            },
 +            Some((_, node)) => break Some(node),
 +        }
 +    }
 +}
 +
 +/// Checks if the result of an expression is used, or it's type is unified with another branch.
 +pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    !matches!(
 +        get_expr_use_or_unification_node(tcx, expr),
 +        None | Some(Node::Stmt(Stmt {
 +            kind: StmtKind::Expr(_)
 +                | StmtKind::Semi(_)
 +                | StmtKind::Local(Local {
 +                    pat: Pat {
 +                        kind: PatKind::Wild,
 +                        ..
 +                    },
 +                    ..
 +                }),
 +            ..
 +        }))
 +    )
 +}
 +
 +/// Checks if the expression is the final expression returned from a block.
 +pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
 +}
 +
 +pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
 +    cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
 +        if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
 +            attr.path == sym::no_std
 +        } else {
 +            false
 +        }
 +    })
 +}
 +
 +/// Check if parent of a hir node is a trait implementation block.
 +/// For example, `f` in
 +/// ```rust,ignore
 +/// impl Trait for S {
 +///     fn f() {}
 +/// }
 +/// ```
 +pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
 +    if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
 +        matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Check if it's even possible to satisfy the `where` clause for the item.
 +///
 +/// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example:
 +///
 +/// ```ignore
 +/// fn foo() where i32: Iterator {
 +///     for _ in 2i32 {}
 +/// }
 +/// ```
 +pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
 +    use rustc_trait_selection::traits;
 +    let predicates = cx
 +        .tcx
 +        .predicates_of(did)
 +        .predicates
 +        .iter()
 +        .filter_map(|(p, _)| if p.is_global(cx.tcx) { Some(*p) } else { None });
 +    traits::impossible_predicates(
 +        cx.tcx,
 +        traits::elaborate_predicates(cx.tcx, predicates)
 +            .map(|o| o.predicate)
 +            .collect::<Vec<_>>(),
 +    )
 +}
 +
 +/// Returns the `DefId` of the callee if the given expression is a function or method call.
 +pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
 +    match &expr.kind {
 +        ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
 +        ExprKind::Call(
 +            Expr {
 +                kind: ExprKind::Path(qpath),
 +                hir_id: path_hir_id,
 +                ..
 +            },
 +            ..,
 +        ) => cx.typeck_results().qpath_res(qpath, *path_hir_id).opt_def_id(),
 +        _ => None,
 +    }
 +}
 +
 +/// Returns Option<String> where String is a textual representation of the type encapsulated in the
 +/// slice iff the given expression is a slice of primitives (as defined in the
 +/// `is_recursively_primitive_type` function) and None otherwise.
 +pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
 +    let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
 +    let expr_kind = expr_type.kind();
 +    let is_primitive = match expr_kind {
 +        rustc_ty::Slice(element_type) => is_recursively_primitive_type(element_type),
 +        rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
 +            if let rustc_ty::Slice(element_type) = inner_ty.kind() {
 +                is_recursively_primitive_type(element_type)
 +            } else {
 +                unreachable!()
 +            }
 +        },
 +        _ => false,
 +    };
 +
 +    if is_primitive {
 +        // if we have wrappers like Array, Slice or Tuple, print these
 +        // and get the type enclosed in the slice ref
 +        match expr_type.peel_refs().walk(cx.tcx).nth(1).unwrap().expect_ty().kind() {
 +            rustc_ty::Slice(..) => return Some("slice".into()),
 +            rustc_ty::Array(..) => return Some("array".into()),
 +            rustc_ty::Tuple(..) => return Some("tuple".into()),
 +            _ => {
 +                // is_recursively_primitive_type() should have taken care
 +                // of the rest and we can rely on the type that is found
 +                let refs_peeled = expr_type.peel_refs();
 +                return Some(refs_peeled.walk(cx.tcx).last().unwrap().to_string());
 +            },
 +        }
 +    }
 +    None
 +}
 +
 +/// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
 +/// `hash` must be comformed with `eq`
 +pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
 +where
 +    Hash: Fn(&T) -> u64,
 +    Eq: Fn(&T, &T) -> bool,
 +{
 +    match exprs {
 +        [a, b] if eq(a, b) => return vec![(a, b)],
 +        _ if exprs.len() <= 2 => return vec![],
 +        _ => {},
 +    }
 +
 +    let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
 +
 +    let mut map: UnhashMap<u64, Vec<&_>> =
 +        UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
 +
 +    for expr in exprs {
 +        match map.entry(hash(expr)) {
 +            Entry::Occupied(mut o) => {
 +                for o in o.get() {
 +                    if eq(o, expr) {
 +                        match_expr_list.push((o, expr));
 +                    }
 +                }
 +                o.get_mut().push(expr);
 +            },
 +            Entry::Vacant(v) => {
 +                v.insert(vec![expr]);
 +            },
 +        }
 +    }
 +
 +    match_expr_list
 +}
 +
 +/// Peels off all references on the pattern. Returns the underlying pattern and the number of
 +/// references removed.
 +pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
 +    fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
 +        if let PatKind::Ref(pat, _) = pat.kind {
 +            peel(pat, count + 1)
 +        } else {
 +            (pat, count)
 +        }
 +    }
 +    peel(pat, 0)
 +}
 +
 +/// Peels of expressions while the given closure returns `Some`.
 +pub fn peel_hir_expr_while<'tcx>(
 +    mut expr: &'tcx Expr<'tcx>,
 +    mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
 +) -> &'tcx Expr<'tcx> {
 +    while let Some(e) = f(expr) {
 +        expr = e;
 +    }
 +    expr
 +}
 +
 +/// Peels off up to the given number of references on the expression. Returns the underlying
 +/// expression and the number of references removed.
 +pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
 +    let mut remaining = count;
 +    let e = peel_hir_expr_while(expr, |e| match e.kind {
-         ExprKind::AddrOf(BorrowKind::Ref, _, e) => {
++        ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
 +            remaining -= 1;
 +            Some(e)
 +        },
 +        _ => None,
 +    });
 +    (e, count - remaining)
 +}
 +
 +/// Peels off all references on the expression. Returns the underlying expression and the number of
 +/// references removed.
 +pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
 +    let mut count = 0;
 +    let e = peel_hir_expr_while(expr, |e| match e.kind {
++        ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
 +            count += 1;
 +            Some(e)
 +        },
 +        _ => None,
 +    });
 +    (e, count)
 +}
 +
 +/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
 +/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
 +pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
 +    loop {
 +        match expr.kind {
 +            ExprKind::AddrOf(_, _, e) => expr = e,
 +            ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
 +            _ => break,
 +        }
 +    }
 +    expr
 +}
 +
 +#[macro_export]
 +macro_rules! unwrap_cargo_metadata {
 +    ($cx: ident, $lint: ident, $deps: expr) => {{
 +        let mut command = cargo_metadata::MetadataCommand::new();
 +        if !$deps {
 +            command.no_deps();
 +        }
 +
 +        match command.exec() {
 +            Ok(metadata) => metadata,
 +            Err(err) => {
 +                span_lint($cx, $lint, DUMMY_SP, &format!("could not read cargo metadata: {}", err));
 +                return;
 +            },
 +        }
 +    }};
 +}
 +
 +pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
 +    if_chain! {
 +        if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
 +        if let Res::Def(_, def_id) = path.res;
 +        then {
 +            cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr)
 +        } else {
 +            false
 +        }
 +    }
 +}
 +
 +/// Checks whether item either has `test` attribute applied, or
 +/// is a module with `test` in its name.
 +pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
 +    if let Some(def_id) = tcx.hir().opt_local_def_id(item.hir_id()) {
 +        if tcx.has_attr(def_id.to_def_id(), sym::test) {
 +            return true;
 +        }
 +    }
 +
 +    matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test")
 +}
 +
 +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 4a9c4fd0276b378074b0b9895512f22a2e5a65d1,0000000000000000000000000000000000000000..fa57dfbb57edccdc4d384af5ac4a0e226ab9ea2f
mode 100644,000000..100644
--- /dev/null
@@@ -1,30 -1,0 +1,33 @@@
 +use rustc_semver::RustcVersion;
 +
 +macro_rules! msrv_aliases {
 +    ($($major:literal,$minor:literal,$patch:literal {
 +        $($name:ident),* $(,)?
 +    })*) => {
 +        $($(
 +        pub const $name: RustcVersion = RustcVersion::new($major, $minor, $patch);
 +        )*)*
 +    };
 +}
 +
 +// names may refer to stabilized feature flags or library items
 +msrv_aliases! {
 +    1,53,0 { OR_PATTERNS }
++    1,52,0 { STR_SPLIT_ONCE }
 +    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 }
 +    1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
 +    1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
 +    1,38,0 { POINTER_CAST }
 +    1,37,0 { TYPE_ALIAS_ENUM_VARIANTS }
 +    1,36,0 { ITERATOR_COPIED }
 +    1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
 +    1,34,0 { TRY_FROM }
 +    1,30,0 { ITERATOR_FIND_MAP }
 +    1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST }
 +    1,16,0 { STR_REPEAT }
 +}
index b0c3fe1e5a7121d56cafc32e45a0f872536d633b,0000000000000000000000000000000000000000..d7e46c2d3eb9d2826966f19fc3d8f98b058a150d
mode 100644,000000..100644
--- /dev/null
@@@ -1,179 -1,0 +1,180 @@@
 +//! This module contains paths to types and functions Clippy needs to know
 +//! about.
 +//!
 +//! Whenever possible, please consider diagnostic items over hardcoded paths.
 +//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
 +
 +pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"];
 +#[cfg(feature = "metadata-collector-lint")]
 +pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
 +#[cfg(feature = "metadata-collector-lint")]
 +pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
 +    ["rustc_lint_defs", "Applicability", "Unspecified"],
 +    ["rustc_lint_defs", "Applicability", "HasPlaceholders"],
 +    ["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
 +    ["rustc_lint_defs", "Applicability", "MachineApplicable"],
 +];
 +#[cfg(feature = "metadata-collector-lint")]
 +pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];
 +pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
 +pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
 +pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
 +pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
 +pub(super) const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"];
 +/// Preferably use the diagnostic item `sym::Borrow` where possible
 +pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
 +pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
 +pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
 +pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
 +pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
 +pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
 +pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
 +pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
 +pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"];
 +pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
 +pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
 +/// Preferably use the diagnostic item `sym::deref_method` where possible
 +pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
 +pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"];
 +pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
 +pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
 +pub const DROP: [&str; 3] = ["core", "mem", "drop"];
 +pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
 +#[cfg(feature = "internal-lints")]
 +pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
 +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: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
 +pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
 +pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"];
 +pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
 +pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
 +pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"];
 +pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
 +pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
 +#[cfg(feature = "internal-lints")]
 +pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
 +#[cfg(feature = "internal-lints")]
 +pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
 +pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
 +pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
 +pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
 +pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
 +pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
 +pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
 +pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
 +pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
++pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
 +#[cfg(feature = "internal-lints")]
 +pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
 +#[cfg(feature = "internal-lints")]
 +pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
 +pub const LIBC_STRLEN: [&str; 2] = ["libc", "strlen"];
 +#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))]
 +pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
 +pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
 +pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
 +pub const MEM_MANUALLY_DROP: [&str; 4] = ["core", "mem", "manually_drop", "ManuallyDrop"];
 +pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"];
 +pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"];
 +pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"];
 +pub const MEM_SIZE_OF: [&str; 3] = ["core", "mem", "size_of"];
 +pub const MEM_SIZE_OF_VAL: [&str; 3] = ["core", "mem", "size_of_val"];
 +pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"];
 +pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
 +pub const OPS_MODULE: [&str; 2] = ["core", "ops"];
 +/// Preferably use the diagnostic item `sym::option_type` where possible
 +pub const OPTION: [&str; 3] = ["core", "option", "Option"];
 +pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"];
 +pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
 +pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
 +pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
 +pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
 +pub(super) const PANICKING_PANIC: [&str; 3] = ["core", "panicking", "panic"];
 +pub(super) const PANICKING_PANIC_FMT: [&str; 3] = ["core", "panicking", "panic_fmt"];
 +pub(super) const PANICKING_PANIC_STR: [&str; 3] = ["core", "panicking", "panic_str"];
 +pub(super) const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"];
 +pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"];
 +pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 2] = ["parking_lot", "RwLockReadGuard"];
 +pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWriteGuard"];
 +pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
 +pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
 +pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
 +pub const PERMISSIONS_FROM_MODE: [&str; 7] = ["std", "os", "imp", "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_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"];
 +pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
 +pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
 +pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
 +pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
 +pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
 +pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
 +/// Preferably use the diagnostic item `sym::result_type` where possible
 +pub const RESULT: [&str; 3] = ["core", "result", "Result"];
 +pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
 +pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
 +pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"];
 +pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
 +pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
 +pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
 +pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
 +pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"];
 +pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
 +pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
 +pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
 +pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
 +pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
 +pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
 +pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
 +pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
 +pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
 +pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
 +pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
 +pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
 +#[cfg(feature = "internal-lints")]
 +pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
 +#[cfg(feature = "internal-lints")]
 +pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
 +#[cfg(feature = "internal-lints")]
 +pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
 +#[cfg(feature = "internal-lints")]
 +pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
 +#[cfg(feature = "internal-lints")]
 +pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
 +#[cfg(feature = "internal-lints")]
 +pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
 +pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
 +pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
 +pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
 +pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
 +pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
 +pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
 +pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
 +pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
 +pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
 +pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
index e5bbf75c3b0a1857ed5b7c4580cb8e1d973ce587,0000000000000000000000000000000000000000..6cb2bd7f6efba33928d8aae0c09aba3ccbd4071b
mode 100644,000000..100644
--- /dev/null
@@@ -1,385 -1,0 +1,383 @@@
-                 ty::PredicateKind::Coerce(_) => {
-                     panic!("coerce predicate on function: {:#?}", predicate)
-                 },
 +// This code used to be a part of `rustc` but moved to Clippy as a result of
 +// https://github.com/rust-lang/rust/issues/76618. Because of that, it contains unused code and some
 +// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
 +// differ from the time of `rustc` even if the name stays the same.
 +
 +use rustc_hir as hir;
 +use rustc_hir::def_id::DefId;
 +use rustc_middle::mir::{
 +    Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
 +    TerminatorKind,
 +};
 +use rustc_middle::ty::subst::GenericArgKind;
 +use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
 +use rustc_semver::RustcVersion;
 +use rustc_span::symbol::sym;
 +use rustc_span::Span;
 +use rustc_target::spec::abi::Abi::RustIntrinsic;
 +use std::borrow::Cow;
 +
 +type McfResult = Result<(), (Span, Cow<'static, str>)>;
 +
 +pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult {
 +    let def_id = body.source.def_id();
 +    let mut current = def_id;
 +    loop {
 +        let predicates = tcx.predicates_of(current);
 +        for (predicate, _) in predicates.predicates {
 +            match predicate.kind().skip_binder() {
 +                ty::PredicateKind::RegionOutlives(_)
 +                | ty::PredicateKind::TypeOutlives(_)
 +                | ty::PredicateKind::WellFormed(_)
 +                | ty::PredicateKind::Projection(_)
 +                | ty::PredicateKind::ConstEvaluatable(..)
 +                | ty::PredicateKind::ConstEquate(..)
 +                | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
 +                ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
 +                ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate),
 +                ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate),
++                ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {:#?}", predicate),
 +                ty::PredicateKind::Trait(pred) => {
 +                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
 +                        continue;
 +                    }
 +                    match pred.self_ty().kind() {
 +                        ty::Param(ref p) => {
 +                            let generics = tcx.generics_of(current);
 +                            let def = generics.type_param(p, tcx);
 +                            let span = tcx.def_span(def.def_id);
 +                            return Err((
 +                                span,
 +                                "trait bounds other than `Sized` \
 +                                 on const fn parameters are unstable"
 +                                    .into(),
 +                            ));
 +                        },
 +                        // other kinds of bounds are either tautologies
 +                        // or cause errors in other passes
 +                        _ => continue,
 +                    }
 +                },
 +            }
 +        }
 +        match predicates.parent {
 +            Some(parent) => current = parent,
 +            None => break,
 +        }
 +    }
 +
 +    for local in &body.local_decls {
 +        check_ty(tcx, local.ty, local.source_info.span)?;
 +    }
 +    // impl trait is gone in MIR, so check the return type manually
 +    check_ty(
 +        tcx,
 +        tcx.fn_sig(def_id).output().skip_binder(),
 +        body.local_decls.iter().next().unwrap().source_info.span,
 +    )?;
 +
 +    for bb in body.basic_blocks() {
 +        check_terminator(tcx, body, bb.terminator(), msrv)?;
 +        for stmt in &bb.statements {
 +            check_statement(tcx, body, def_id, stmt)?;
 +        }
 +    }
 +    Ok(())
 +}
 +
 +fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
 +    for arg in ty.walk(tcx) {
 +        let ty = match arg.unpack() {
 +            GenericArgKind::Type(ty) => ty,
 +
 +            // No constraints on lifetimes or constants, except potentially
 +            // constants' types, but `walk` will get to them as well.
 +            GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
 +        };
 +
 +        match ty.kind() {
 +            ty::Ref(_, _, hir::Mutability::Mut) => {
 +                return Err((span, "mutable references in const fn are unstable".into()));
 +            },
 +            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
 +            ty::FnPtr(..) => {
 +                return Err((span, "function pointers in const fn are unstable".into()));
 +            },
 +            ty::Dynamic(preds, _) => {
 +                for pred in preds.iter() {
 +                    match pred.skip_binder() {
 +                        ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
 +                            return Err((
 +                                span,
 +                                "trait bounds other than `Sized` \
 +                                 on const fn parameters are unstable"
 +                                    .into(),
 +                            ));
 +                        },
 +                        ty::ExistentialPredicate::Trait(trait_ref) => {
 +                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
 +                                return Err((
 +                                    span,
 +                                    "trait bounds other than `Sized` \
 +                                     on const fn parameters are unstable"
 +                                        .into(),
 +                                ));
 +                            }
 +                        },
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +    Ok(())
 +}
 +
 +fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult {
 +    match rvalue {
 +        Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
 +        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
 +        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
 +            check_place(tcx, *place, span, body)
 +        },
 +        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
 +            use rustc_middle::ty::cast::CastTy;
 +            let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
 +            let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
 +            match (cast_in, cast_out) {
 +                (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
 +                    Err((span, "casting pointers to ints is unstable in const fn".into()))
 +                },
 +                _ => check_operand(tcx, operand, span, body),
 +            }
 +        },
 +        Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => {
 +            check_operand(tcx, operand, span, body)
 +        },
 +        Rvalue::Cast(
 +            CastKind::Pointer(
 +                PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
 +            ),
 +            _,
 +            _,
 +        ) => Err((span, "function pointer casts are not allowed in const fn".into())),
 +        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
 +            let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
 +                deref_ty.ty
 +            } else {
 +                // We cannot allow this for now.
 +                return Err((span, "unsizing casts are only allowed for references right now".into()));
 +            };
 +            let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
 +            if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
 +                check_operand(tcx, op, span, body)?;
 +                // Casting/coercing things to slices is fine.
 +                Ok(())
 +            } else {
 +                // We just can't allow trait objects until we have figured out trait method calls.
 +                Err((span, "unsizing casts are not allowed in const fn".into()))
 +            }
 +        },
 +        // binops are fine on integers
 +        Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
 +            check_operand(tcx, lhs, span, body)?;
 +            check_operand(tcx, rhs, span, body)?;
 +            let ty = lhs.ty(body, tcx);
 +            if ty.is_integral() || ty.is_bool() || ty.is_char() {
 +                Ok(())
 +            } else {
 +                Err((
 +                    span,
 +                    "only int, `bool` and `char` operations are stable in const fn".into(),
 +                ))
 +            }
 +        },
 +        Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
 +        Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())),
 +        Rvalue::UnaryOp(_, operand) => {
 +            let ty = operand.ty(body, tcx);
 +            if ty.is_integral() || ty.is_bool() {
 +                check_operand(tcx, operand, span, body)
 +            } else {
 +                Err((span, "only int and `bool` operations are stable in const fn".into()))
 +            }
 +        },
 +        Rvalue::Aggregate(_, operands) => {
 +            for operand in operands {
 +                check_operand(tcx, operand, span, body)?;
 +            }
 +            Ok(())
 +        },
 +    }
 +}
 +
 +fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult {
 +    let span = statement.source_info.span;
 +    match &statement.kind {
 +        StatementKind::Assign(box (place, rval)) => {
 +            check_place(tcx, *place, span, body)?;
 +            check_rvalue(tcx, body, def_id, rval, span)
 +        },
 +
 +        StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body),
 +        // just an assignment
 +        StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),
 +
 +        StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
 +
 +        StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { dst, src, count }) => {
 +            check_operand(tcx, dst, span, body)?;
 +            check_operand(tcx, src, span, body)?;
 +            check_operand(tcx, count, span, body)
 +        },
 +        // These are all NOPs
 +        StatementKind::StorageLive(_)
 +        | StatementKind::StorageDead(_)
 +        | StatementKind::Retag { .. }
 +        | StatementKind::AscribeUserType(..)
 +        | StatementKind::Coverage(..)
 +        | StatementKind::Nop => Ok(()),
 +    }
 +}
 +
 +fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
 +    match operand {
 +        Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
 +        Operand::Constant(c) => match c.check_static_ptr(tcx) {
 +            Some(_) => Err((span, "cannot access `static` items in const fn".into())),
 +            None => Ok(()),
 +        },
 +    }
 +}
 +
 +fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
 +    let mut cursor = place.projection.as_ref();
 +    while let [ref proj_base @ .., elem] = *cursor {
 +        cursor = proj_base;
 +        match elem {
 +            ProjectionElem::Field(..) => {
 +                let base_ty = Place::ty_from(place.local, proj_base, body, tcx).ty;
 +                if let Some(def) = base_ty.ty_adt_def() {
 +                    // No union field accesses in `const fn`
 +                    if def.is_union() {
 +                        return Err((span, "accessing union fields is unstable".into()));
 +                    }
 +                }
 +            },
 +            ProjectionElem::ConstantIndex { .. }
 +            | ProjectionElem::Downcast(..)
 +            | ProjectionElem::Subslice { .. }
 +            | ProjectionElem::Deref
 +            | ProjectionElem::Index(_) => {},
 +        }
 +    }
 +
 +    Ok(())
 +}
 +
 +fn check_terminator(
 +    tcx: TyCtxt<'tcx>,
 +    body: &'a Body<'tcx>,
 +    terminator: &Terminator<'tcx>,
 +    msrv: Option<&RustcVersion>,
 +) -> McfResult {
 +    let span = terminator.source_info.span;
 +    match &terminator.kind {
 +        TerminatorKind::FalseEdge { .. }
 +        | TerminatorKind::FalseUnwind { .. }
 +        | TerminatorKind::Goto { .. }
 +        | TerminatorKind::Return
 +        | TerminatorKind::Resume
 +        | TerminatorKind::Unreachable => Ok(()),
 +
 +        TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
 +        TerminatorKind::DropAndReplace { place, value, .. } => {
 +            check_place(tcx, *place, span, body)?;
 +            check_operand(tcx, value, span, body)
 +        },
 +
 +        TerminatorKind::SwitchInt {
 +            discr,
 +            switch_ty: _,
 +            targets: _,
 +        } => check_operand(tcx, discr, span, body),
 +
 +        TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
 +        TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
 +            Err((span, "const fn generators are unstable".into()))
 +        },
 +
 +        TerminatorKind::Call {
 +            func,
 +            args,
 +            from_hir_call: _,
 +            destination: _,
 +            cleanup: _,
 +            fn_span: _,
 +        } => {
 +            let fn_ty = func.ty(body, tcx);
 +            if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
 +                if !is_const_fn(tcx, fn_def_id, msrv) {
 +                    return Err((
 +                        span,
 +                        format!(
 +                            "can only call other `const fn` within a `const fn`, \
 +                             but `{:?}` is not stable as `const fn`",
 +                            func,
 +                        )
 +                        .into(),
 +                    ));
 +                }
 +
 +                // HACK: This is to "unstabilize" the `transmute` intrinsic
 +                // within const fns. `transmute` is allowed in all other const contexts.
 +                // This won't really scale to more intrinsics or functions. Let's allow const
 +                // transmutes in const fn before we add more hacks to this.
 +                if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute {
 +                    return Err((
 +                        span,
 +                        "can only call `transmute` from const items, not `const fn`".into(),
 +                    ));
 +                }
 +
 +                check_operand(tcx, func, span, body)?;
 +
 +                for arg in args {
 +                    check_operand(tcx, arg, span, body)?;
 +                }
 +                Ok(())
 +            } else {
 +                Err((span, "can only call other const fns within const fn".into()))
 +            }
 +        },
 +
 +        TerminatorKind::Assert {
 +            cond,
 +            expected: _,
 +            msg: _,
 +            target: _,
 +            cleanup: _,
 +        } => check_operand(tcx, cond, span, body),
 +
 +        TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
 +    }
 +}
 +
 +fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool {
 +    rustc_mir::const_eval::is_const_fn(tcx, def_id)
 +        && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
 +            if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level {
 +                // Checking MSRV is manually necessary because `rustc` has no such concept. This entire
 +                // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`.
 +                // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
 +                crate::meets_msrv(
 +                    msrv,
 +                    &RustcVersion::parse(&since.as_str())
 +                        .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"),
 +                )
 +            } else {
 +                // Unstable const fn with the feature enabled.
 +                msrv.is_none()
 +            }
 +        })
 +}
index 65d93e8f86e43f1c7953d83f45721bb9add27c43,0000000000000000000000000000000000000000..ab05a0b423853f245ca00b5d2f940383da0d8ef9
mode 100644,000000..100644
--- /dev/null
@@@ -1,746 -1,0 +1,746 @@@
- // Copied from the rust standart library, and then edited
 +//! Contains utility functions to generate suggestions.
 +#![deny(clippy::missing_docs_in_private_items)]
 +
 +use crate::higher;
 +use crate::source::{snippet, snippet_opt, snippet_with_context, snippet_with_macro_callsite};
 +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_lint::{EarlyContext, LateContext, LintContext};
 +use rustc_span::source_map::{CharPos, Span};
 +use rustc_span::{BytePos, Pos, SyntaxContext};
 +use std::borrow::Cow;
 +use std::convert::TryInto;
 +use std::fmt::Display;
 +use std::ops::{Add, Neg, Not, Sub};
 +
 +/// A helper type to build suggestion correctly handling parenthesis.
 +#[derive(Clone, PartialEq)]
 +pub enum Sugg<'a> {
 +    /// An expression that never needs parenthesis 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>),
 +}
 +
 +/// 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) | Sugg::BinOp(_, ref s) => s.fmt(f),
 +        }
 +    }
 +}
 +
 +#[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method
 +impl<'a> Sugg<'a> {
 +    /// Prepare a suggestion from an expression.
 +    pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Self> {
 +        snippet_opt(cx, expr.span).map(|snippet| {
 +            let snippet = Cow::Owned(snippet);
 +            Self::hir_from_snippet(expr, 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 snippet = snippet_with_macro_callsite(cx, expr.span, default);
 +
 +        Self::hir_from_snippet(expr, 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 {
 +        let (snippet, in_macro) = snippet_with_context(cx, expr.span, ctxt, default, applicability);
 +
 +        if in_macro {
 +            Sugg::NonParen(snippet)
 +        } else {
 +            Self::hir_from_snippet(expr, snippet)
 +        }
 +    }
 +
 +    /// 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<'_>, snippet: 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,
 +            };
 +            return Sugg::BinOp(op, snippet);
 +        }
 +
 +        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(snippet),
 +            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::LlvmInlineAsm(..)
 +            | 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(snippet),
 +            hir::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
 +            hir::ExprKind::AssignOp(op, ..) => Sugg::BinOp(hirbinop2assignop(op), snippet),
 +            hir::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(op.node.into()), snippet),
 +            hir::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
 +            hir::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
 +        }
 +    }
 +
 +    /// Prepare a suggestion from an expression.
 +    pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
 +        use rustc_ast::ast::RangeLimits;
 +
 +        let snippet = 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(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::LlvmInlineAsm(..)
 +            | 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::Struct(..)
 +            | ast::ExprKind::Try(..)
 +            | ast::ExprKind::TryBlock(..)
 +            | ast::ExprKind::Tup(..)
 +            | ast::ExprKind::Array(..)
 +            | ast::ExprKind::While(..)
 +            | ast::ExprKind::Await(..)
 +            | ast::ExprKind::Err => Sugg::NonParen(snippet),
 +            ast::ExprKind::Range(.., RangeLimits::HalfOpen) => Sugg::BinOp(AssocOp::DotDot, snippet),
 +            ast::ExprKind::Range(.., RangeLimits::Closed) => Sugg::BinOp(AssocOp::DotDotEq, snippet),
 +            ast::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
 +            ast::ExprKind::AssignOp(op, ..) => Sugg::BinOp(astbinop2assignop(op), snippet),
 +            ast::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(op.node), snippet),
 +            ast::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
 +            ast::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
 +        }
 +    }
 +
 +    /// Convenience method to create the `<lhs> && <rhs>` suggestion.
 +    pub fn and(self, rhs: &Self) -> Sugg<'static> {
 +        make_binop(ast::BinOpKind::And, &self, rhs)
 +    }
 +
 +    /// Convenience method to create the `<lhs> & <rhs>` suggestion.
 +    pub fn bit_and(self, rhs: &Self) -> Sugg<'static> {
 +        make_binop(ast::BinOpKind::BitAnd, &self, rhs)
 +    }
 +
 +    /// Convenience method to create the `<lhs> as <rhs>` suggestion.
 +    pub fn as_ty<R: Display>(self, rhs: R) -> Sugg<'static> {
 +        make_assoc(AssocOp::As, &self, &Sugg::NonParen(rhs.to_string().into()))
 +    }
 +
 +    /// Convenience method to create the `&<expr>` suggestion.
 +    pub fn addr(self) -> Sugg<'static> {
 +        make_unop("&", self)
 +    }
 +
 +    /// Convenience method to create the `&mut <expr>` suggestion.
 +    pub fn mut_addr(self) -> Sugg<'static> {
 +        make_unop("&mut ", self)
 +    }
 +
 +    /// Convenience method to create the `*<expr>` suggestion.
 +    pub fn deref(self) -> Sugg<'static> {
 +        make_unop("*", self)
 +    }
 +
 +    /// Convenience method to create the `&*<expr>` suggestion. Currently this
 +    /// is needed because `sugg.deref().addr()` produces an unnecessary set of
 +    /// parentheses around the deref.
 +    pub fn addr_deref(self) -> Sugg<'static> {
 +        make_unop("&*", self)
 +    }
 +
 +    /// Convenience method to create the `&mut *<expr>` suggestion. Currently
 +    /// this is needed because `sugg.deref().mut_addr()` produces an unnecessary
 +    /// set of parentheses around the deref.
 +    pub fn mut_addr_deref(self) -> Sugg<'static> {
 +        make_unop("&mut *", self)
 +    }
 +
 +    /// Convenience method to transform suggestion into a return call
 +    pub fn make_return(self) -> Sugg<'static> {
 +        Sugg::NonParen(Cow::Owned(format!("return {}", self)))
 +    }
 +
 +    /// Convenience method to transform suggestion into a block
 +    /// where the suggestion is a trailing expression
 +    pub fn blockify(self) -> Sugg<'static> {
 +        Sugg::NonParen(Cow::Owned(format!("{{ {} }}", self)))
 +    }
 +
 +    /// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>`
 +    /// suggestion.
 +    #[allow(dead_code)]
 +    pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> {
 +        match limit {
 +            ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, end),
 +            ast::RangeLimits::Closed => make_assoc(AssocOp::DotDotEq, &self, end),
 +        }
 +    }
 +
 +    /// Adds parenthesis 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()`).
 +    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(_, sugg) => {
 +                if has_enclosing_paren(&sugg) {
 +                    Sugg::NonParen(sugg)
 +                } else {
 +                    Sugg::NonParen(format!("({})", sugg).into())
 +                }
 +            },
 +        }
 +    }
 +}
 +
 +/// Return `true` if `sugg` is enclosed in parenthesis.
 +fn has_enclosing_paren(sugg: impl AsRef<str>) -> bool {
 +    let mut chars = sugg.as_ref().chars();
 +    if let Some('(') = chars.next() {
 +        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 Not for Sugg<'_> {
 +    type Output = Sugg<'static>;
 +    fn not(self) -> Sugg<'static> {
 +        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);
 +    let rhs = ParenHelper::new(rhs_paren, rhs);
 +    let sugg = match op {
 +        AssocOp::Add
 +        | AssocOp::BitAnd
 +        | AssocOp::BitOr
 +        | AssocOp::BitXor
 +        | AssocOp::Divide
 +        | AssocOp::Equal
 +        | AssocOp::Greater
 +        | AssocOp::GreaterEqual
 +        | AssocOp::LAnd
 +        | AssocOp::LOr
 +        | AssocOp::Less
 +        | AssocOp::LessEqual
 +        | AssocOp::Modulus
 +        | AssocOp::Multiply
 +        | AssocOp::NotEqual
 +        | AssocOp::ShiftLeft
 +        | AssocOp::ShiftRight
 +        | AssocOp::Subtract => 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),
 +    };
 +
 +    Sugg::BinOp(op, sugg.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 `DiagnosticBuilder`.
 +pub trait DiagnosticBuilderExt<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> DiagnosticBuilderExt<T> for rustc_errors::DiagnosticBuilder<'_> {
 +    fn suggest_item_with_attr<D: Display + ?Sized>(
 +        &mut self,
 +        cx: &T,
 +        item: Span,
 +        msg: &str,
 +        attr: &D,
 +        applicability: Applicability,
 +    ) {
 +        if let Some(indent) = indentation(cx, item) {
 +            let span = item.with_hi(item.lo());
 +
 +            self.span_suggestion(span, msg, format!("{}\n{}", attr, indent), applicability);
 +        }
 +    }
 +
 +    fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability) {
 +        if let Some(indent) = indentation(cx, item) {
 +            let span = item.with_hi(item.lo());
 +
 +            let mut first = true;
 +            let new_item = new_item
 +                .lines()
 +                .map(|l| {
 +                    if first {
 +                        first = false;
 +                        format!("{}\n", l)
 +                    } else {
 +                        format!("{}{}\n", indent, l)
 +                    }
 +                })
 +                .collect::<String>();
 +
 +            self.span_suggestion(span, msg, format!("{}\n{}", new_item, indent), applicability);
 +        }
 +    }
 +
 +    fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability) {
 +        let mut remove_span = item;
 +        let hi = cx.sess().source_map().next_point(remove_span).hi();
 +        let fmpos = cx.sess().source_map().lookup_byte_offset(hi);
 +
 +        if let Some(ref src) = fmpos.sf.src {
 +            let non_whitespace_offset = src[fmpos.pos.to_usize()..].find(|c| c != ' ' && c != '\t' && c != '\n');
 +
 +            if let Some(non_whitespace_offset) = non_whitespace_offset {
 +                remove_span = remove_span
 +                    .with_hi(remove_span.hi() + BytePos(non_whitespace_offset.try_into().expect("offset too large")));
 +            }
 +        }
 +
 +        self.span_suggestion(remove_span, msg, String::new(), applicability);
 +    }
 +}
 +
 +#[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 + 1)".into());
 +        assert_eq!("(1 + 1)", sugg.maybe_par().to_string());
 +
 +        let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1) + (1 + 1)".into());
 +        assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_par().to_string());
 +    }
 +}
index 3cd8ed5aa2c8c50ef32a129d0879f5428883969f,0000000000000000000000000000000000000000..d6f9ebe89bc7fe44136d09231399edcde1f9b73a
mode 100644,000000..100644
--- /dev/null
@@@ -1,362 -1,0 +1,369 @@@
- use rustc_middle::ty::{self, TyCtxt, AdtDef, IntTy, Ty, TypeFoldable, UintTy};
 +//! Util methods for [`rustc_middle::ty`]
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use rustc_ast::ast::Mutability;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir as hir;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{TyKind, Unsafety};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
++use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, TypeFoldable, UintTy};
 +use rustc_span::sym;
 +use rustc_span::symbol::{Ident, Symbol};
 +use rustc_span::DUMMY_SP;
 +use rustc_trait_selection::infer::InferCtxtExt;
 +use rustc_trait_selection::traits::query::normalize::AtExt;
 +
 +use crate::{match_def_path, must_use_attr};
 +
 +pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env)
 +}
 +
 +/// Checks whether a type can be partially moved.
 +pub fn can_partially_move_ty(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    if has_drop(cx, ty) || is_copy(cx, ty) {
 +        return false;
 +    }
 +    match ty.kind() {
 +        ty::Param(_) => false,
 +        ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))),
 +        _ => true,
 +    }
 +}
 +
 +/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
 +pub fn contains_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, other_ty: Ty<'tcx>) -> bool {
 +    ty.walk(tcx).any(|inner| match inner.unpack() {
 +        GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty),
 +        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
 +    })
 +}
 +
 +/// Walks into `ty` and returns `true` if any inner type is an instance of the given adt
 +/// constructor.
 +pub fn contains_adt_constructor<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, adt: &'tcx AdtDef) -> bool {
 +    ty.walk(tcx).any(|inner| match inner.unpack() {
 +        GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt),
 +        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
 +    })
 +}
 +
 +/// Resolves `<T as Iterator>::Item` for `T`
 +/// Do not invoke without first verifying that the type implements `Iterator`
 +pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
 +    cx.tcx
 +        .get_diagnostic_item(sym::Iterator)
 +        .and_then(|iter_did| {
 +            cx.tcx.associated_items(iter_did).find_by_name_and_kind(
 +                cx.tcx,
 +                Ident::from_str("Item"),
 +                ty::AssocKind::Type,
 +                iter_did,
 +            )
 +        })
 +        .map(|assoc| {
 +            let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[]));
 +            cx.tcx.normalize_erasing_regions(cx.param_env, proj)
 +        })
 +}
 +
 +/// Returns true if ty has `iter` or `iter_mut` methods
 +pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
 +    // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
 +    // exists and has the desired signature. Unfortunately FnCtxt is not exported
 +    // so we can't use its `lookup_method` method.
 +    let into_iter_collections: &[Symbol] = &[
 +        sym::vec_type,
 +        sym::option_type,
 +        sym::result_type,
 +        sym::BTreeMap,
 +        sym::BTreeSet,
 +        sym::vecdeque_type,
 +        sym::LinkedList,
 +        sym::BinaryHeap,
 +        sym::hashset_type,
 +        sym::hashmap_type,
 +        sym::PathBuf,
 +        sym::Path,
 +        sym::Receiver,
 +    ];
 +
 +    let ty_to_check = match probably_ref_ty.kind() {
 +        ty::Ref(_, ty_to_check, _) => ty_to_check,
 +        _ => probably_ref_ty,
 +    };
 +
 +    let def_id = match ty_to_check.kind() {
 +        ty::Array(..) => return Some(sym::array),
 +        ty::Slice(..) => return Some(sym::slice),
 +        ty::Adt(adt, _) => adt.did,
 +        _ => return None,
 +    };
 +
 +    for &name in into_iter_collections {
 +        if cx.tcx.is_diagnostic_item(name, def_id) {
 +            return Some(cx.tcx.item_name(def_id));
 +        }
 +    }
 +    None
 +}
 +
 +/// Checks whether a type implements a trait.
 +/// The function returns false in case the type contains an inference variable.
 +/// See also [`get_trait_def_id`](super::get_trait_def_id).
 +pub fn implements_trait<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: Ty<'tcx>,
 +    trait_id: DefId,
 +    ty_params: &[GenericArg<'tcx>],
 +) -> bool {
 +    // Clippy shouldn't have infer types
 +    assert!(!ty.needs_infer());
 +
 +    let ty = cx.tcx.erase_regions(ty);
 +    if ty.has_escaping_bound_vars() {
 +        return false;
 +    }
 +    let ty_params = cx.tcx.mk_substs(ty_params.iter());
 +    cx.tcx.infer_ctxt().enter(|infcx| {
 +        infcx
 +            .type_implements_trait(trait_id, ty, ty_params, cx.param_env)
 +            .must_apply_modulo_regions()
 +    })
 +}
 +
 +/// Checks whether this type implements `Drop`.
 +pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    match ty.ty_adt_def() {
 +        Some(def) => def.has_dtor(cx.tcx),
 +        None => false,
 +    }
 +}
 +
 +// Returns whether the type has #[must_use] attribute
 +pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => must_use_attr(cx.tcx.get_attrs(adt.did)).is_some(),
 +        ty::Foreign(ref did) => must_use_attr(cx.tcx.get_attrs(*did)).is_some(),
 +        ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => {
 +            // for the Array case we don't need to care for the len == 0 case
 +            // because we don't want to lint functions returning empty arrays
 +            is_must_use_ty(cx, *ty)
 +        },
 +        ty::Tuple(substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)),
 +        ty::Opaque(ref def_id, _) => {
 +            for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
 +                if let ty::PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() {
 +                    if must_use_attr(cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() {
 +                        return true;
 +                    }
 +                }
 +            }
 +            false
 +        },
 +        ty::Dynamic(binder, _) => {
 +            for predicate in binder.iter() {
 +                if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
 +                    if must_use_attr(cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
 +                        return true;
 +                    }
 +                }
 +            }
 +            false
 +        },
 +        _ => false,
 +    }
 +}
 +
 +// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
 +// this function can be removed once the `normalize` method does not panic when normalization does
 +// not succeed
 +/// Checks if `Ty` is normalizable. This function is useful
 +/// to avoid crashes on `layout_of`.
 +pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
 +    is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default())
 +}
 +
 +fn is_normalizable_helper<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    param_env: ty::ParamEnv<'tcx>,
 +    ty: Ty<'tcx>,
 +    cache: &mut FxHashMap<Ty<'tcx>, bool>,
 +) -> bool {
 +    if let Some(&cached_result) = cache.get(ty) {
 +        return cached_result;
 +    }
 +    // prevent recursive loops, false-negative is better than endless loop leading to stack overflow
 +    cache.insert(ty, false);
 +    let result = cx.tcx.infer_ctxt().enter(|infcx| {
 +        let cause = rustc_middle::traits::ObligationCause::dummy();
 +        if infcx.at(&cause, param_env).normalize(ty).is_ok() {
 +            match ty.kind() {
 +                ty::Adt(def, substs) => def.variants.iter().all(|variant| {
 +                    variant
 +                        .fields
 +                        .iter()
 +                        .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
 +                }),
 +                _ => ty.walk(cx.tcx).all(|generic_arg| match generic_arg.unpack() {
 +                    GenericArgKind::Type(inner_ty) if inner_ty != ty => {
 +                        is_normalizable_helper(cx, param_env, inner_ty, cache)
 +                    },
 +                    _ => true, // if inner_ty == ty, we've already checked it
 +                }),
 +            }
 +        } else {
 +            false
 +        }
 +    });
 +    cache.insert(ty, result);
 +    result
 +}
 +
++/// Returns true iff 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 iff the given type is a primitive (a bool or char, any integer or floating-point
 +/// number type, a str, or an array, slice, or tuple of those types).
 +pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
 +    match ty.kind() {
 +        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
 +        ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
 +        ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
 +        ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type),
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the type is a reference equals to a diagnostic item
 +pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
 +    match ty.kind() {
 +        ty::Ref(_, ref_ty, _) => match ref_ty.kind() {
 +            ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
 +            _ => false,
 +        },
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the type is equal to a diagnostic item
 +///
 +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
 +pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the type is equal to a lang item.
 +///
 +/// Returns `false` if the `LangItem` is not defined.
 +pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => cx.tcx.lang_items().require(lang_item).map_or(false, |li| li == adt.did),
 +        _ => false,
 +    }
 +}
 +
 +/// Return `true` if the passed `typ` is `isize` or `usize`.
 +pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
 +    matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
 +}
 +
 +/// Checks if type is struct, enum or union type with the given def path.
 +///
 +/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
 +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
 +pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => match_def_path(cx, adt.did, path),
 +        _ => false,
 +    }
 +}
 +
 +/// Peels off all references on the type. Returns the underlying type and the number of references
 +/// removed.
 +pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
 +    fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
 +        if let ty::Ref(_, ty, _) = ty.kind() {
 +            peel(ty, count + 1)
 +        } else {
 +            (ty, count)
 +        }
 +    }
 +    peel(ty, 0)
 +}
 +
 +/// Peels off all references on the type.Returns the underlying type, the number of references
 +/// removed, and whether the pointer is ultimately mutable or not.
 +pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
 +    fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
 +        match ty.kind() {
 +            ty::Ref(_, ty, Mutability::Mut) => f(ty, count + 1, mutability),
 +            ty::Ref(_, ty, Mutability::Not) => f(ty, count + 1, Mutability::Not),
 +            _ => (ty, count, mutability),
 +        }
 +    }
 +    f(ty, 0, Mutability::Mut)
 +}
 +
 +/// Returns `true` if the given type is an `unsafe` function.
 +pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    match ty.kind() {
 +        ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
 +        _ => false,
 +    }
 +}
 +
 +/// Returns the base type for HIR references and pointers.
 +pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
 +    match ty.kind {
 +        TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty),
 +        _ => ty,
 +    }
 +}
 +
 +/// Returns the base type for references and raw pointers, and count reference
 +/// depth.
 +pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
 +    fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
 +        match ty.kind() {
 +            ty::Ref(_, ty, _) => inner(ty, depth + 1),
 +            _ => (ty, depth),
 +        }
 +    }
 +    inner(ty, 0)
 +}
 +
 +/// Returns `true` if types `a` and `b` are same types having same `Const` generic args,
 +/// otherwise returns `false`
 +pub fn same_type_and_consts(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
 +    match (&a.kind(), &b.kind()) {
 +        (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => {
 +            if did_a != did_b {
 +                return false;
 +            }
 +
 +            substs_a
 +                .iter()
 +                .zip(substs_b.iter())
 +                .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) {
 +                    (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b,
 +                    (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => {
 +                        same_type_and_consts(type_a, type_b)
 +                    },
 +                    _ => true,
 +                })
 +        },
 +        _ => a == b,
 +    }
 +}
index ac885e9994404b605dd5b0d1c91df3cc45c6b970,0000000000000000000000000000000000000000..098ec175fe2d91fcf530df7b6885c81d64e42fa1
mode 100644,000000..100644
--- /dev/null
@@@ -1,248 -1,0 +1,243 @@@
- use rustc_hir::def::Res;
 +use crate as utils;
 +use rustc_hir as hir;
- use rustc_hir::{Expr, ExprKind, HirId, Path};
 +use rustc_hir::intravisit;
 +use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
 +use rustc_hir::HirIdSet;
- pub fn is_potentially_mutated<'tcx>(variable: &'tcx Path<'_>, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
-     if let Res::Local(id) = variable.res {
-         mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&id))
-     } else {
-         true
-     }
++use rustc_hir::{Expr, ExprKind, HirId};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::mir::FakeReadCause;
 +use rustc_middle::ty;
 +use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 +
 +/// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined.
 +pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option<HirIdSet> {
 +    let mut delegate = MutVarsDelegate {
 +        used_mutably: HirIdSet::default(),
 +        skip: false,
 +    };
 +    cx.tcx.infer_ctxt().enter(|infcx| {
 +        ExprUseVisitor::new(
 +            &mut delegate,
 +            &infcx,
 +            expr.hir_id.owner,
 +            cx.param_env,
 +            cx.typeck_results(),
 +        )
 +        .walk_expr(expr);
 +    });
 +
 +    if delegate.skip {
 +        return None;
 +    }
 +    Some(delegate.used_mutably)
 +}
 +
++pub fn is_potentially_mutated<'tcx>(variable: HirId, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
++    mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&variable))
 +}
 +
 +struct MutVarsDelegate {
 +    used_mutably: HirIdSet,
 +    skip: bool,
 +}
 +
 +impl<'tcx> MutVarsDelegate {
 +    #[allow(clippy::similar_names)]
 +    fn update(&mut self, cat: &PlaceWithHirId<'tcx>) {
 +        match cat.place.base {
 +            PlaceBase::Local(id) => {
 +                self.used_mutably.insert(id);
 +            },
 +            PlaceBase::Upvar(_) => {
 +                //FIXME: This causes false negatives. We can't get the `NodeId` from
 +                //`Categorization::Upvar(_)`. So we search for any `Upvar`s in the
 +                //`while`-body, not just the ones in the condition.
 +                self.skip = true;
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
 +    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
 +
 +    fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, bk: ty::BorrowKind) {
 +        if let ty::BorrowKind::MutBorrow = bk {
 +            self.update(cmt);
 +        }
 +    }
 +
 +    fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
 +        self.update(cmt);
 +    }
 +
 +    fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
 +}
 +
 +pub struct ParamBindingIdCollector {
 +    binding_hir_ids: Vec<hir::HirId>,
 +}
 +impl<'tcx> ParamBindingIdCollector {
 +    fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec<hir::HirId> {
 +        let mut hir_ids: Vec<hir::HirId> = Vec::new();
 +        for param in body.params.iter() {
 +            let mut finder = ParamBindingIdCollector {
 +                binding_hir_ids: Vec::new(),
 +            };
 +            finder.visit_param(param);
 +            for hir_id in &finder.binding_hir_ids {
 +                hir_ids.push(*hir_id);
 +            }
 +        }
 +        hir_ids
 +    }
 +}
 +impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
 +        if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind {
 +            self.binding_hir_ids.push(hir_id);
 +        }
 +        intravisit::walk_pat(self, pat);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
 +        intravisit::NestedVisitorMap::None
 +    }
 +}
 +
 +pub struct BindingUsageFinder<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    binding_ids: Vec<hir::HirId>,
 +    usage_found: bool,
 +}
 +impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> {
 +    pub fn are_params_used(cx: &'a LateContext<'tcx>, body: &'tcx hir::Body<'tcx>) -> bool {
 +        let mut finder = BindingUsageFinder {
 +            cx,
 +            binding_ids: ParamBindingIdCollector::collect_binding_hir_ids(body),
 +            usage_found: false,
 +        };
 +        finder.visit_body(body);
 +        finder.usage_found
 +    }
 +}
 +impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
 +        if !self.usage_found {
 +            intravisit::walk_expr(self, expr);
 +        }
 +    }
 +
 +    fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
 +        if let hir::def::Res::Local(id) = path.res {
 +            if self.binding_ids.contains(&id) {
 +                self.usage_found = true;
 +            }
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
 +        intravisit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
 +    }
 +}
 +
 +struct ReturnBreakContinueMacroVisitor {
 +    seen_return_break_continue: bool,
 +}
 +
 +impl ReturnBreakContinueMacroVisitor {
 +    fn new() -> ReturnBreakContinueMacroVisitor {
 +        ReturnBreakContinueMacroVisitor {
 +            seen_return_break_continue: false,
 +        }
 +    }
 +}
 +
 +impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor {
 +    type Map = Map<'tcx>;
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +
 +    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
 +        if self.seen_return_break_continue {
 +            // No need to look farther if we've already seen one of them
 +            return;
 +        }
 +        match &ex.kind {
 +            ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => {
 +                self.seen_return_break_continue = true;
 +            },
 +            // Something special could be done here to handle while or for loop
 +            // desugaring, as this will detect a break if there's a while loop
 +            // or a for loop inside the expression.
 +            _ => {
 +                if utils::in_macro(ex.span) {
 +                    self.seen_return_break_continue = true;
 +                } else {
 +                    rustc_hir::intravisit::walk_expr(self, ex);
 +                }
 +            },
 +        }
 +    }
 +}
 +
 +pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
 +    let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new();
 +    recursive_visitor.visit_expr(expression);
 +    recursive_visitor.seen_return_break_continue
 +}
 +
 +pub struct UsedAfterExprVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    definition: HirId,
 +    past_expr: bool,
 +    used_after_expr: bool,
 +}
 +impl<'a, 'tcx> UsedAfterExprVisitor<'a, 'tcx> {
 +    pub fn is_found(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
 +        utils::path_to_local(expr).map_or(false, |definition| {
 +            let mut visitor = UsedAfterExprVisitor {
 +                cx,
 +                expr,
 +                definition,
 +                past_expr: false,
 +                used_after_expr: false,
 +            };
 +            utils::get_enclosing_block(cx, definition).map_or(false, |block| {
 +                visitor.visit_block(block);
 +                visitor.used_after_expr
 +            })
 +        })
 +    }
 +}
 +
 +impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedAfterExprVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 +        if self.used_after_expr {
 +            return;
 +        }
 +
 +        if expr.hir_id == self.expr.hir_id {
 +            self.past_expr = true;
 +        } else if self.past_expr && utils::path_to_local_id(expr, self.definition) {
 +            self.used_after_expr = true;
 +        } else {
 +            intravisit::walk_expr(self, expr);
 +        }
 +    }
 +}
index ce00106dd4d8087afc501f234cfd442ca73c1047,0000000000000000000000000000000000000000..503effbdad5725069eb4ddc268a08c80ae7b9014
mode 100644,000000..100644
--- /dev/null
@@@ -1,262 -1,0 +1,267 @@@
- pub struct LocalUsedVisitor<'hir> {
-     hir: Map<'hir>,
-     pub local_hir_id: HirId,
-     pub used: bool,
- }
- impl<'hir> LocalUsedVisitor<'hir> {
-     pub fn new(cx: &LateContext<'hir>, local_hir_id: HirId) -> Self {
-         Self {
-             hir: cx.tcx.hir(),
-             local_hir_id,
-             used: false,
-         }
-     }
-     fn check<T>(&mut self, t: T, visit: fn(&mut Self, T)) -> bool {
-         visit(self, t);
-         std::mem::replace(&mut self.used, false)
-     }
-     pub fn check_arm(&mut self, arm: &'hir Arm<'_>) -> bool {
-         self.check(arm, Self::visit_arm)
-     }
-     pub fn check_body(&mut self, body: &'hir Body<'_>) -> bool {
-         self.check(body, Self::visit_body)
-     }
-     pub fn check_expr(&mut self, expr: &'hir Expr<'_>) -> bool {
-         self.check(expr, Self::visit_expr)
-     }
-     pub fn check_stmt(&mut self, stmt: &'hir Stmt<'_>) -> bool {
-         self.check(stmt, Self::visit_stmt)
-     }
- }
- impl<'v> Visitor<'v> for LocalUsedVisitor<'v> {
-     type Map = Map<'v>;
-     fn visit_expr(&mut self, expr: &'v Expr<'v>) {
-         if self.used {
-             return;
-         }
-         if path_to_local_id(expr, self.local_hir_id) {
-             self.used = true;
-         } else {
-             walk_expr(self, expr);
-         }
-     }
-     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-         NestedVisitorMap::OnlyBodies(self.hir)
-     }
- }
 +use crate::path_to_local_id;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
 +use rustc_hir::{def::Res, Arm, Block, Body, BodyId, Destination, Expr, ExprKind, HirId, Stmt};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::map::Map;
++use std::ops::ControlFlow;
 +
 +/// returns `true` if expr contains match expr desugared from try
 +fn contains_try(expr: &hir::Expr<'_>) -> bool {
 +    struct TryFinder {
 +        found: bool,
 +    }
 +
 +    impl<'hir> intravisit::Visitor<'hir> for TryFinder {
 +        type Map = Map<'hir>;
 +
 +        fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
 +            intravisit::NestedVisitorMap::None
 +        }
 +
 +        fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +            if self.found {
 +                return;
 +            }
 +            match expr.kind {
 +                hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true,
 +                _ => intravisit::walk_expr(self, expr),
 +            }
 +        }
 +    }
 +
 +    let mut visitor = TryFinder { found: false };
 +    visitor.visit_expr(expr);
 +    visitor.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> {
 +        type Map = Map<'hir>;
 +
 +        fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
 +            intravisit::NestedVisitorMap::None
 +        }
 +
 +        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 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);
++//         }
++//     }
++// }
 +
 +/// Calls the given function for each break expression.
 +pub fn visit_break_exprs<'tcx>(
 +    node: impl Visitable<'tcx>,
 +    f: impl FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>),
 +) {
 +    struct V<F>(F);
 +    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>)> Visitor<'tcx> for V<F> {
 +        type Map = ErasedMap<'tcx>;
 +        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +            NestedVisitorMap::None
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if let ExprKind::Break(dest, sub_expr) = e.kind {
 +                self.0(e, dest, sub_expr);
 +            }
 +            walk_expr(self, e);
 +        }
 +    }
 +
 +    node.visit(&mut V(f));
 +}
 +
 +/// Checks if the given resolved path is used in the given body.
 +pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
 +    struct V<'a, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        res: Res,
 +        found: bool,
 +    }
 +    impl Visitor<'tcx> for V<'_, 'tcx> {
 +        type Map = Map<'tcx>;
 +        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +            NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.found {
 +                return;
 +            }
 +
 +            if let ExprKind::Path(p) = &e.kind {
 +                if self.cx.qpath_res(p, e.hir_id) == self.res {
 +                    self.found = true;
 +                }
 +            } else {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +
 +    let mut v = V { cx, res, found: false };
 +    v.visit_expr(&cx.tcx.hir().body(body).value);
 +    v.found
 +}
++
++/// Calls the given function for each usage of the given local.
++pub fn for_each_local_usage<'tcx, B>(
++    cx: &LateContext<'tcx>,
++    visitable: impl Visitable<'tcx>,
++    id: HirId,
++    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
++) -> ControlFlow<B> {
++    struct V<'tcx, B, F> {
++        map: Map<'tcx>,
++        id: HirId,
++        f: F,
++        res: ControlFlow<B>,
++    }
++    impl<'tcx, B, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>> Visitor<'tcx> for V<'tcx, B, F> {
++        type Map = Map<'tcx>;
++        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++            NestedVisitorMap::OnlyBodies(self.map)
++        }
++
++        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
++            if self.res.is_continue() {
++                if path_to_local_id(e, self.id) {
++                    self.res = (self.f)(e);
++                } else {
++                    walk_expr(self, e);
++                }
++            }
++        }
++    }
++
++    let mut v = V {
++        map: cx.tcx.hir(),
++        id,
++        f,
++        res: ControlFlow::CONTINUE,
++    };
++    visitable.visit(&mut v);
++    v.res
++}
++
++/// Checks if the given local is used.
++pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
++    for_each_local_usage(cx, visitable, id, |_| ControlFlow::BREAK).is_break()
++}
index f2260c3d1a267dd4a1093cf198a49d83535ecec5,0000000000000000000000000000000000000000..004eb28b44640fb59ab3d43c31203ab97c622870
mode 100644,000000..100644
--- /dev/null
@@@ -1,668 -1,0 +1,669 @@@
- 1. Adding a new configuration entry to [clippy_utils::conf](/clippy_utils/src/conf.rs)
 +# Adding a new lint
 +
 +You are probably here because you want to add a new lint to Clippy. If this is
 +the first time you're contributing to Clippy, this document guides you through
 +creating an example lint from scratch.
 +
 +To get started, we will create a lint that detects functions called `foo`,
 +because that's clearly a non-descriptive name.
 +
 +- [Adding a new lint](#adding-a-new-lint)
 +  - [Setup](#setup)
 +  - [Getting Started](#getting-started)
 +  - [Testing](#testing)
 +    - [Cargo lints](#cargo-lints)
 +  - [Rustfix tests](#rustfix-tests)
 +  - [Edition 2018 tests](#edition-2018-tests)
 +  - [Testing manually](#testing-manually)
 +  - [Lint declaration](#lint-declaration)
 +  - [Lint passes](#lint-passes)
 +  - [Emitting a lint](#emitting-a-lint)
 +  - [Adding the lint logic](#adding-the-lint-logic)
 +  - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv)
 +  - [Author lint](#author-lint)
 +  - [Documentation](#documentation)
 +  - [Running rustfmt](#running-rustfmt)
 +  - [Debugging](#debugging)
 +  - [PR Checklist](#pr-checklist)
 +  - [Adding configuration to a lint](#adding-configuration-to-a-lint)
 +  - [Cheatsheet](#cheatsheet)
 +
 +## Setup
 +
 +See the [Basics](basics.md#get-the-code) documentation.
 +
 +## Getting Started
 +
 +There is a bit of boilerplate code that needs to be set up when creating a new
 +lint. Fortunately, you can use the clippy dev tools to handle this for you. We
 +are naming our new lint `foo_functions` (lints are generally written in snake
 +case), and we don't need type information so it will have an early pass type
 +(more on this later on). If you're not sure if the name you chose fits the lint,
 +take a look at our [lint naming guidelines][lint_naming]. To get started on this
 +lint you can run `cargo dev new_lint --name=foo_functions --pass=early
 +--category=pedantic` (category will default to nursery if not provided). This
 +command will create two files: `tests/ui/foo_functions.rs` and
 +`clippy_lints/src/foo_functions.rs`, as well as run `cargo dev update_lints` to
 +register the new lint. For cargo lints, two project hierarchies (fail/pass) will
 +be created by default under `tests/ui-cargo`.
 +
 +Next, we'll open up these files and add our lint!
 +
 +## Testing
 +
 +Let's write some tests first that we can execute while we iterate on our lint.
 +
 +Clippy uses UI tests for testing. UI tests check that the output of Clippy is
 +exactly as expected. Each test is just a plain Rust file that contains the code
 +we want to check. The output of Clippy is compared against a `.stderr` file.
 +Note that you don't have to create this file yourself, we'll get to
 +generating the `.stderr` files further down.
 +
 +We start by opening the test file created at `tests/ui/foo_functions.rs`.
 +
 +Update the file with some examples to get started:
 +
 +```rust
 +#![warn(clippy::foo_functions)]
 +
 +// Impl methods
 +struct A;
 +impl A {
 +    pub fn fo(&self) {}
 +    pub fn foo(&self) {}
 +    pub fn food(&self) {}
 +}
 +
 +// Default trait methods
 +trait B {
 +    fn fo(&self) {}
 +    fn foo(&self) {}
 +    fn food(&self) {}
 +}
 +
 +// Plain functions
 +fn fo() {}
 +fn foo() {}
 +fn food() {}
 +
 +fn main() {
 +    // We also don't want to lint method calls
 +    foo();
 +    let a = A;
 +    a.foo();
 +}
 +```
 +
 +Now we can run the test with `TESTNAME=foo_functions cargo uitest`,
 +currently this test is meaningless though.
 +
 +While we are working on implementing our lint, we can keep running the UI
 +test. That allows us to check if the output is turning into what we want.
 +
 +Once we are satisfied with the output, we need to run
 +`cargo dev bless` to update the `.stderr` file for our lint.
 +Please note that, we should run `TESTNAME=foo_functions cargo uitest`
 +every time before running `cargo dev bless`.
 +Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit
 +our lint, we need to commit the generated `.stderr` files, too. In general, you
 +should only commit files changed by `cargo dev bless` for the
 +specific lint you are creating/editing. Note that if the generated files are
 +empty, they should be removed.
 +
 +Note that you can run multiple test files by specifying a comma separated list:
 +`TESTNAME=foo_functions,test2,test3`.
 +
 +### Cargo lints
 +
 +For cargo lints, the process of testing differs in that we are interested in
 +the `Cargo.toml` manifest file. We also need a minimal crate associated
 +with that manifest.
 +
 +If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint`
 +we will find by default two new crates, each with its manifest file:
 +
 +* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error.
 +* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger the lint.
 +
 +If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it.
 +
 +The process of generating the `.stderr` file is the same, and prepending the `TESTNAME`
 +variable to `cargo uitest` works too.
 +
 +## Rustfix tests
 +
 +If the lint you are working on is making use of structured suggestions, the
 +test file should include a `// run-rustfix` comment at the top. This will
 +additionally run [rustfix] for that test. Rustfix will apply the suggestions
 +from the lint to the code of the test file and compare that to the contents of
 +a `.fixed` file.
 +
 +Use `cargo dev bless` to automatically generate the
 +`.fixed` file after running the tests.
 +
 +[rustfix]: https://github.com/rust-lang/rustfix
 +
 +## Edition 2018 tests
 +
 +Some features require the 2018 edition to work (e.g. `async_await`), but
 +compile-test tests run on the 2015 edition by default. To change this behavior
 +add `// edition:2018` at the top of the test file (note that it's space-sensitive).
 +
 +## Testing manually
 +
 +Manually testing against an example file can be useful if you have added some
 +`println!`s and the test suite output becomes unreadable. To try Clippy with
 +your local modifications, run
 +
 +```
 +env __CLIPPY_INTERNAL_TESTS=true cargo run --bin clippy-driver -- -L ./target/debug input.rs
 +```
 +
 +from the working copy root. With tests in place, let's have a look at
 +implementing our lint now.
 +
 +## Lint declaration
 +
 +Let's start by opening the new file created in the `clippy_lints` crate
 +at `clippy_lints/src/foo_functions.rs`. That's the crate where all the
 +lint code is. This file has already imported some initial things we will need:
 +
 +```rust
 +use rustc_lint::{EarlyLintPass, EarlyContext};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_ast::ast::*;
 +```
 +
 +The next step is to update the lint declaration. Lints are declared using the
 +[`declare_clippy_lint!`][declare_clippy_lint] macro, and we just need to update
 +the auto-generated lint declaration to have a real description, something like this:
 +
 +```rust
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // example code
 +    /// ```
 +    pub FOO_FUNCTIONS,
 +    pedantic,
 +    "function named `foo`, which is not a descriptive name"
 +}
 +```
 +
 +* The section of lines prefixed with `///` constitutes the lint documentation
 +  section. This is the default documentation style and will be displayed
 +  [like this][example_lint_page]. To render and open this documentation locally
 +  in a browser, run `cargo dev serve`.
 +* `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the
 +  [lint naming guidelines][lint_naming] here when naming your lint.
 +  In short, the name should state the thing that is being checked for and
 +  read well when used with `allow`/`warn`/`deny`.
 +* `pedantic` sets the lint level to `Allow`.
 +  The exact mapping can be found [here][category_level_mapping]
 +* The last part should be a text that explains what exactly is wrong with the
 +  code
 +
 +The rest of this file contains an empty implementation for our lint pass,
 +which in this case is `EarlyLintPass` and should look like this:
 +
 +```rust
 +// clippy_lints/src/foo_functions.rs
 +
 +// .. imports and lint declaration ..
 +
 +declare_lint_pass!(FooFunctions => [FOO_FUNCTIONS]);
 +
 +impl EarlyLintPass for FooFunctions {}
 +```
 +
 +Normally after declaring the lint, we have to run `cargo dev update_lints`,
 +which updates some files, so Clippy knows about the new lint. Since we used
 +`cargo dev new_lint ...` to generate the lint declaration, this was done
 +automatically. While `update_lints` automates most of the things, it doesn't
 +automate everything. We will have to register our lint pass manually in the
 +`register_plugins` function in `clippy_lints/src/lib.rs`:
 +
 +```rust
 +store.register_early_pass(|| box foo_functions::FooFunctions);
 +```
 +
 +As one may expect, there is a corresponding `register_late_pass` method
 +available as well. Without a call to one of `register_early_pass` or
 +`register_late_pass`, the lint pass in question will not be run.
 +
 +One reason that `cargo dev` does not automate this step is that multiple lints
 +can use the same lint pass, so registering the lint pass may already be done
 +when adding a new lint. Another reason that this step is not automated is that
 +the order that the passes are registered determines the order the passes
 +actually run, which in turn affects the order that any emitted lints are output
 +in.
 +
 +[declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60
 +[example_lint_page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
 +[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
 +[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110
 +
 +## Lint passes
 +
 +Writing a lint that only checks for the name of a function means that we only
 +have to deal with the AST and don't have to deal with the type system at all.
 +This is good, because it makes writing this particular lint less complicated.
 +
 +We have to make this decision with every new Clippy lint. It boils down to using
 +either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass].
 +
 +In short, the `LateLintPass` has access to type information while the
 +`EarlyLintPass` doesn't. If you don't need access to type information, use the
 +`EarlyLintPass`. The `EarlyLintPass` is also faster. However linting speed
 +hasn't really been a concern with Clippy so far.
 +
 +Since we don't need type information for checking the function name, we used
 +`--pass=early` when running the new lint automation and all the imports were
 +added accordingly.
 +
 +[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
 +[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
 +
 +## Emitting a lint
 +
 +With UI tests and the lint declaration in place, we can start working on the
 +implementation of the lint logic.
 +
 +Let's start by implementing the `EarlyLintPass` for our `FooFunctions`:
 +
 +```rust
 +impl EarlyLintPass for FooFunctions {
 +    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
 +        // TODO: Emit lint here
 +    }
 +}
 +```
 +
 +We implement the [`check_fn`][check_fn] method from the
 +[`EarlyLintPass`][early_lint_pass] trait. This gives us access to various
 +information about the function that is currently being checked. More on that in
 +the next section. Let's worry about the details later and emit our lint for
 +*every* function definition first.
 +
 +Depending on how complex we want our lint message to be, we can choose from a
 +variety of lint emission functions. They can all be found in
 +[`clippy_utils/src/diagnostics.rs`][diagnostics].
 +
 +`span_lint_and_help` seems most appropriate in this case. It allows us to
 +provide an extra help message and we can't really suggest a better name
 +automatically. This is how it looks:
 +
 +```rust
 +impl EarlyLintPass for FooFunctions {
 +    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
 +        span_lint_and_help(
 +            cx,
 +            FOO_FUNCTIONS,
 +            span,
 +            "function named `foo`",
 +            None,
 +            "consider using a more meaningful name"
 +        );
 +    }
 +}
 +```
 +
 +Running our UI test should now produce output that contains the lint message.
 +
 +According to [the rustc-dev-guide], the text should be matter of fact and avoid
 +capitalization and periods, unless multiple sentences are needed.
 +When code or an identifier must appear in a message or label, it should be
 +surrounded with single grave accents \`.
 +
 +[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
 +[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/diagnostics.rs
 +[the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html
 +
 +## Adding the lint logic
 +
 +Writing the logic for your lint will most likely be different from our example,
 +so this section is kept rather short.
 +
 +Using the [`check_fn`][check_fn] method gives us access to [`FnKind`][fn_kind]
 +that has the [`FnKind::Fn`] variant. It provides access to the name of the
 +function/method via an [`Ident`][ident].
 +
 +With that we can expand our `check_fn` method to:
 +
 +```rust
 +impl EarlyLintPass for FooFunctions {
 +    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
 +        if is_foo_fn(fn_kind) {
 +            span_lint_and_help(
 +                cx,
 +                FOO_FUNCTIONS,
 +                span,
 +                "function named `foo`",
 +                None,
 +                "consider using a more meaningful name"
 +            );
 +        }
 +    }
 +}
 +```
 +
 +We separate the lint conditional from the lint emissions because it makes the
 +code a bit easier to read. In some cases this separation would also allow to
 +write some unit tests (as opposed to only UI tests) for the separate function.
 +
 +In our example, `is_foo_fn` looks like:
 +
 +```rust
 +// use statements, impl EarlyLintPass, check_fn, ..
 +
 +fn is_foo_fn(fn_kind: FnKind<'_>) -> bool {
 +    match fn_kind {
 +        FnKind::Fn(_, ident, ..) => {
 +            // check if `fn` name is `foo`
 +            ident.name.as_str() == "foo"
 +        }
 +        // ignore closures
 +        FnKind::Closure(..) => false
 +    }
 +}
 +```
 +
 +Now we should also run the full test suite with `cargo test`. At this point
 +running `cargo test` should produce the expected output. Remember to run
 +`cargo dev bless` to update the `.stderr` file.
 +
 +`cargo test` (as opposed to `cargo uitest`) will also ensure that our lint
 +implementation is not violating any Clippy lints itself.
 +
 +That should be it for the lint implementation. Running `cargo test` should now
 +pass.
 +
 +[fn_kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html
 +[`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn
 +[ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html
 +
 +## Specifying the lint's minimum supported Rust version (MSRV)
 +
 +Sometimes a lint makes suggestions that require a certain version of Rust. For example, the `manual_strip` lint suggests
 +using `str::strip_prefix` and `str::strip_suffix` which is only available after Rust 1.45. In such cases, you need to
 +ensure that the MSRV configured for the project is >= the MSRV of the required Rust feature. If multiple features are
 +required, just use the one with a lower MSRV.
 +
 +First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`](/clippy_utils/src/msrvs.rs). This can be
 +accessed later as `msrvs::STR_STRIP_PREFIX`, for example.
 +
 +```rust
 +msrv_aliases! {
 +    ..
 +    1,45,0 { STR_STRIP_PREFIX }
 +}
 +```
 +
 +In order to access the project-configured MSRV, you need to have an `msrv` field in the LintPass struct, and a
 +constructor to initialize the field. The `msrv` value is passed to the constructor in `clippy_lints/lib.rs`.
 +
 +```rust
 +pub struct ManualStrip {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl ManualStrip {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +```
 +
 +The project's MSRV can then be matched against the feature MSRV in the LintPass
 +using the `meets_msrv` utility function.
 +
 +``` rust
 +if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) {
 +    return;
 +}
 +```
 +
 +The project's MSRV can also be specified as an inner attribute, which overrides
 +the value from `clippy.toml`. This can be accounted for using the
 +`extract_msrv_attr!(LintContext)` macro and passing
 +`LateContext`/`EarlyContext`.
 +
 +```rust
 +impl<'tcx> LateLintPass<'tcx> for ManualStrip {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        ...
 +    }
 +    extract_msrv_attr!(LateContext);
 +}
 +```
 +
 +Once the `msrv` is added to the lint, a relevant test case should be added to
 +`tests/ui/min_rust_version_attr.rs` which verifies that the lint isn't emitted
 +if the project's MSRV is lower.
 +
 +As a last step, the lint should be added to the lint documentation. This is done
 +in `clippy_lints/src/utils/conf.rs`:
 +
 +```rust
 +define_Conf! {
 +    /// Lint: LIST, OF, LINTS, <THE_NEWLY_ADDED_LINT>. The minimum rust version that the project supports
 +    (msrv: Option<String> = None),
 +    ...
 +}
 +```
 +
 +## Author lint
 +
 +If you have trouble implementing your lint, there is also the internal `author`
 +lint to generate Clippy code that detects the offending pattern. It does not
 +work for all of the Rust syntax, but can give a good starting point.
 +
 +The quickest way to use it, is the
 +[Rust playground: play.rust-lang.org][author_example].
 +Put the code you want to lint into the editor and add the `#[clippy::author]`
 +attribute above the item. Then run Clippy via `Tools -> Clippy` and you should
 +see the generated code in the output below.
 +
 +[Here][author_example] is an example on the playground.
 +
 +If the command was executed successfully, you can copy the code over to where
 +you are implementing your lint.
 +
 +[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
 +
 +## Documentation
 +
 +The final thing before submitting our PR is to add some documentation to our
 +lint declaration.
 +
 +Please document your lint with a doc comment akin to the following:
 +
 +```rust
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for ... (describe what the lint matches).
 +    ///
 +    /// ### Why is this bad?
 +    /// Supply the reason for linting the code.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// Insert a short example of code that triggers the lint
 +    ///
 +    /// // Good
 +    /// Insert a short example of improved code that doesn't trigger the lint
 +    /// ```
 +    pub FOO_FUNCTIONS,
 +    pedantic,
 +    "function named `foo`, which is not a descriptive name"
 +}
 +```
 +
 +Once your lint is merged, this documentation will show up in the [lint
 +list][lint_list].
 +
 +[lint_list]: https://rust-lang.github.io/rust-clippy/master/index.html
 +
 +## Running rustfmt
 +
 +[Rustfmt] is a tool for formatting Rust code according to style guidelines.
 +Your code has to be formatted by `rustfmt` before a PR can be merged.
 +Clippy uses nightly `rustfmt` in the CI.
 +
 +It can be installed via `rustup`:
 +
 +```bash
 +rustup component add rustfmt --toolchain=nightly
 +```
 +
 +Use `cargo dev fmt` to format the whole codebase. Make sure that `rustfmt` is
 +installed for the nightly toolchain.
 +
 +[Rustfmt]: https://github.com/rust-lang/rustfmt
 +
 +## Debugging
 +
 +If you want to debug parts of your lint implementation, you can use the [`dbg!`]
 +macro anywhere in your code. Running the tests should then include the debug
 +output in the `stdout` part.
 +
 +[`dbg!`]: https://doc.rust-lang.org/std/macro.dbg.html
 +
 +## PR Checklist
 +
 +Before submitting your PR make sure you followed all of the basic requirements:
 +
 +<!-- Sync this with `.github/PULL_REQUEST_TEMPLATE` -->
 +
 +- \[ ] Followed [lint naming conventions][lint_naming]
 +- \[ ] Added passing UI tests (including committed `.stderr` file)
 +- \[ ] `cargo test` passes locally
 +- \[ ] Executed `cargo dev update_lints`
 +- \[ ] Added lint documentation
 +- \[ ] Run `cargo dev fmt`
 +
 +## Adding configuration to a lint
 +
 +Clippy supports the configuration of lints values using a `clippy.toml` file in the workspace
 +directory. Adding a configuration to a lint can be useful for thresholds or to constrain some
 +behavior that can be seen as a false positive for some users. Adding a configuration is done
 +in the following steps:
 +
-     /// Lint: LINT_NAME. <The configuration field doc comment>
++1. Adding a new configuration entry to [clippy_lints::utils::conf](/clippy_lints/src/utils/conf.rs)
 +    like this:
 +    ```rust
-     The configuration value and identifier should usually be the same. The doc comment will be
-     automatically added to the lint documentation.
++    /// Lint: LINT_NAME.
++    ///
++    /// <The configuration field doc comment>
 +    (configuration_ident: Type = DefaultValue),
 +    ```
++    The doc comment will be automatically added to the lint documentation.
 +2. Adding the configuration value to the lint impl struct:
 +    1. This first requires the definition of a lint impl struct. Lint impl structs are usually
 +        generated with the `declare_lint_pass!` macro. This struct needs to be defined manually
 +        to add some kind of metadata to it:
 +        ```rust
 +        // Generated struct definition
 +        declare_lint_pass!(StructName => [
 +            LINT_NAME
 +        ]);
 +
 +        // New manual definition struct
 +        #[derive(Copy, Clone)]
 +        pub struct StructName {}
 +
 +        impl_lint_pass!(StructName => [
 +            LINT_NAME
 +        ]);
 +        ```
 +
 +    2. Next add the configuration value and a corresponding creation method like this:
 +        ```rust
 +        #[derive(Copy, Clone)]
 +        pub struct StructName {
 +            configuration_ident: Type,
 +        }
 +
 +        // ...
 +
 +        impl StructName {
 +            pub fn new(configuration_ident: Type) -> Self {
 +                Self {
 +                    configuration_ident,
 +                }
 +            }
 +        }
 +        ```
 +3. Passing the configuration value to the lint impl struct:
 +
 +    First find the struct construction in the [clippy_lints lib file](/clippy_lints/src/lib.rs).
 +    The configuration value is now cloned or copied into a local value that is then passed to the
 +    impl struct like this:
 +    ```rust
 +    // Default generated registration:
 +    store.register_*_pass(|| box module::StructName);
 +
 +    // New registration with configuration value
 +    let configuration_ident = conf.configuration_ident.clone();
 +    store.register_*_pass(move || box module::StructName::new(configuration_ident));
 +    ```
 +
 +    Congratulations the work is almost done. The configuration value can now be accessed
 +    in the linting code via `self.configuration_ident`.
 +
 +4. Adding tests:
 +    1. The default configured value can be tested like any normal lint in [`tests/ui`](/tests/ui).
 +    2. The configuration itself will be tested separately in [`tests/ui-toml`](/tests/ui-toml).
 +        Simply add a new subfolder with a fitting name. This folder contains a `clippy.toml` file
 +        with the configuration value and a rust file that should be linted by Clippy. The test can
 +        otherwise be written as usual.
 +
 +## Cheatsheet
 +
 +Here are some pointers to things you are likely going to need for every lint:
 +
 +* [Clippy utils][utils] - Various helper functions. Maybe the function you need
 +  is already in here (`implements_trait`, `match_def_path`, `snippet`, etc)
 +* [Clippy diagnostics][diagnostics]
 +* [The `if_chain` macro][if_chain]
 +* [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro]
 +* [`Span`][span]
 +* [`Applicability`][applicability]
 +* [Common tools for writing lints](common_tools_writing_lints.md) helps with common operations
 +* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler concepts
 +* [The nightly rustc docs][nightly_docs] which has been linked to throughout
 +  this guide
 +
 +For `EarlyLintPass` lints:
 +
 +* [`EarlyLintPass`][early_lint_pass]
 +* [`rustc_ast::ast`][ast]
 +
 +For `LateLintPass` lints:
 +
 +* [`LateLintPass`][late_lint_pass]
 +* [`Ty::TyKind`][ty]
 +
 +While most of Clippy's lint utils are documented, most of rustc's internals lack
 +documentation currently. This is unfortunate, but in most cases you can probably
 +get away with copying things from existing similar lints. If you are stuck,
 +don't hesitate to ask on [Zulip] or in the issue/PR.
 +
 +[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs
 +[if_chain]: https://docs.rs/if_chain/*/if_chain/
 +[from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
 +[in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html
 +[span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html
 +[applicability]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html
 +[rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/
 +[nightly_docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/
 +[ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html
 +[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/index.html
 +[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy
index 0a85f65001101c4fa5a172ac462b73d781e5e125,0000000000000000000000000000000000000000..1a6b7c8cb47a9db623f64fe5868d29d5f1dd5a40
mode 100644,000000..100644
--- /dev/null
@@@ -1,203 -1,0 +1,210 @@@
- There are two ways to do this, depending if the target trait is part of lang items.
 +# Common tools for writing lints
 +
 +You may need following tooltips to catch up with common operations.
 +
 +- [Common tools for writing lints](#common-tools-for-writing-lints)
 +  - [Retrieving the type of an expression](#retrieving-the-type-of-an-expression)
 +  - [Checking if an expression is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method)
 +  - [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait)
 +  - [Checking if a type defines a specific method](#checking-if-a-type-defines-a-specific-method)
 +  - [Dealing with macros](#dealing-with-macros)
 +
 +Useful Rustc dev guide links:
 +- [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation)
++- [Diagnostic items](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html)
 +- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html)
 +- [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html)
 +
 +# Retrieving the type of an expression
 +
 +Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for example to answer following questions:
 +
 +- which type does this expression correspond to (using its [`TyKind`][TyKind])?
 +- is it a sized type?
 +- is it a primitive type?
 +- does it implement a trait?
 +
 +This operation is performed using the [`expr_ty()`][expr_ty] method from the [`TypeckResults`][TypeckResults] struct,
 +that gives you access to the underlying structure [`TyS`][TyS].
 +
 +Example of use:
 +```rust
 +impl LateLintPass<'_> for MyStructLint {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        // Get type of `expr`
 +        let ty = cx.typeck_results().expr_ty(expr);
 +        // Match its kind to enter its type
 +        match ty.kind {
 +            ty::Adt(adt_def, _) if adt_def.is_struct() => println!("Our `expr` is a struct!"),
 +            _ => ()
 +        }
 +    }
 +}
 +```
 +
 +Similarly in [`TypeckResults`][TypeckResults] methods, you have the [`pat_ty()`][pat_ty] method
 +to retrieve a type from a pattern.
 +
 +Two noticeable items here:
 +- `cx` is the lint context [`LateContext`][LateContext]. The two most useful
 +  data structures in this context are `tcx` and the `TypeckResults` returned by
 +  `LateContext::typeck_results`, allowing us to jump to type definitions and
 +  other compilation stages such as HIR.
 +- `typeck_results`'s return value is [`TypeckResults`][TypeckResults] and is
 +  created by type checking step, it includes useful information such as types
 +  of expressions, ways to resolve methods and so on.
 +
 +# Checking if an expr is calling a specific method
 +
 +Starting with an `expr`, you can check whether it is calling a specific method `some_method`:
 +
 +```rust
 +impl LateLintPass<'_> for MyStructLint {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if_chain! {
 +            // Check our expr is calling a method
 +            if let hir::ExprKind::MethodCall(path, _, _args, _) = &expr.kind;
 +            // Check the name of this method is `some_method`
 +            if path.ident.name == sym!(some_method);
 +            then {
 +                // ...
 +            }
 +        }
 +    }
 +}
 +```
 +
 +# Checking if a type implements a specific trait
 +
- use clippy_utils::{implements_trait, match_trait_method, paths};
++There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither.
 +
 +```rust
-         // 1. Using expression and Clippy's convenient method
-         // we use `match_trait_method` function from Clippy's toolbox
-         if match_trait_method(cx, expr, &paths::INTO) {
-             // `expr` implements `Into` trait
++use clippy_utils::{implements_trait, is_trait_method, match_trait_method, paths};
++use rustc_span::symbol::sym;
 +
 +impl LateLintPass<'_> for MyStructLint {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-         // 2. Using type context `TyCtxt`
++        // 1. Using diagnostic items with the expression
++        // we use `is_trait_method` function from Clippy's utils
++        if is_trait_method(cx, expr, sym::Iterator) {
++            // method call in `expr` belongs to `Iterator` trait
 +        }
 +
- > Prefer using lang items, if the target trait is available there.
- A list of defined paths for Clippy can be found in [paths.rs][paths]
++        // 2. Using lang items with the expression type
 +        let ty = cx.typeck_results().expr_ty(expr);
 +        if cx.tcx.lang_items()
 +            // we are looking for the `DefId` of `Drop` trait in lang items
 +            .drop_trait()
 +            // then we use it with our type `ty` by calling `implements_trait` from Clippy's utils
 +            .map_or(false, |id| implements_trait(cx, ty, id, &[])) {
 +                // `expr` implements `Drop` trait
 +            }
++
++        // 3. Using the type path with the expression
++        // we use `match_trait_method` function from Clippy's utils
++        if match_trait_method(cx, expr, &paths::INTO) {
++            // `expr` implements `Into` trait
++        }
 +    }
 +}
 +```
 +
++> Prefer using diagnostic and lang items, if the target trait has one.
 +
 +We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate.
++A list of defined paths for Clippy can be found in [paths.rs][paths]
 +
 +# Checking if a type defines a specific method
 +
 +To check if our type defines a method called `some_method`:
 +
 +```rust
 +use clippy_utils::{is_type_diagnostic_item, return_ty};
 +
 +impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
 +        if_chain! {
 +            // Check if item is a method/function
 +            if let ImplItemKind::Fn(ref signature, _) = impl_item.kind;
 +            // Check the method is named `some_method`
 +            if impl_item.ident.name == sym!(some_method);
 +            // We can also check it has a parameter `self`
 +            if signature.decl.implicit_self.has_implicit_self();
 +            // We can go further and even check if its return type is `String`
 +            if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type));
 +            then {
 +                // ...
 +            }
 +        }
 +    }
 +}
 +```
 +
 +# Dealing with macros
 +
 +There are several helpers in [`clippy_utils`][utils] to deal with macros:
 +
 +- `in_macro()`: detect if the given span is expanded by a macro
 +
 +You may want to use this for example to not start linting in any macro.
 +
 +```rust
 +macro_rules! foo {
 +    ($param:expr) => {
 +        match $param {
 +            "bar" => println!("whatever"),
 +            _ => ()
 +        }
 +    };
 +}
 +
 +foo!("bar");
 +
 +// if we lint the `match` of `foo` call and test its span
 +assert_eq!(in_macro(match_span), true);
 +```
 +
 +- `in_external_macro()`: detect if the given span is from an external macro, defined in a foreign crate
 +
 +You may want to use it for example to not start linting in macros from other crates
 +
 +```rust
 +#[macro_use]
 +extern crate a_crate_with_macros;
 +
 +// `foo` is defined in `a_crate_with_macros`
 +foo!("bar");
 +
 +// if we lint the `match` of `foo` call and test its span
 +assert_eq!(in_external_macro(cx.sess(), match_span), true);
 +```
 +
 +- `differing_macro_contexts()`: returns true if the two given spans are not from the same context
 +
 +```rust
 +macro_rules! m {
 +    ($a:expr, $b:expr) => {
 +        if $a.is_some() {
 +            $b;
 +        }
 +    }
 +}
 +
 +let x: Option<u32> = Some(42);
 +m!(x, x.unwrap());
 +
 +// These spans are not from the same context
 +// x.is_some() is from inside the macro
 +// x.unwrap() is from outside the macro
 +assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
 +```
 +
 +[TyS]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html
 +[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
 +[TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html
 +[expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty
 +[LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html
 +[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
 +[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty
 +[paths]: ../clippy_utils/src/paths.rs
 +[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs
index 8c33fa372eccb07aed9139810d40240c2dcd1dd2,0000000000000000000000000000000000000000..ada033de6e3abe08ea16c946228514b4f95298ff
mode 100644,000000..100644
--- /dev/null
@@@ -1,24 -1,0 +1,25 @@@
 +[package]
 +name = "lintcheck"
 +version = "0.0.1"
 +description = "tool to monitor impact of changes in Clippys 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 = "2018"
 +publish = false
 +
 +[dependencies]
 +clap = "2.33"
 +flate2 = {version = "1.0.19"}
 +fs_extra = {version = "1.2.0"}
 +rayon = {version = "1.5.0"}
 +serde = {version = "1.0", features = ["derive"]}
 +serde_json = {version = "1.0"}
 +tar = {version = "0.4.30"}
 +toml = {version = "0.5"}
 +ureq = {version = "2.0.0-rc3"}
++walkdir = {version = "2.3.2"}
 +
 +[features]
 +deny-warnings = []
index 7d2f3173fb0e7cd4d5d475decac18787b14f60b0,0000000000000000000000000000000000000000..f1e03ba42966d9bd31e3597f6d00ef0e0e1fe7b3
mode 100644,000000..100644
--- /dev/null
@@@ -1,925 -1,0 +1,935 @@@
-                 use fs_extra::dir;
-                 // simply copy the entire directory into our target dir
-                 let copy_dest = PathBuf::from(format!("{}/", LINTCHECK_SOURCES));
-                 // the source path of the crate we copied,  ${copy_dest}/crate_name
-                 let crate_root = copy_dest.join(name); // .../crates/local_crate
-                 if crate_root.exists() {
-                     println!(
-                         "Not copying {} to {}, destination already exists",
-                         path.display(),
-                         crate_root.display()
-                     );
-                 } else {
-                     println!("Copying {} to {}", path.display(), copy_dest.display());
-                     dir::copy(path, &copy_dest, &dir::CopyOptions::new()).unwrap_or_else(|_| {
-                         panic!("Failed to copy from {}, to  {}", path.display(), crate_root.display())
-                     });
 +// Run clippy on a fixed set of crates and collect the warnings.
 +// This helps observing the impact clippy changes have on a set of real-world code (and not just our
 +// testsuite).
 +//
 +// When a new lint is introduced, we can search the results for new warnings and check for false
 +// positives.
 +
 +#![allow(clippy::collapsible_else_if)]
 +
 +use std::ffi::OsStr;
 +use std::process::Command;
 +use std::sync::atomic::{AtomicUsize, Ordering};
 +use std::{collections::HashMap, io::ErrorKind};
 +use std::{
 +    env, fmt,
 +    fs::write,
 +    path::{Path, PathBuf},
 +};
 +
 +use clap::{App, Arg, ArgMatches};
 +use rayon::prelude::*;
 +use serde::{Deserialize, Serialize};
 +use serde_json::Value;
++use walkdir::{DirEntry, WalkDir};
 +
 +#[cfg(not(windows))]
 +const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver";
 +#[cfg(not(windows))]
 +const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy";
 +
 +#[cfg(windows)]
 +const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver.exe";
 +#[cfg(windows)]
 +const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy.exe";
 +
 +const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads";
 +const LINTCHECK_SOURCES: &str = "target/lintcheck/sources";
 +
 +/// List of sources to check, loaded from a .toml file
 +#[derive(Debug, Serialize, Deserialize)]
 +struct SourceList {
 +    crates: HashMap<String, TomlCrate>,
 +}
 +
 +/// A crate source stored inside the .toml
 +/// will be translated into on one of the `CrateSource` variants
 +#[derive(Debug, Serialize, Deserialize)]
 +struct TomlCrate {
 +    name: String,
 +    versions: Option<Vec<String>>,
 +    git_url: Option<String>,
 +    git_hash: Option<String>,
 +    path: Option<String>,
 +    options: Option<Vec<String>>,
 +}
 +
 +/// Represents an archive we download from crates.io, or a git repo, or a local repo/folder
 +/// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate`
 +#[derive(Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)]
 +enum CrateSource {
 +    CratesIo {
 +        name: String,
 +        version: String,
 +        options: Option<Vec<String>>,
 +    },
 +    Git {
 +        name: String,
 +        url: String,
 +        commit: String,
 +        options: Option<Vec<String>>,
 +    },
 +    Path {
 +        name: String,
 +        path: PathBuf,
 +        options: Option<Vec<String>>,
 +    },
 +}
 +
 +/// Represents the actual source code of a crate that we ran "cargo clippy" on
 +#[derive(Debug)]
 +struct Crate {
 +    version: String,
 +    name: String,
 +    // path to the extracted sources that clippy can check
 +    path: PathBuf,
 +    options: Option<Vec<String>>,
 +}
 +
 +/// A single warning that clippy issued while checking a `Crate`
 +#[derive(Debug)]
 +struct ClippyWarning {
 +    crate_name: String,
 +    crate_version: String,
 +    file: String,
 +    line: String,
 +    column: String,
 +    linttype: String,
 +    message: String,
 +    is_ice: bool,
 +}
 +
 +impl std::fmt::Display for ClippyWarning {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        writeln!(
 +            f,
 +            r#"target/lintcheck/sources/{}-{}/{}:{}:{} {} "{}""#,
 +            &self.crate_name, &self.crate_version, &self.file, &self.line, &self.column, &self.linttype, &self.message
 +        )
 +    }
 +}
 +
 +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 = ureq::get(&url).call().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 } => {
-                     path: crate_root,
++                // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file.
++                // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory
++                // as a result of this filter.
++                let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name);
++                if dest_crate_root.exists() {
++                    println!("Deleting existing directory at {:?}", dest_crate_root);
++                    std::fs::remove_dir_all(&dest_crate_root).unwrap();
++                }
++
++                println!("Copying {:?} to {:?}", path, dest_crate_root);
++
++                fn is_cache_dir(entry: &DirEntry) -> bool {
++                    std::fs::read(entry.path().join("CACHEDIR.TAG"))
++                        .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55"))
++                        .unwrap_or(false)
++                }
++
++                for entry in WalkDir::new(path).into_iter().filter_entry(|e| !is_cache_dir(e)) {
++                    let entry = entry.unwrap();
++                    let entry_path = entry.path();
++                    let relative_entry_path = entry_path.strip_prefix(path).unwrap();
++                    let dest_path = dest_crate_root.join(relative_entry_path);
++                    let metadata = entry_path.symlink_metadata().unwrap();
++
++                    if metadata.is_dir() {
++                        std::fs::create_dir(dest_path).unwrap();
++                    } else if metadata.is_file() {
++                        std::fs::copy(entry_path, dest_path).unwrap();
++                    }
 +                }
 +
 +                Crate {
 +                    version: String::from("local"),
 +                    name: name.clone(),
++                    path: dest_crate_root,
 +                    options: options.clone(),
 +                }
 +            },
 +        }
 +    }
 +}
 +
 +impl Crate {
 +    /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy
 +    /// issued
 +    fn run_clippy_lints(
 +        &self,
 +        cargo_clippy_path: &Path,
 +        target_dir_index: &AtomicUsize,
 +        thread_limit: usize,
 +        total_crates_to_lint: usize,
 +        fix: bool,
 +    ) -> Vec<ClippyWarning> {
 +        // advance the atomic index by one
 +        let index = target_dir_index.fetch_add(1, Ordering::SeqCst);
 +        // "loop" the index within 0..thread_limit
 +        let thread_index = index % thread_limit;
 +        let perc = (index * 100) / total_crates_to_lint;
 +
 +        if thread_limit == 1 {
 +            println!(
 +                "{}/{} {}% Linting {} {}",
 +                index, total_crates_to_lint, perc, &self.name, &self.version
 +            );
 +        } else {
 +            println!(
 +                "{}/{} {}% Linting {} {} in target dir {:?}",
 +                index, total_crates_to_lint, perc, &self.name, &self.version, thread_index
 +            );
 +        }
 +
 +        let cargo_clippy_path = std::fs::canonicalize(cargo_clippy_path).unwrap();
 +
 +        let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir");
 +
 +        let mut args = if fix {
 +            vec!["--fix", "--allow-no-vcs", "--", "--cap-lints=warn"]
 +        } else {
 +            vec!["--", "--message-format=json", "--", "--cap-lints=warn"]
 +        };
 +
 +        if let Some(options) = &self.options {
 +            for opt in options {
 +                args.push(opt);
 +            }
 +        } else {
 +            args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"])
 +        }
 +
 +        let all_output = std::process::Command::new(&cargo_clippy_path)
 +            // use the looping index to create individual target dirs
 +            .env(
 +                "CARGO_TARGET_DIR",
 +                shared_target_dir.join(format!("_{:?}", thread_index)),
 +            )
 +            // lint warnings will look like this:
 +            // src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter`
 +            .args(&args)
 +            .current_dir(&self.path)
 +            .output()
 +            .unwrap_or_else(|error| {
 +                panic!(
 +                    "Encountered error:\n{:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
 +                    error,
 +                    &cargo_clippy_path.display(),
 +                    &self.path.display()
 +                );
 +            });
 +        let stdout = String::from_utf8_lossy(&all_output.stdout);
 +        let stderr = String::from_utf8_lossy(&all_output.stderr);
 +        let status = &all_output.status;
 +
 +        if !status.success() {
 +            eprintln!(
 +                "\nWARNING: bad exit status after checking {} {} \n",
 +                self.name, self.version
 +            );
 +        }
 +
 +        if fix {
 +            if let Some(stderr) = stderr
 +                .lines()
 +                .find(|line| line.contains("failed to automatically apply fixes suggested by rustc to crate"))
 +            {
 +                let subcrate = &stderr[63..];
 +                println!(
 +                    "ERROR: failed to apply some suggetion to {} / to (sub)crate {}",
 +                    self.name, subcrate
 +                );
 +            }
 +            // fast path, we don't need the warnings anyway
 +            return Vec::new();
 +        }
 +
 +        let output_lines = stdout.lines();
 +        let warnings: Vec<ClippyWarning> = output_lines
 +            .into_iter()
 +            // get all clippy warnings and ICEs
 +            .filter(|line| filter_clippy_warnings(&line))
 +            .map(|json_msg| parse_json_message(json_msg, &self))
 +            .collect();
 +
 +        warnings
 +    }
 +}
 +
 +#[derive(Debug)]
 +struct LintcheckConfig {
 +    // max number of jobs to spawn (default 1)
 +    max_jobs: usize,
 +    // we read the sources to check from here
 +    sources_toml_path: PathBuf,
 +    // we save the clippy lint results here
 +    lintcheck_results_path: PathBuf,
 +    // whether to just run --fix and not collect all the warnings
 +    fix: bool,
 +}
 +
 +impl LintcheckConfig {
 +    fn from_clap(clap_config: &ArgMatches) -> Self {
 +        // first, check if we got anything passed via the LINTCHECK_TOML env var,
 +        // if not, ask clap if we got any value for --crates-toml  <foo>
 +        // if not, use the default "lintcheck/lintcheck_crates.toml"
 +        let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| {
 +            clap_config
 +                .value_of("crates-toml")
 +                .clone()
 +                .unwrap_or("lintcheck/lintcheck_crates.toml")
 +                .to_string()
 +        });
 +
 +        let sources_toml_path = PathBuf::from(sources_toml);
 +
 +        // for the path where we save the lint results, get the filename without extension (so for
 +        // wasd.toml, use "wasd"...)
 +        let filename: PathBuf = sources_toml_path.file_stem().unwrap().into();
 +        let lintcheck_results_path = PathBuf::from(format!("lintcheck-logs/{}_logs.txt", filename.display()));
 +
 +        // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and
 +        // use half of that for the physical core count
 +        // by default use a single thread
 +        let max_jobs = match clap_config.value_of("threads") {
 +            Some(threads) => {
 +                let threads: usize = threads
 +                    .parse()
 +                    .unwrap_or_else(|_| panic!("Failed to parse '{}' to a digit", threads));
 +                if threads == 0 {
 +                    // automatic choice
 +                    // Rayon seems to return thread count so half that for core count
 +                    (rayon::current_num_threads() / 2) as usize
 +                } else {
 +                    threads
 +                }
 +            },
 +            // no -j passed, use a single thread
 +            None => 1,
 +        };
 +        let fix: bool = clap_config.is_present("fix");
 +
 +        LintcheckConfig {
 +            max_jobs,
 +            sources_toml_path,
 +            lintcheck_results_path,
 +            fix,
 +        }
 +    }
 +}
 +
 +/// takes a single json-formatted clippy warnings and returns true (we are interested in that line)
 +/// or false (we aren't)
 +fn filter_clippy_warnings(line: &str) -> bool {
 +    // we want to collect ICEs because clippy might have crashed.
 +    // these are summarized later
 +    if line.contains("internal compiler error: ") {
 +        return true;
 +    }
 +    // in general, we want all clippy warnings
 +    // however due to some kind of bug, sometimes there are absolute paths
 +    // to libcore files inside the message
 +    // or we end up with cargo-metadata output (https://github.com/rust-lang/rust-clippy/issues/6508)
 +
 +    // filter out these message to avoid unnecessary noise in the logs
 +    if line.contains("clippy::")
 +        && !(line.contains("could not read cargo metadata")
 +            || (line.contains(".rustup") && line.contains("toolchains")))
 +    {
 +        return true;
 +    }
 +    false
 +}
 +
 +/// Builds clippy inside the repo to make sure we have a clippy executable we can use.
 +fn build_clippy() {
 +    let status = Command::new("cargo")
 +        .arg("build")
 +        .status()
 +        .expect("Failed to build clippy!");
 +    if !status.success() {
 +        eprintln!("Error: Failed to compile Clippy!");
 +        std::process::exit(1);
 +    }
 +}
 +
 +/// Read a `toml` file and return a list of `CrateSources` that we want to check with clippy
 +fn read_crates(toml_path: &Path) -> Vec<CrateSource> {
 +    let toml_content: String =
 +        std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display()));
 +    let crate_list: SourceList =
 +        toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e));
 +    // parse the hashmap of the toml file into a list of crates
 +    let tomlcrates: Vec<TomlCrate> = crate_list
 +        .crates
 +        .into_iter()
 +        .map(|(_cratename, tomlcrate)| tomlcrate)
 +        .collect();
 +
 +    // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate =>
 +    // multiple Cratesources)
 +    let mut crate_sources = Vec::new();
 +    tomlcrates.into_iter().for_each(|tk| {
 +        if let Some(ref path) = tk.path {
 +            crate_sources.push(CrateSource::Path {
 +                name: tk.name.clone(),
 +                path: PathBuf::from(path),
 +                options: tk.options.clone(),
 +            });
 +        }
 +
 +        // if we have multiple versions, save each one
 +        if let Some(ref versions) = tk.versions {
 +            versions.iter().for_each(|ver| {
 +                crate_sources.push(CrateSource::CratesIo {
 +                    name: tk.name.clone(),
 +                    version: ver.to_string(),
 +                    options: tk.options.clone(),
 +                });
 +            })
 +        }
 +        // otherwise, we should have a git source
 +        if tk.git_url.is_some() && tk.git_hash.is_some() {
 +            crate_sources.push(CrateSource::Git {
 +                name: tk.name.clone(),
 +                url: tk.git_url.clone().unwrap(),
 +                commit: tk.git_hash.clone().unwrap(),
 +                options: tk.options.clone(),
 +            });
 +        }
 +        // if we have a version as well as a git data OR only one git data, something is funky
 +        if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some())
 +            || tk.git_hash.is_some() != tk.git_url.is_some()
 +        {
 +            eprintln!("tomlkrate: {:?}", tk);
 +            if tk.git_hash.is_some() != tk.git_url.is_some() {
 +                panic!("Error: Encountered TomlCrate with only one of git_hash and git_url!");
 +            }
 +            if tk.path.is_some() && (tk.git_hash.is_some() || tk.versions.is_some()) {
 +                panic!("Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields");
 +            }
 +            unreachable!("Failed to translate TomlCrate into CrateSource!");
 +        }
 +    });
 +    // sort the crates
 +    crate_sources.sort();
 +
 +    crate_sources
 +}
 +
 +/// Parse the json output of clippy and return a `ClippyWarning`
 +fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning {
 +    let jmsg: Value = serde_json::from_str(&json_message).unwrap_or_else(|e| panic!("Failed to parse json:\n{:?}", e));
 +
 +    let file: String = jmsg["message"]["spans"][0]["file_name"]
 +        .to_string()
 +        .trim_matches('"')
 +        .into();
 +
 +    let file = if file.contains(".cargo") {
 +        // if we deal with macros, a filename may show the origin of a macro which can be inside a dep from
 +        // the registry.
 +        // don't show the full path in that case.
 +
 +        // /home/matthias/.cargo/registry/src/github.com-1ecc6299db9ec823/syn-1.0.63/src/custom_keyword.rs
 +        let path = PathBuf::from(file);
 +        let mut piter = path.iter();
 +        // consume all elements until we find ".cargo", so that "/home/matthias" is skipped
 +        let _: Option<&OsStr> = piter.find(|x| x == &std::ffi::OsString::from(".cargo"));
 +        // collect the remaining segments
 +        let file = piter.collect::<PathBuf>();
 +        format!("{}", file.display())
 +    } else {
 +        file
 +    };
 +
 +    ClippyWarning {
 +        crate_name: krate.name.to_string(),
 +        crate_version: krate.version.to_string(),
 +        file,
 +        line: jmsg["message"]["spans"][0]["line_start"]
 +            .to_string()
 +            .trim_matches('"')
 +            .into(),
 +        column: jmsg["message"]["spans"][0]["text"][0]["highlight_start"]
 +            .to_string()
 +            .trim_matches('"')
 +            .into(),
 +        linttype: jmsg["message"]["code"]["code"].to_string().trim_matches('"').into(),
 +        message: jmsg["message"]["message"].to_string().trim_matches('"').into(),
 +        is_ice: json_message.contains("internal compiler error: "),
 +    }
 +}
 +
 +/// Generate a short list of occuring lints-types and their count
 +fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) {
 +    // count lint type occurrences
 +    let mut counter: HashMap<&String, usize> = HashMap::new();
 +    clippy_warnings
 +        .iter()
 +        .for_each(|wrn| *counter.entry(&wrn.linttype).or_insert(0) += 1);
 +
 +    // collect into a tupled list for sorting
 +    let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect();
 +    // sort by "000{count} {clippy::lintname}"
 +    // to not have a lint with 200 and 2 warnings take the same spot
 +    stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint));
 +
 +    let stats_string = stats
 +        .iter()
 +        .map(|(lint, count)| format!("{} {}\n", lint, count))
 +        .collect::<String>();
 +
 +    (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 is_in_clippy_root() -> bool {
 +    if let Ok(pb) = std::env::current_dir() {
 +        if let Some(file) = pb.file_name() {
 +            return file == PathBuf::from("rust-clippy");
 +        }
 +    }
 +
 +    false
 +}
 +
 +/// lintchecks `main()` function
 +///
 +/// # Panics
 +///
 +/// This function panics if the clippy binaries don't exist
 +/// or if lintcheck is executed from the wrong directory (aka none-repo-root)
 +pub fn main() {
 +    // assert that we launch lintcheck from the repo root (via cargo lintcheck)
 +    if !is_in_clippy_root() {
 +        eprintln!("lintcheck needs to be run from clippys repo root!\nUse `cargo lintcheck` alternatively.");
 +        std::process::exit(3);
 +    }
 +
 +    let clap_config = &get_clap_config();
 +
 +    let config = LintcheckConfig::from_clap(clap_config);
 +
 +    println!("Compiling clippy...");
 +    build_clippy();
 +    println!("Done compiling");
 +
 +    // if the clippy bin is newer than our logs, throw away target dirs to force clippy to
 +    // refresh the logs
 +    if lintcheck_needs_rerun(&config.lintcheck_results_path) {
 +        let shared_target_dir = "target/lintcheck/shared_target_dir";
 +        // if we get an Err here, the shared target dir probably does simply not exist
 +        if let Ok(metadata) = std::fs::metadata(&shared_target_dir) {
 +            if metadata.is_dir() {
 +                println!("Clippy is newer than lint check logs, clearing lintcheck shared target dir...");
 +                std::fs::remove_dir_all(&shared_target_dir)
 +                    .expect("failed to remove target/lintcheck/shared_target_dir");
 +            }
 +        }
 +    }
 +
 +    let cargo_clippy_path: PathBuf = PathBuf::from(CARGO_CLIPPY_PATH)
 +        .canonicalize()
 +        .expect("failed to canonicalize path to clippy binary");
 +
 +    // assert that clippy is found
 +    assert!(
 +        cargo_clippy_path.is_file(),
 +        "target/debug/cargo-clippy binary not found! {}",
 +        cargo_clippy_path.display()
 +    );
 +
 +    let clippy_ver = std::process::Command::new(CARGO_CLIPPY_PATH)
 +        .arg("--version")
 +        .output()
 +        .map(|o| String::from_utf8_lossy(&o.stdout).into_owned())
 +        .expect("could not get clippy version!");
 +
 +    // download and extract the crates, then run clippy on them and collect clippys warnings
 +    // flatten into one big list of warnings
 +
 +    let crates = read_crates(&config.sources_toml_path);
 +    let old_stats = read_stats_from_file(&config.lintcheck_results_path);
 +
 +    let counter = AtomicUsize::new(1);
 +
 +    let clippy_warnings: Vec<ClippyWarning> = if let Some(only_one_crate) = clap_config.value_of("only") {
 +        // if we don't have the specified crate in the .toml, throw an error
 +        if !crates.iter().any(|krate| {
 +            let name = match krate {
 +                CrateSource::CratesIo { name, .. } | CrateSource::Git { name, .. } | CrateSource::Path { name, .. } => {
 +                    name
 +                },
 +            };
 +            name == only_one_crate
 +        }) {
 +            eprintln!(
 +                "ERROR: could not find crate '{}' in lintcheck/lintcheck_crates.toml",
 +                only_one_crate
 +            );
 +            std::process::exit(1);
 +        }
 +
 +        // only check a single crate that was passed via cmdline
 +        crates
 +            .into_iter()
 +            .map(|krate| krate.download_and_extract())
 +            .filter(|krate| krate.name == only_one_crate)
 +            .flat_map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &AtomicUsize::new(0), 1, 1, config.fix))
 +            .collect()
 +    } else {
 +        if config.max_jobs > 1 {
 +            // run parallel with rayon
 +
 +            // Ask rayon for thread count. Assume that half of that is the number of physical cores
 +            // Use one target dir for each core so that we can run N clippys in parallel.
 +            // We need to use different target dirs because cargo would lock them for a single build otherwise,
 +            // killing the parallelism. However this also means that deps will only be reused half/a
 +            // quarter of the time which might result in a longer wall clock runtime
 +
 +            // This helps when we check many small crates with dep-trees that don't have a lot of branches in
 +            // order to achive some kind of parallelism
 +
 +            // by default, use a single thread
 +            let num_cpus = config.max_jobs;
 +            let num_crates = crates.len();
 +
 +            // check all crates (default)
 +            crates
 +                .into_par_iter()
 +                .map(|krate| krate.download_and_extract())
 +                .flat_map(|krate| {
 +                    krate.run_clippy_lints(&cargo_clippy_path, &counter, num_cpus, num_crates, config.fix)
 +                })
 +                .collect()
 +        } else {
 +            // run sequential
 +            let num_crates = crates.len();
 +            crates
 +                .into_iter()
 +                .map(|krate| krate.download_and_extract())
 +                .flat_map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &counter, 1, num_crates, config.fix))
 +                .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(ToString::to_string).collect();
 +    all_msgs.sort();
 +    all_msgs.push("\n\n\n\nStats:\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(&format!("\n{}", all_msgs.join("")));
 +    text.push_str("ICEs:\n");
 +    ices.iter()
 +        .for_each(|(cratename, msg)| text.push_str(&format!("{}: '{}'", cratename, msg)));
 +
 +    println!("Writing logs to {}", config.lintcheck_results_path.display());
 +    write(&config.lintcheck_results_path, text).unwrap();
 +
 +    print_stats(old_stats, new_stats);
 +}
 +
 +/// 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();
 +
 +    // search for the beginning "Stats:" and the end "ICEs:" of the section we want
 +    let start = lines.iter().position(|line| line == "Stats:").unwrap();
 +    let end = lines.iter().position(|line| line == "ICEs:").unwrap();
 +
 +    let stats_lines = &lines[start + 1..end];
 +
 +    stats_lines
 +        .iter()
 +        .map(|line| {
 +            let mut spl = line.split(' ');
 +            (
 +                spl.next().unwrap().to_string(),
 +                spl.next().unwrap().parse::<usize>().unwrap(),
 +            )
 +        })
 +        .collect::<HashMap<String, usize>>()
 +}
 +
 +/// print how lint counts changed between runs
 +fn print_stats(old_stats: HashMap<String, usize>, new_stats: HashMap<&String, usize>) {
 +    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())
 +        .for_each(|(old_key, old_value)| {
 +            println!("{} {} => 0", old_key, old_value);
 +        });
 +}
 +
 +/// Create necessary directories to run the lintcheck tool.
 +///
 +/// # Panics
 +///
 +/// This function panics if creating one of the dirs fails.
 +fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) {
 +    std::fs::create_dir("target/lintcheck/").unwrap_or_else(|err| {
 +        if err.kind() != ErrorKind::AlreadyExists {
 +            panic!("cannot create lintcheck target dir");
 +        }
 +    });
 +    std::fs::create_dir(&krate_download_dir).unwrap_or_else(|err| {
 +        if err.kind() != ErrorKind::AlreadyExists {
 +            panic!("cannot create crate download dir");
 +        }
 +    });
 +    std::fs::create_dir(&extract_dir).unwrap_or_else(|err| {
 +        if err.kind() != ErrorKind::AlreadyExists {
 +            panic!("cannot create crate extraction dir");
 +        }
 +    });
 +}
 +
 +fn get_clap_config<'a>() -> ArgMatches<'a> {
 +    App::new("lintcheck")
 +        .about("run clippy on a set of crates and check output")
 +        .arg(
 +            Arg::with_name("only")
 +                .takes_value(true)
 +                .value_name("CRATE")
 +                .long("only")
 +                .help("only process a single crate of the list"),
 +        )
 +        .arg(
 +            Arg::with_name("crates-toml")
 +                .takes_value(true)
 +                .value_name("CRATES-SOURCES-TOML-PATH")
 +                .long("crates-toml")
 +                .help("set the path for a crates.toml where lintcheck should read the sources from"),
 +        )
 +        .arg(
 +            Arg::with_name("threads")
 +                .takes_value(true)
 +                .value_name("N")
 +                .short("j")
 +                .long("jobs")
 +                .help("number of threads to use, 0 automatic choice"),
 +        )
 +        .arg(
 +            Arg::with_name("fix")
 +                .long("--fix")
 +                .help("runs cargo clippy --fix and checks if all suggestions apply"),
 +        )
 +        .get_matches()
 +}
 +
 +/// Returns the path to the Clippy project directory
 +///
 +/// # Panics
 +///
 +/// Panics if the current directory could not be retrieved, there was an error reading any of the
 +/// Cargo.toml files or ancestor directory is the clippy root directory
 +#[must_use]
 +pub fn clippy_project_root() -> PathBuf {
 +    let current_dir = std::env::current_dir().unwrap();
 +    for path in current_dir.ancestors() {
 +        let result = std::fs::read_to_string(path.join("Cargo.toml"));
 +        if let Err(err) = &result {
 +            if err.kind() == std::io::ErrorKind::NotFound {
 +                continue;
 +            }
 +        }
 +
 +        let content = result.unwrap();
 +        if content.contains("[package]\nname = \"clippy\"") {
 +            return path.to_path_buf();
 +        }
 +    }
 +    panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
 +}
 +
 +#[test]
 +fn lintcheck_test() {
 +    let args = [
 +        "run",
 +        "--target-dir",
 +        "lintcheck/target",
 +        "--manifest-path",
 +        "./lintcheck/Cargo.toml",
 +        "--",
 +        "--crates-toml",
 +        "lintcheck/test_sources.toml",
 +    ];
 +    let status = std::process::Command::new("cargo")
 +        .args(&args)
 +        .current_dir("..") // repo root
 +        .status();
 +    //.output();
 +
 +    assert!(status.unwrap().success());
 +}
index 23887f178454972c47b777c305ded39a12d84fbf,0000000000000000000000000000000000000000..92bde3423a20a811c3c05cf0294f4b62c04bcc35
mode 100644,000000..100644
--- /dev/null
@@@ -1,3 -1,0 +1,3 @@@
- channel = "nightly-2021-08-12"
 +[toolchain]
++channel = "nightly-2021-09-08"
 +components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
index 0a82377a10e4247eaebac2c19ab4696b96673fc0,0000000000000000000000000000000000000000..c63c47690d52ad9e9027ecfa6058217e09eae70f
mode 100644,000000..100644
--- /dev/null
@@@ -1,326 -1,0 +1,337 @@@
- #![feature(try_blocks)]
 +#![feature(test)] // compiletest_rs requires this attribute
 +#![feature(once_cell)]
- // When we'll want to use `extern crate ..` for a dependency that is used
- // both by the crate and the compiler itself, we can't simply pass -L flags
- // as we'll get a duplicate matching versions. Instead, disambiguate with
- // `--extern dep=path`.
- // See https://github.com/rust-lang/rust-clippy/issues/4015.
- //
- // FIXME: We cannot use `cargo build --message-format=json` to resolve to dependency files.
- //        Because it would force-rebuild if the options passed to `build` command is not the same
- //        as what we manually pass to `cargo` invocation
- fn third_party_crates() -> String {
-     use std::collections::HashMap;
-     static CRATES: &[&str] = &[
-         "clippy_lints",
-         "clippy_utils",
-         "if_chain",
-         "quote",
-         "regex",
-         "serde",
-         "serde_derive",
-         "syn",
-     ];
-     let dep_dir = cargo::TARGET_LIB.join("deps");
-     let mut crates: HashMap<&str, Vec<PathBuf>> = HashMap::with_capacity(CRATES.len());
-     let mut flags = String::new();
-     for entry in fs::read_dir(dep_dir).unwrap().flatten() {
-         let path = entry.path();
-         if let Some(name) = try {
-             let name = path.file_name()?.to_str()?;
-             let (name, _) = name.strip_suffix(".rlib")?.strip_prefix("lib")?.split_once('-')?;
-             CRATES.iter().copied().find(|&c| c == name)?
-         } {
-             flags += &format!(" --extern {}={}", name, path.display());
-             crates.entry(name).or_default().push(path.clone());
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use compiletest_rs as compiletest;
 +use compiletest_rs::common::Mode as TestMode;
 +
++use std::collections::HashMap;
 +use std::env::{self, remove_var, set_var, var_os};
 +use std::ffi::{OsStr, OsString};
 +use std::fs;
 +use std::io;
 +use std::path::{Path, PathBuf};
 +
 +mod cargo;
 +
 +// whether to run internal tests or not
 +const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints");
 +
++/// All crates used in UI tests are listed here
++static TEST_DEPENDENCIES: &[&str] = &[
++    "clippy_utils",
++    "derive_new",
++    "if_chain",
++    "itertools",
++    "quote",
++    "regex",
++    "serde",
++    "serde_derive",
++    "syn",
++];
++
++// Test dependencies may need an `extern crate` here to ensure that they show up
++// in the depinfo file (otherwise cargo thinks they are unused)
++#[allow(unused_extern_crates)]
++extern crate clippy_utils;
++#[allow(unused_extern_crates)]
++extern crate derive_new;
++#[allow(unused_extern_crates)]
++extern crate if_chain;
++#[allow(unused_extern_crates)]
++extern crate itertools;
++#[allow(unused_extern_crates)]
++extern crate quote;
++#[allow(unused_extern_crates)]
++extern crate syn;
++
 +fn host_lib() -> PathBuf {
 +    option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from)
 +}
 +
 +fn clippy_driver_path() -> PathBuf {
 +    option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from)
 +}
 +
-     crates.retain(|_, paths| paths.len() > 1);
-     if !crates.is_empty() {
-         let crate_names = crates.keys().map(|s| format!("`{}`", s)).collect::<Vec<_>>().join(", ");
-         // add backslashes for an easy copy-paste `rm` command
-         let paths = crates
-             .into_values()
-             .flatten()
-             .map(|p| strip_current_dir(&p).display().to_string())
-             .collect::<Vec<_>>()
-             .join(" \\\n");
-         // Check which action should be done in order to remove compiled deps.
-         // If pre-installed version of compiler is used, `cargo clean` will do.
-         // Otherwise (for bootstrapped compiler), the dependencies directory
-         // must be removed manually.
-         let suggested_action = if std::env::var_os("RUSTC_BOOTSTRAP").is_some() {
-             "removing the stageN-tools directory"
-         } else {
-             "running `cargo clean`"
-         };
-         panic!(
-             "\n----------------------------------------------------------------------\n\
-             ERROR: Found multiple rlibs for crates: {}\n\
-             Try {} or remove the following files:\n\n{}\n\n\
-             For details on this error see https://github.com/rust-lang/rust-clippy/issues/7343\n\
-             ----------------------------------------------------------------------\n",
-             crate_names, suggested_action, paths
-         );
++/// Produces a string with an `--extern` flag for all UI test crate
++/// dependencies.
++///
++/// The dependency files are located by parsing the depinfo file for this test
++/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test
++/// dependencies must be added to Cargo.toml at the project root. Test
++/// dependencies that are not *directly* used by this test module require an
++/// `extern crate` declaration.
++fn extern_flags() -> String {
++    let current_exe_depinfo = {
++        let mut path = env::current_exe().unwrap();
++        path.set_extension("d");
++        std::fs::read_to_string(path).unwrap()
++    };
++    let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len());
++    for line in current_exe_depinfo.lines() {
++        // each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:`
++        let parse_name_path = || {
++            if line.starts_with(char::is_whitespace) {
++                return None;
++            }
++            let path_str = line.strip_suffix(':')?;
++            let path = Path::new(path_str);
++            if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") {
++                return None;
++            }
++            let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?;
++            // the "lib" prefix is not present for dll files
++            let name = name.strip_prefix("lib").unwrap_or(name);
++            Some((name, path_str))
++        };
++        if let Some((name, path)) = parse_name_path() {
++            if TEST_DEPENDENCIES.contains(&name) {
++                // A dependency may be listed twice if it is available in sysroot,
++                // and the sysroot dependencies are listed first. As of the writing,
++                // this only seems to apply to if_chain.
++                crates.insert(name, path);
++            }
 +        }
 +    }
-     flags
++    let not_found: Vec<&str> = TEST_DEPENDENCIES
++        .iter()
++        .copied()
++        .filter(|n| !crates.contains_key(n))
++        .collect();
++    if !not_found.is_empty() {
++        panic!("dependencies not found in depinfo: {:?}", not_found);
 +    }
-         "--emit=metadata -L {0} -L {1} -Dwarnings -Zui-testing {2}",
++    crates
++        .into_iter()
++        .map(|(name, path)| format!("--extern {}={} ", name, path))
++        .collect()
 +}
 +
 +fn default_config() -> compiletest::Config {
 +    let mut config = compiletest::Config::default();
 +
 +    if let Ok(filters) = env::var("TESTNAME") {
 +        config.filters = filters.split(',').map(std::string::ToString::to_string).collect();
 +    }
 +
 +    if let Some(path) = option_env!("RUSTC_LIB_PATH") {
 +        let path = PathBuf::from(path);
 +        config.run_lib_path = path.clone();
 +        config.compile_lib_path = path;
 +    }
 +
++    // 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.
 +    config.target_rustcflags = Some(format!(
-         third_party_crates(),
++        "--emit=metadata -L dependency={} -L dependency={} -Dwarnings -Zui-testing {}",
 +        host_lib().join("deps").display(),
 +        cargo::TARGET_LIB.join("deps").display(),
- fn strip_current_dir(path: &Path) -> &Path {
-     if let Ok(curr) = env::current_dir() {
-         if let Ok(stripped) = path.strip_prefix(curr) {
-             return stripped;
-         }
-     }
-     path
- }
++        extern_flags(),
 +    ));
 +
 +    config.build_base = host_lib().join("test_build_base");
 +    config.rustc_path = clippy_driver_path();
 +    config
 +}
 +
 +fn run_ui(cfg: &mut compiletest::Config) {
 +    cfg.mode = TestMode::Ui;
 +    cfg.src_base = Path::new("tests").join("ui");
 +    // use tests/clippy.toml
 +    let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap());
 +    compiletest::run_tests(cfg);
 +}
 +
 +fn run_internal_tests(cfg: &mut compiletest::Config) {
 +    // only run internal tests with the internal-tests feature
 +    if !RUN_INTERNAL_TESTS {
 +        return;
 +    }
 +    cfg.mode = TestMode::Ui;
 +    cfg.src_base = Path::new("tests").join("ui-internal");
 +    compiletest::run_tests(cfg);
 +}
 +
 +fn run_ui_toml(config: &mut compiletest::Config) {
 +    fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
 +        let mut result = true;
 +        let opts = compiletest::test_opts(config);
 +        for dir in fs::read_dir(&config.src_base)? {
 +            let dir = dir?;
 +            if !dir.file_type()?.is_dir() {
 +                continue;
 +            }
 +            let dir_path = dir.path();
 +            let _g = VarGuard::set("CARGO_MANIFEST_DIR", &dir_path);
 +            for file in fs::read_dir(&dir_path)? {
 +                let file = file?;
 +                let file_path = file.path();
 +                if file.file_type()?.is_dir() {
 +                    continue;
 +                }
 +                if file_path.extension() != Some(OsStr::new("rs")) {
 +                    continue;
 +                }
 +                let paths = compiletest::common::TestPaths {
 +                    file: file_path,
 +                    base: config.src_base.clone(),
 +                    relative_dir: dir_path.file_name().unwrap().into(),
 +                };
 +                let test_name = compiletest::make_test_name(config, &paths);
 +                let index = tests
 +                    .iter()
 +                    .position(|test| test.desc.name == test_name)
 +                    .expect("The test should be in there");
 +                result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
 +            }
 +        }
 +        Ok(result)
 +    }
 +
 +    config.mode = TestMode::Ui;
 +    config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap();
 +
 +    let tests = compiletest::make_tests(config);
 +
 +    let res = run_tests(config, tests);
 +    match res {
 +        Ok(true) => {},
 +        Ok(false) => panic!("Some tests failed"),
 +        Err(e) => {
 +            panic!("I/O failure during tests: {:?}", e);
 +        },
 +    }
 +}
 +
 +fn run_ui_cargo(config: &mut compiletest::Config) {
 +    fn run_tests(
 +        config: &compiletest::Config,
 +        filters: &[String],
 +        mut tests: Vec<tester::TestDescAndFn>,
 +    ) -> Result<bool, io::Error> {
 +        let mut result = true;
 +        let opts = compiletest::test_opts(config);
 +
 +        for dir in fs::read_dir(&config.src_base)? {
 +            let dir = dir?;
 +            if !dir.file_type()?.is_dir() {
 +                continue;
 +            }
 +
 +            // Use the filter if provided
 +            let dir_path = dir.path();
 +            for filter in filters {
 +                if !dir_path.ends_with(filter) {
 +                    continue;
 +                }
 +            }
 +
 +            for case in fs::read_dir(&dir_path)? {
 +                let case = case?;
 +                if !case.file_type()?.is_dir() {
 +                    continue;
 +                }
 +
 +                let src_path = case.path().join("src");
 +
 +                // When switching between branches, if the previous branch had a test
 +                // that the current branch does not have, the directory is not removed
 +                // because an ignored Cargo.lock file exists.
 +                if !src_path.exists() {
 +                    continue;
 +                }
 +
 +                env::set_current_dir(&src_path)?;
 +                for file in fs::read_dir(&src_path)? {
 +                    let file = file?;
 +                    if file.file_type()?.is_dir() {
 +                        continue;
 +                    }
 +
 +                    // Search for the main file to avoid running a test for each file in the project
 +                    let file_path = file.path();
 +                    match file_path.file_name().and_then(OsStr::to_str) {
 +                        Some("main.rs") => {},
 +                        _ => continue,
 +                    }
 +                    let _g = VarGuard::set("CLIPPY_CONF_DIR", case.path());
 +                    let paths = compiletest::common::TestPaths {
 +                        file: file_path,
 +                        base: config.src_base.clone(),
 +                        relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
 +                    };
 +                    let test_name = compiletest::make_test_name(config, &paths);
 +                    let index = tests
 +                        .iter()
 +                        .position(|test| test.desc.name == test_name)
 +                        .expect("The test should be in there");
 +                    result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
 +                }
 +            }
 +        }
 +        Ok(result)
 +    }
 +
 +    if cargo::is_rustc_test_suite() {
 +        return;
 +    }
 +
 +    config.mode = TestMode::Ui;
 +    config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();
 +
 +    let tests = compiletest::make_tests(config);
 +
 +    let current_dir = env::current_dir().unwrap();
 +    let res = run_tests(config, &config.filters, tests);
 +    env::set_current_dir(current_dir).unwrap();
 +
 +    match res {
 +        Ok(true) => {},
 +        Ok(false) => panic!("Some tests failed"),
 +        Err(e) => {
 +            panic!("I/O failure during tests: {:?}", e);
 +        },
 +    }
 +}
 +
 +fn prepare_env() {
 +    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
 +    set_var("__CLIPPY_INTERNAL_TESTS", "true");
 +    //set_var("RUST_BACKTRACE", "0");
 +}
 +
 +#[test]
 +fn compile_test() {
 +    prepare_env();
 +    let mut config = default_config();
 +    run_ui(&mut config);
 +    run_ui_toml(&mut config);
 +    run_ui_cargo(&mut config);
 +    run_internal_tests(&mut config);
 +}
 +
 +/// Restores an env var on drop
 +#[must_use]
 +struct VarGuard {
 +    key: &'static str,
 +    value: Option<OsString>,
 +}
 +
 +impl VarGuard {
 +    fn set(key: &'static str, val: impl AsRef<OsStr>) -> Self {
 +        let value = var_os(key);
 +        set_var(key, val);
 +        Self { key, value }
 +    }
 +}
 +
 +impl Drop for VarGuard {
 +    fn drop(&mut self) {
 +        match self.value.as_deref() {
 +            None => remove_var(self.key),
 +            Some(value) => set_var(self.key, value),
 +        }
 +    }
 +}
index 4ede20c52583b0bbcef93972f47fff53aa2879db,0000000000000000000000000000000000000000..54f452172deb4a3f727e3a586fbd8f3266c45efe
mode 100644,000000..100644
--- /dev/null
@@@ -1,246 -1,0 +1,248 @@@
 +//! 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
 +
 +// Dogfood cannot run on Windows
 +#![cfg(not(windows))]
 +#![feature(once_cell)]
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use std::lazy::SyncLazy;
 +use std::path::PathBuf;
 +use std::process::Command;
 +
 +mod cargo;
 +
 +static CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| cargo::TARGET_LIB.join("cargo-clippy"));
 +
 +#[test]
 +fn dogfood_clippy() {
 +    // run clippy on itself and fail the test if lint warnings are reported
 +    if cargo::is_rustc_test_suite() {
 +        return;
 +    }
 +    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
 +
 +    let mut command = Command::new(&*CLIPPY_PATH);
 +    command
 +        .current_dir(root_dir)
 +        .env("CLIPPY_DOGFOOD", "1")
 +        .env("CARGO_INCREMENTAL", "0")
 +        .arg("clippy")
 +        .arg("--all-targets")
 +        .arg("--all-features")
 +        .arg("--")
 +        .args(&["-D", "clippy::all"])
 +        .args(&["-D", "clippy::pedantic"])
 +        .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
 +
 +    // internal lints only exist if we build with the internal-lints feature
 +    if cfg!(feature = "internal-lints") {
 +        command.args(&["-D", "clippy::internal"]);
 +    }
 +
 +    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());
 +}
 +
 +fn test_no_deps_ignores_path_deps_in_workspaces() {
 +    if cargo::is_rustc_test_suite() {
 +        return;
 +    }
 +    let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
 +    let target_dir = root.join("target").join("dogfood");
 +    let cwd = root.join("clippy_workspace_tests");
 +
 +    // Make sure we start with a clean state
 +    Command::new("cargo")
 +        .current_dir(&cwd)
 +        .env("CARGO_TARGET_DIR", &target_dir)
 +        .arg("clean")
 +        .args(&["-p", "subcrate"])
 +        .args(&["-p", "path_dep"])
 +        .output()
 +        .unwrap();
 +
 +    // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint.
 +    // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`.
 +    let output = Command::new(&*CLIPPY_PATH)
 +        .current_dir(&cwd)
 +        .env("CLIPPY_DOGFOOD", "1")
 +        .env("CARGO_INCREMENTAL", "0")
 +        .arg("clippy")
 +        .args(&["-p", "subcrate"])
 +        .arg("--no-deps")
 +        .arg("--")
 +        .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
 +        .args(&["--cfg", r#"feature="primary_package_test""#])
 +        .output()
 +        .unwrap();
 +    println!("status: {}", output.status);
 +    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
 +    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
 +
 +    assert!(output.status.success());
 +
 +    let lint_path_dep = || {
 +        // Test that without the `--no-deps` argument, `path_dep` is linted.
 +        let output = Command::new(&*CLIPPY_PATH)
 +            .current_dir(&cwd)
 +            .env("CLIPPY_DOGFOOD", "1")
 +            .env("CARGO_INCREMENTAL", "0")
 +            .arg("clippy")
 +            .args(&["-p", "subcrate"])
 +            .arg("--")
 +            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
 +            .args(&["--cfg", r#"feature="primary_package_test""#])
 +            .output()
 +            .unwrap();
 +        println!("status: {}", output.status);
 +        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
 +        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
 +
 +        assert!(!output.status.success());
 +        assert!(
 +            String::from_utf8(output.stderr)
 +                .unwrap()
 +                .contains("error: empty `loop {}` wastes CPU cycles")
 +        );
 +    };
 +
 +    // Make sure Cargo is aware of the removal of `--no-deps`.
 +    lint_path_dep();
 +
 +    let successful_build = || {
 +        let output = Command::new(&*CLIPPY_PATH)
 +            .current_dir(&cwd)
 +            .env("CLIPPY_DOGFOOD", "1")
 +            .env("CARGO_INCREMENTAL", "0")
 +            .arg("clippy")
 +            .args(&["-p", "subcrate"])
 +            .arg("--")
 +            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
 +            .output()
 +            .unwrap();
 +        println!("status: {}", output.status);
 +        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
 +        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
 +
 +        assert!(output.status.success());
 +
 +        output
 +    };
 +
 +    // Trigger a sucessful build, so Cargo would like to cache the build result.
 +    successful_build();
 +
 +    // Make sure there's no spurious rebuild when nothing changes.
 +    let stderr = String::from_utf8(successful_build().stderr).unwrap();
 +    assert!(!stderr.contains("Compiling"));
 +    assert!(!stderr.contains("Checking"));
 +    assert!(stderr.contains("Finished"));
 +
 +    // Make sure Cargo is aware of the new `--cfg` flag.
 +    lint_path_dep();
 +}
 +
 +#[test]
 +fn dogfood_subprojects() {
 +    // run clippy on remaining subprojects and fail the test if lint warnings are reported
 +    if cargo::is_rustc_test_suite() {
 +        return;
 +    }
 +
 +    // NOTE: `path_dep` crate is omitted on purpose here
 +    for project in &[
 +        "clippy_workspace_tests",
 +        "clippy_workspace_tests/src",
 +        "clippy_workspace_tests/subcrate",
 +        "clippy_workspace_tests/subcrate/src",
 +        "clippy_dev",
 +        "clippy_lints",
 +        "clippy_utils",
 +        "rustc_tools_util",
 +    ] {
 +        run_clippy_for_project(project);
 +    }
 +
 +    // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the
 +    // same time, so we test this immediately after the dogfood for workspaces.
 +    test_no_deps_ignores_path_deps_in_workspaces();
 +}
 +
 +#[test]
 +#[ignore]
 +#[cfg(feature = "metadata-collector-lint")]
 +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_project("clippy_lints");
 +
 +    // Check if cargo caching got in the way
 +    if let Ok(file) = File::open(metadata_output_path) {
 +        if let Ok(metadata) = file.metadata() {
 +            if let Ok(last_modification) = metadata.modified() {
 +                if last_modification > start_time {
 +                    // The output file has been modified. Most likely by a hungry
 +                    // metadata collection monster. So We'll return.
 +                    return;
 +                }
 +            }
 +        }
 +    }
 +
 +    // Force cargo to invalidate the caches
 +    filetime::set_file_mtime(
 +        PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("clippy_lints/src/lib.rs"),
 +        filetime::FileTime::now(),
 +    )
 +    .unwrap();
 +
 +    // Running the collection again
 +    run_clippy_for_project("clippy_lints");
 +}
 +
 +fn run_clippy_for_project(project: &str) {
 +    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
 +
 +    let mut command = Command::new(&*CLIPPY_PATH);
 +
 +    command
 +        .current_dir(root_dir.join(project))
 +        .env("CLIPPY_DOGFOOD", "1")
 +        .env("CARGO_INCREMENTAL", "0")
 +        .arg("clippy")
 +        .arg("--all-targets")
 +        .arg("--all-features")
 +        .arg("--")
 +        .args(&["-D", "clippy::all"])
 +        .args(&["-D", "clippy::pedantic"])
 +        .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
 +
 +    // internal lints only exist if we build with the internal-lints feature
 +    if cfg!(feature = "internal-lints") {
 +        command.args(&["-D", "clippy::internal"]);
 +    }
 +
 +    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 7616d8001e8853bb8e9de4e44659d01b3253bd6f,0000000000000000000000000000000000000000..be42f1fbb2023b4bb5bd8d4e2937be64cafa6f80
mode 100644,000000..100644
--- /dev/null
@@@ -1,36 -1,0 +1,39 @@@
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++#![warn(rust_2018_idioms, unused_lifetimes)]
++
 +use std::path::PathBuf;
 +use std::process::Command;
 +
 +#[test]
 +fn fmt() {
 +    if option_env!("RUSTC_TEST_SUITE").is_some() || option_env!("NO_FMT_TEST").is_some() {
 +        return;
 +    }
 +
 +    // Skip this test if nightly rustfmt is unavailable
 +    let rustup_output = Command::new("rustup")
 +        .args(&["component", "list", "--toolchain", "nightly"])
 +        .output()
 +        .unwrap();
 +    assert!(rustup_output.status.success());
 +    let component_output = String::from_utf8_lossy(&rustup_output.stdout);
 +    if !component_output.contains("rustfmt") {
 +        return;
 +    }
 +
 +    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
 +    let output = Command::new("cargo")
 +        .current_dir(root_dir)
 +        .args(&["dev", "fmt", "--check"])
 +        .output()
 +        .unwrap();
 +
 +    println!("status: {}", output.status);
 +    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
 +    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
 +
 +    assert!(
 +        output.status.success(),
 +        "Formatting check failed. Run `cargo dev fmt` to update formatting."
 +    );
 +}
index 1718089e8bd27db39c2d7e4c64d15810547b988d,0000000000000000000000000000000000000000..7e3eff3c7324fc3b4f31b1d4443b576e3cd41883
mode 100644,000000..100644
--- /dev/null
@@@ -1,84 -1,0 +1,86 @@@
 +#![cfg(feature = "integration")]
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use std::env;
 +use std::ffi::OsStr;
 +use std::process::Command;
 +
 +#[cfg_attr(feature = "integration", test)]
 +fn integration_test() {
 +    let repo_name = env::var("INTEGRATION").expect("`INTEGRATION` var not set");
 +    let repo_url = format!("https://github.com/{}", repo_name);
 +    let crate_name = repo_name
 +        .split('/')
 +        .nth(1)
 +        .expect("repo name should have format `<org>/<name>`");
 +
 +    let mut repo_dir = tempfile::tempdir().expect("couldn't create temp dir").into_path();
 +    repo_dir.push(crate_name);
 +
 +    let st = Command::new("git")
 +        .args(&[
 +            OsStr::new("clone"),
 +            OsStr::new("--depth=1"),
 +            OsStr::new(&repo_url),
 +            OsStr::new(&repo_dir),
 +        ])
 +        .status()
 +        .expect("unable to run git");
 +    assert!(st.success());
 +
 +    let root_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
 +    let target_dir = std::path::Path::new(&root_dir).join("target");
 +    let clippy_binary = target_dir.join(env!("PROFILE")).join("cargo-clippy");
 +
 +    let output = Command::new(clippy_binary)
 +        .current_dir(repo_dir)
 +        .env("RUST_BACKTRACE", "full")
 +        .env("CARGO_TARGET_DIR", target_dir)
 +        .args(&[
 +            "clippy",
 +            "--all-targets",
 +            "--all-features",
 +            "--",
 +            "--cap-lints",
 +            "warn",
 +            "-Wclippy::pedantic",
 +            "-Wclippy::nursery",
 +        ])
 +        .output()
 +        .expect("unable to run clippy");
 +
 +    let stderr = String::from_utf8_lossy(&output.stderr);
 +    if stderr.contains("internal compiler error") {
 +        let backtrace_start = stderr
 +            .find("thread 'rustc' panicked at")
 +            .expect("start of backtrace not found");
 +        let backtrace_end = stderr
 +            .rfind("error: internal compiler error")
 +            .expect("end of backtrace not found");
 +
 +        panic!(
 +            "internal compiler error\nBacktrace:\n\n{}",
 +            &stderr[backtrace_start..backtrace_end]
 +        );
 +    } else if stderr.contains("query stack during panic") {
 +        panic!("query stack during panic in the output");
 +    } else if stderr.contains("E0463") {
 +        // Encountering E0463 (can't find crate for `x`) did _not_ cause the build to fail in the
 +        // past. Even though it should have. That's why we explicitly panic here.
 +        // See PR #3552 and issue #3523 for more background.
 +        panic!("error: E0463");
 +    } else if stderr.contains("E0514") {
 +        panic!("incompatible crate versions");
 +    } else if stderr.contains("failed to run `rustc` to learn about target-specific information") {
 +        panic!("couldn't find librustc_driver, consider setting `LD_LIBRARY_PATH`");
 +    } else if stderr.contains("toolchain") && stderr.contains("is not installed") {
 +        panic!("missing required toolchain");
 +    }
 +
 +    match output.status.code() {
 +        Some(0) => println!("Compilation successful"),
 +        Some(code) => eprintln!("Compilation failed. Exit code: {}", code),
 +        None => panic!("Process terminated by signal"),
 +    }
 +}
index 2f8989c8e114082eabd4957b8431f0a090d879a8,0000000000000000000000000000000000000000..b4d94dc983fec11fecc2efc20e8dbf006193ae07
mode 100644,000000..100644
--- /dev/null
@@@ -1,108 -1,0 +1,111 @@@
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++#![warn(rust_2018_idioms, unused_lifetimes)]
++
 +use std::ffi::OsStr;
 +use std::path::PathBuf;
 +
 +use regex::RegexSet;
 +
 +#[derive(Debug)]
 +struct Message {
 +    path: PathBuf,
 +    bad_lines: Vec<String>,
 +}
 +
 +impl Message {
 +    fn new(path: PathBuf) -> Self {
 +        let content: String = std::fs::read_to_string(&path).unwrap();
 +        // we don't want the first letter after "error: ", "help: " ... to be capitalized
 +        // also no puncutation (except for "?" ?) at the end of a line
 +        let regex_set: RegexSet = RegexSet::new(&[
 +            r"error: [A-Z]",
 +            r"help: [A-Z]",
 +            r"warning: [A-Z]",
 +            r"note: [A-Z]",
 +            r"try this: [A-Z]",
 +            r"error: .*[.!]$",
 +            r"help: .*[.!]$",
 +            r"warning: .*[.!]$",
 +            r"note: .*[.!]$",
 +            r"try this: .*[.!]$",
 +        ])
 +        .unwrap();
 +
 +        // sometimes the first character is capitalized and it is legal (like in "C-like enum variants") or
 +        // we want to ask a question ending in "?"
 +        let exceptions_set: RegexSet = RegexSet::new(&[
 +            r".*C-like enum variant discriminant is not portable to 32-bit targets",
 +            r".*did you mean `unix`?",
 +            r".*the arguments may be inverted...",
 +            r".*Intel x86 assembly syntax used",
 +            r".*AT&T x86 assembly syntax used",
 +            r".*remove .*the return type...",
 +            r"note: Clippy version: .*",
 +            r"the compiler unexpectedly panicked. this is a bug.",
 +        ])
 +        .unwrap();
 +
 +        let bad_lines = content
 +            .lines()
 +            .filter(|line| regex_set.matches(line).matched_any())
 +            // ignore exceptions
 +            .filter(|line| !exceptions_set.matches(line).matched_any())
 +            .map(ToOwned::to_owned)
 +            .collect::<Vec<String>>();
 +
 +        Message { path, bad_lines }
 +    }
 +}
 +
 +#[test]
 +fn lint_message_convention() {
 +    // disable the test inside the rustc test suite
 +    if option_env!("RUSTC_TEST_SUITE").is_some() {
 +        return;
 +    }
 +
 +    // make sure that lint messages:
 +    // * are not capitalized
 +    // * don't have puncuation 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 9cef7438d225cbc51cdedd1b471ece46bbb89e78,0000000000000000000000000000000000000000..bd342e390f52f3d065bdef329f2c082ed3f322c7
mode 100644,000000..100644
--- /dev/null
@@@ -1,54 -1,0 +1,56 @@@
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++#![warn(rust_2018_idioms, unused_lifetimes)]
 +#![allow(clippy::assertions_on_constants)]
 +
 +use std::fs::{self, DirEntry};
 +use std::path::Path;
 +
 +#[test]
 +fn test_missing_tests() {
 +    let missing_files = explore_directory(Path::new("./tests"));
 +    if !missing_files.is_empty() {
 +        assert!(
 +            false,
 +            "Didn't see a test file for the following files:\n\n{}\n",
 +            missing_files
 +                .iter()
 +                .map(|s| format!("\t{}", s))
 +                .collect::<Vec<_>>()
 +                .join("\n")
 +        );
 +    }
 +}
 +
 +/*
 +Test for missing files.
 +
 +Since rs files are alphabetically before stderr/stdout, we can sort by the full name
 +and iter in that order. If we've seen the file stem for the first time and it's not
 +a rust file, it means the rust file has to be missing.
 +*/
 +fn explore_directory(dir: &Path) -> Vec<String> {
 +    let mut missing_files: Vec<String> = Vec::new();
 +    let mut current_file = String::new();
 +    let mut files: Vec<DirEntry> = fs::read_dir(dir).unwrap().filter_map(Result::ok).collect();
 +    files.sort_by_key(std::fs::DirEntry::path);
 +    for entry in &files {
 +        let path = entry.path();
 +        if path.is_dir() {
 +            missing_files.extend(explore_directory(&path));
 +        } else {
 +            let file_stem = path.file_stem().unwrap().to_str().unwrap().to_string();
 +            if let Some(ext) = path.extension() {
 +                match ext.to_str().unwrap() {
 +                    "rs" => current_file = file_stem.clone(),
 +                    "stderr" | "stdout" => {
 +                        if file_stem != current_file {
 +                            missing_files.push(path.to_str().unwrap().to_string());
 +                        }
 +                    },
 +                    _ => continue,
 +                };
 +            }
 +        }
 +    }
 +    missing_files
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..97d51462a946fc3dd4d1c59acf17e5583b540858
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++
++# Content that triggers the lint goes here
++
++[package]
++name = "feature_name"
++version = "0.1.0"
++publish = false
++
++[workspace]
++
++[features]
++use-qwq = []
++use_qwq = []
++with-owo = []
++with_owo = []
++qvq-support = []
++qvq_support = []
++no-qaq = []
++no_qaq = []
++not-orz = []
++not_orz = []
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..64f01a98c90e9c0ab647d28b3a693cc86fd67ab9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++// compile-flags: --crate-name=feature_name
++#![warn(clippy::redundant_feature_names)]
++#![warn(clippy::negative_feature_names)]
++
++fn main() {
++    // test code goes here
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b9e6cb49bc982fbcb1f1b909d0d65a7eaaf61076
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++error: the "no-" prefix in the feature name "no-qaq" is negative
++   |
++   = note: `-D clippy::negative-feature-names` implied by `-D warnings`
++   = help: consider renaming the feature to "qaq", but make sure the feature adds functionality
++
++error: the "no_" prefix in the feature name "no_qaq" is negative
++   |
++   = help: consider renaming the feature to "qaq", but make sure the feature adds functionality
++
++error: the "not-" prefix in the feature name "not-orz" is negative
++   |
++   = help: consider renaming the feature to "orz", but make sure the feature adds functionality
++
++error: the "not_" prefix in the feature name "not_orz" is negative
++   |
++   = help: consider renaming the feature to "orz", but make sure the feature adds functionality
++
++error: the "-support" suffix in the feature name "qvq-support" is redundant
++   |
++   = note: `-D clippy::redundant-feature-names` implied by `-D warnings`
++   = help: consider renaming the feature to "qvq"
++
++error: the "_support" suffix in the feature name "qvq_support" is redundant
++   |
++   = help: consider renaming the feature to "qvq"
++
++error: the "use-" prefix in the feature name "use-qwq" is redundant
++   |
++   = help: consider renaming the feature to "qwq"
++
++error: the "use_" prefix in the feature name "use_qwq" is redundant
++   |
++   = help: consider renaming the feature to "qwq"
++
++error: the "with-" prefix in the feature name "with-owo" is redundant
++   |
++   = help: consider renaming the feature to "owo"
++
++error: the "with_" prefix in the feature name "with_owo" is redundant
++   |
++   = help: consider renaming the feature to "owo"
++
++error: aborting due to 10 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cf947312bd4798b38f721d1fa879a00d4cb461b2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++
++# This file should not trigger the lint
++
++[package]
++name = "feature_name"
++version = "0.1.0"
++publish = false
++
++[workspace]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..64f01a98c90e9c0ab647d28b3a693cc86fd67ab9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++// compile-flags: --crate-name=feature_name
++#![warn(clippy::redundant_feature_names)]
++#![warn(clippy::negative_feature_names)]
++
++fn main() {
++    // test code goes here
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..27b61c09fb48b597ee0f28be1fa6e19a48578613
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++[package]
++name = "fail"
++version = "0.1.0"
++edition = "2018"
++
++# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
++
++[dependencies]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..91cd540a28fddc60cc7427a55ad9713bae39c034
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++pub mod stuff;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7713fa9d35c4a357a2ac0696d72e9989c320e194
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++pub mod most;
++
++pub struct Inner;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5a5eaf9670f9012a40b67a6599ac5b0665cee131
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++pub struct Snarks;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a12734db7cb5f3175c32bfb2c518ee5f78a7b27f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++pub mod inner;
++
++pub struct Thing;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3e985d4e904c16984bb29ea97697979027380a79
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++#![warn(clippy::self_named_module_files)]
++
++mod bad;
++
++fn main() {
++    let _ = bad::Thing;
++    let _ = bad::inner::stuff::Inner;
++    let _ = bad::inner::stuff::most::Snarks;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..af4c298b310852bf997a072838f58a71efcfe3fb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++error: `mod.rs` files are required, found `/bad/inner.rs`
++  --> $DIR/bad/inner.rs:1:1
++   |
++LL | pub mod stuff;
++   | ^
++   |
++   = note: `-D clippy::self-named-module-files` implied by `-D warnings`
++   = help: move `/bad/inner.rs` to `/bad/inner/mod.rs`
++
++error: `mod.rs` files are required, found `/bad/inner/stuff.rs`
++  --> $DIR/bad/inner/stuff.rs:1:1
++   |
++LL | pub mod most;
++   | ^
++   |
++   = help: move `/bad/inner/stuff.rs` to `/bad/inner/stuff/mod.rs`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..27b61c09fb48b597ee0f28be1fa6e19a48578613
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++[package]
++name = "fail"
++version = "0.1.0"
++edition = "2018"
++
++# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
++
++[dependencies]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f19ab10d5fb06c14bd8138abdfe9d782e61dc85d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++pub struct Thing;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c6e9045b8dc47c6c0d0911a6e26eacbb23d4c0ea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++#![warn(clippy::mod_module_files)]
++
++mod bad;
++
++fn main() {
++    let _ = bad::Thing;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..11e15db7fb96b47829251b95126dae9af8791466
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++error: `mod.rs` files are not allowed, found `/bad/mod.rs`
++  --> $DIR/bad/mod.rs:1:1
++   |
++LL | pub struct Thing;
++   | ^
++   |
++   = note: `-D clippy::mod-module-files` implied by `-D warnings`
++   = help: move `/bad/mod.rs` to `/bad.rs`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..27b61c09fb48b597ee0f28be1fa6e19a48578613
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++[package]
++name = "fail"
++version = "0.1.0"
++edition = "2018"
++
++# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
++
++[dependencies]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f19ab10d5fb06c14bd8138abdfe9d782e61dc85d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++pub struct Thing;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9e08715fc0522b411db17af915e310cd7557671e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++#![warn(clippy::self_named_module_files)]
++
++mod bad;
++mod more;
++
++fn main() {
++    let _ = bad::Thing;
++    let _ = more::foo::Foo;
++    let _ = more::inner::Inner;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4a835673a596bcd05404f43d34082bf2785f2a9d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++pub struct Foo;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aa84f78cc2ca19a90feb696d2daecdbce92793d7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++pub struct Inner;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d79569f78ffbe60eea689a3c7afe69aaa9b1ff85
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++pub mod foo;
++pub mod inner;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3c0896dd2cda7d921dceca29a1ca11f6b94df6f8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++[package]
++name = "pass"
++version = "0.1.0"
++edition = "2018"
++
++# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
++
++[dependencies]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f19ab10d5fb06c14bd8138abdfe9d782e61dc85d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++pub struct Thing;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..50211a340b91b38e8d5a1f27daca27c84ad7e839
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++#![warn(clippy::mod_module_files)]
++
++mod good;
++
++fn main() {
++    let _ = good::Thing;
++}
index fb57a0becbb25e81b751bef0e1be0ddb0b559253,0000000000000000000000000000000000000000..2ae4d613507edce8b3c5c3145b6354c85fe64ba6
mode 100644,000000..100644
--- /dev/null
@@@ -1,60 -1,0 +1,64 @@@
 +#[warn(clippy::approx_constant)]
 +#[allow(unused, clippy::shadow_unrelated, clippy::similar_names)]
 +fn main() {
 +    let my_e = 2.7182;
 +    let almost_e = 2.718;
 +    let no_e = 2.71;
 +
 +    let my_1_frac_pi = 0.3183;
 +    let no_1_frac_pi = 0.31;
 +
 +    let my_frac_1_sqrt_2 = 0.70710678;
 +    let almost_frac_1_sqrt_2 = 0.70711;
 +    let my_frac_1_sqrt_2 = 0.707;
 +
 +    let my_frac_2_pi = 0.63661977;
 +    let no_frac_2_pi = 0.636;
 +
 +    let my_frac_2_sq_pi = 1.128379;
 +    let no_frac_2_sq_pi = 1.128;
 +
 +    let my_frac_pi_2 = 1.57079632679;
 +    let no_frac_pi_2 = 1.5705;
 +
 +    let my_frac_pi_3 = 1.04719755119;
 +    let no_frac_pi_3 = 1.047;
 +
 +    let my_frac_pi_4 = 0.785398163397;
 +    let no_frac_pi_4 = 0.785;
 +
 +    let my_frac_pi_6 = 0.523598775598;
 +    let no_frac_pi_6 = 0.523;
 +
 +    let my_frac_pi_8 = 0.3926990816987;
 +    let no_frac_pi_8 = 0.392;
 +
 +    let my_ln_10 = 2.302585092994046;
 +    let no_ln_10 = 2.303;
 +
 +    let my_ln_2 = 0.6931471805599453;
 +    let no_ln_2 = 0.693;
 +
 +    let my_log10_e = 0.4342944819032518;
 +    let no_log10_e = 0.434;
 +
 +    let my_log2_e = 1.4426950408889634;
 +    let no_log2_e = 1.442;
 +
 +    let log2_10 = 3.321928094887362;
 +    let no_log2_10 = 3.321;
 +
 +    let log10_2 = 0.301029995663981;
 +    let no_log10_2 = 0.301;
 +
 +    let my_pi = 3.1415;
 +    let almost_pi = 3.14;
 +    let no_pi = 3.15;
 +
 +    let my_sq2 = 1.4142;
 +    let no_sq2 = 1.414;
++
++    let my_tau = 6.2832;
++    let almost_tau = 6.28;
++    let no_tau = 6.3;
 +}
index 98b85443f0b70dcab0ab82e4a8b2e087cc1acb35,0000000000000000000000000000000000000000..4da1b8215ae0440bb6c90794c8c095364f66f130
mode 100644,000000..100644
--- /dev/null
@@@ -1,130 -1,0 +1,187 @@@
- error: approximate value of `f{32, 64}::consts::E` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::E` found
 +  --> $DIR/approx_const.rs:4:16
 +   |
 +LL |     let my_e = 2.7182;
 +   |                ^^^^^^
 +   |
 +   = note: `-D clippy::approx-constant` implied by `-D warnings`
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::E` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::E` found
 +  --> $DIR/approx_const.rs:5:20
 +   |
 +LL |     let almost_e = 2.718;
 +   |                    ^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::FRAC_1_PI` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::FRAC_1_PI` found
 +  --> $DIR/approx_const.rs:8:24
 +   |
 +LL |     let my_1_frac_pi = 0.3183;
 +   |                        ^^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found
 +  --> $DIR/approx_const.rs:11:28
 +   |
 +LL |     let my_frac_1_sqrt_2 = 0.70710678;
 +   |                            ^^^^^^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found
 +  --> $DIR/approx_const.rs:12:32
 +   |
 +LL |     let almost_frac_1_sqrt_2 = 0.70711;
 +   |                                ^^^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::FRAC_2_PI` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::FRAC_2_PI` found
 +  --> $DIR/approx_const.rs:15:24
 +   |
 +LL |     let my_frac_2_pi = 0.63661977;
 +   |                        ^^^^^^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::FRAC_2_SQRT_PI` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::FRAC_2_SQRT_PI` found
 +  --> $DIR/approx_const.rs:18:27
 +   |
 +LL |     let my_frac_2_sq_pi = 1.128379;
 +   |                           ^^^^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::FRAC_PI_2` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::FRAC_PI_2` found
 +  --> $DIR/approx_const.rs:21:24
 +   |
 +LL |     let my_frac_pi_2 = 1.57079632679;
 +   |                        ^^^^^^^^^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::FRAC_PI_3` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::FRAC_PI_3` found
 +  --> $DIR/approx_const.rs:24:24
 +   |
 +LL |     let my_frac_pi_3 = 1.04719755119;
 +   |                        ^^^^^^^^^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::FRAC_PI_4` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::FRAC_PI_4` found
 +  --> $DIR/approx_const.rs:27:24
 +   |
 +LL |     let my_frac_pi_4 = 0.785398163397;
 +   |                        ^^^^^^^^^^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::FRAC_PI_6` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::FRAC_PI_6` found
 +  --> $DIR/approx_const.rs:30:24
 +   |
 +LL |     let my_frac_pi_6 = 0.523598775598;
 +   |                        ^^^^^^^^^^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::FRAC_PI_8` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::FRAC_PI_8` found
 +  --> $DIR/approx_const.rs:33:24
 +   |
 +LL |     let my_frac_pi_8 = 0.3926990816987;
 +   |                        ^^^^^^^^^^^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::LN_10` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::LN_10` found
 +  --> $DIR/approx_const.rs:36:20
 +   |
 +LL |     let my_ln_10 = 2.302585092994046;
 +   |                    ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::LN_2` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::LN_2` found
 +  --> $DIR/approx_const.rs:39:19
 +   |
 +LL |     let my_ln_2 = 0.6931471805599453;
 +   |                   ^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::LOG10_E` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::LOG10_E` found
 +  --> $DIR/approx_const.rs:42:22
 +   |
 +LL |     let my_log10_e = 0.4342944819032518;
 +   |                      ^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::LOG2_E` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::LOG2_E` found
 +  --> $DIR/approx_const.rs:45:21
 +   |
 +LL |     let my_log2_e = 1.4426950408889634;
 +   |                     ^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::LOG2_10` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::LOG2_10` found
 +  --> $DIR/approx_const.rs:48:19
 +   |
 +LL |     let log2_10 = 3.321928094887362;
 +   |                   ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::LOG10_2` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::LOG10_2` found
 +  --> $DIR/approx_const.rs:51:19
 +   |
 +LL |     let log10_2 = 0.301029995663981;
 +   |                   ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::PI` found
 +  --> $DIR/approx_const.rs:54:17
 +   |
 +LL |     let my_pi = 3.1415;
 +   |                 ^^^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::PI` found
 +  --> $DIR/approx_const.rs:55:21
 +   |
 +LL |     let almost_pi = 3.14;
 +   |                     ^^^^
++   |
++   = help: consider using the constant directly
 +
- error: approximate value of `f{32, 64}::consts::SQRT_2` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::SQRT_2` found
 +  --> $DIR/approx_const.rs:58:18
 +   |
 +LL |     let my_sq2 = 1.4142;
 +   |                  ^^^^^^
++   |
++   = help: consider using the constant directly
++
++error: approximate value of `f{32, 64}::consts::TAU` found
++  --> $DIR/approx_const.rs:61:18
++   |
++LL |     let my_tau = 6.2832;
++   |                  ^^^^^^
++   |
++   = help: consider using the constant directly
++
++error: approximate value of `f{32, 64}::consts::TAU` found
++  --> $DIR/approx_const.rs:62:22
++   |
++LL |     let almost_tau = 6.28;
++   |                      ^^^^
++   |
++   = help: consider using the constant directly
 +
- error: aborting due to 21 previous errors
++error: aborting due to 23 previous errors
 +
index 7dc3f4ebd4d4681d47b4af94635ee7644f3a2452,0000000000000000000000000000000000000000..86a637ce3093c5ee936983cdc49c5477cab6cd52
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,64 @@@
 +#![allow(dead_code, unused_variables)]
 +
 +/// Utility macro to test linting behavior in `option_methods()`
 +/// The lints included in `option_methods()` should not lint if the call to map is partially
 +/// within a macro
 +#[macro_export]
 +macro_rules! opt_map {
 +    ($opt:expr, $map:expr) => {
 +        ($opt).map($map)
 +    };
 +}
 +
 +/// Struct to generate false positive for Iterator-based lints
 +#[derive(Copy, Clone)]
 +pub struct IteratorFalsePositives {
 +    pub foo: u32,
 +}
 +
 +impl IteratorFalsePositives {
 +    pub fn filter(self) -> IteratorFalsePositives {
 +        self
 +    }
 +
 +    pub fn next(self) -> IteratorFalsePositives {
 +        self
 +    }
 +
 +    pub fn find(self) -> Option<u32> {
 +        Some(self.foo)
 +    }
 +
 +    pub fn position(self) -> Option<u32> {
 +        Some(self.foo)
 +    }
 +
 +    pub fn rposition(self) -> Option<u32> {
 +        Some(self.foo)
 +    }
 +
 +    pub fn nth(self, n: usize) -> Option<u32> {
 +        Some(self.foo)
 +    }
 +
 +    pub fn skip(self, _: usize) -> IteratorFalsePositives {
 +        self
 +    }
 +
 +    pub fn skip_while(self) -> IteratorFalsePositives {
 +        self
 +    }
 +
 +    pub fn count(self) -> usize {
 +        self.foo as usize
 +    }
 +}
++
++#[derive(Copy, Clone)]
++pub struct IteratorMethodFalsePositives;
++
++impl IteratorMethodFalsePositives {
++    pub fn filter(&self, _s: i32) -> std::vec::IntoIter<i32> {
++        unimplemented!();
++    }
++}
index 2de402fae8c7de0d05f04a50f65ce5e3976bca61,0000000000000000000000000000000000000000..ec4d6f3ff840113829343f89f7ff45f2d9e45f8b
mode 100644,000000..100644
--- /dev/null
@@@ -1,59 -1,0 +1,122 @@@
 +#![warn(clippy::bool_assert_comparison)]
 +
++use std::ops::Not;
++
 +macro_rules! a {
 +    () => {
 +        true
 +    };
 +}
 +macro_rules! b {
 +    () => {
 +        true
 +    };
 +}
 +
++// Implements the Not trait but with an output type
++// that's not bool. Should not suggest a rewrite
++#[derive(Debug)]
++enum ImplNotTraitWithoutBool {
++    VariantX(bool),
++    VariantY(u32),
++}
++
++impl PartialEq<bool> for ImplNotTraitWithoutBool {
++    fn eq(&self, other: &bool) -> bool {
++        match *self {
++            ImplNotTraitWithoutBool::VariantX(b) => b == *other,
++            _ => false,
++        }
++    }
++}
++
++impl Not for ImplNotTraitWithoutBool {
++    type Output = Self;
++
++    fn not(self) -> Self::Output {
++        match self {
++            ImplNotTraitWithoutBool::VariantX(b) => ImplNotTraitWithoutBool::VariantX(!b),
++            ImplNotTraitWithoutBool::VariantY(0) => ImplNotTraitWithoutBool::VariantY(1),
++            ImplNotTraitWithoutBool::VariantY(_) => ImplNotTraitWithoutBool::VariantY(0),
++        }
++    }
++}
++
++// This type implements the Not trait with an Output of
++// type bool. Using assert!(..) must be suggested
++#[derive(Debug)]
++struct ImplNotTraitWithBool;
++
++impl PartialEq<bool> for ImplNotTraitWithBool {
++    fn eq(&self, other: &bool) -> bool {
++        false
++    }
++}
++
++impl Not for ImplNotTraitWithBool {
++    type Output = bool;
++
++    fn not(self) -> Self::Output {
++        true
++    }
++}
++
 +fn main() {
++    let a = ImplNotTraitWithoutBool::VariantX(true);
++    let b = ImplNotTraitWithBool;
++
 +    assert_eq!("a".len(), 1);
 +    assert_eq!("a".is_empty(), false);
 +    assert_eq!("".is_empty(), true);
 +    assert_eq!(true, "".is_empty());
 +    assert_eq!(a!(), b!());
 +    assert_eq!(a!(), "".is_empty());
 +    assert_eq!("".is_empty(), b!());
++    assert_eq!(a, true);
++    assert_eq!(b, true);
 +
 +    assert_ne!("a".len(), 1);
 +    assert_ne!("a".is_empty(), false);
 +    assert_ne!("".is_empty(), true);
 +    assert_ne!(true, "".is_empty());
 +    assert_ne!(a!(), b!());
 +    assert_ne!(a!(), "".is_empty());
 +    assert_ne!("".is_empty(), b!());
++    assert_ne!(a, true);
++    assert_ne!(b, true);
 +
 +    debug_assert_eq!("a".len(), 1);
 +    debug_assert_eq!("a".is_empty(), false);
 +    debug_assert_eq!("".is_empty(), true);
 +    debug_assert_eq!(true, "".is_empty());
 +    debug_assert_eq!(a!(), b!());
 +    debug_assert_eq!(a!(), "".is_empty());
 +    debug_assert_eq!("".is_empty(), b!());
++    debug_assert_eq!(a, true);
++    debug_assert_eq!(b, true);
 +
 +    debug_assert_ne!("a".len(), 1);
 +    debug_assert_ne!("a".is_empty(), false);
 +    debug_assert_ne!("".is_empty(), true);
 +    debug_assert_ne!(true, "".is_empty());
 +    debug_assert_ne!(a!(), b!());
 +    debug_assert_ne!(a!(), "".is_empty());
 +    debug_assert_ne!("".is_empty(), b!());
++    debug_assert_ne!(a, true);
++    debug_assert_ne!(b, true);
 +
 +    // assert with error messages
 +    assert_eq!("a".len(), 1, "tadam {}", 1);
 +    assert_eq!("a".len(), 1, "tadam {}", true);
 +    assert_eq!("a".is_empty(), false, "tadam {}", 1);
 +    assert_eq!("a".is_empty(), false, "tadam {}", true);
 +    assert_eq!(false, "a".is_empty(), "tadam {}", true);
++    assert_eq!(a, true, "tadam {}", false);
 +
 +    debug_assert_eq!("a".len(), 1, "tadam {}", 1);
 +    debug_assert_eq!("a".len(), 1, "tadam {}", true);
 +    debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
 +    debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
 +    debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
++    debug_assert_eq!(a, true, "tadam {}", false);
 +}
index f57acf520d5f12e111ce76b150fd4ad98768f1c4,0000000000000000000000000000000000000000..da9b56aa7795d4e9c2396dabf655764996fab801
mode 100644,000000..100644
--- /dev/null
@@@ -1,112 -1,0 +1,136 @@@
-   --> $DIR/bool_assert_comparison.rs:16:5
 +error: used `assert_eq!` with a literal bool
-   --> $DIR/bool_assert_comparison.rs:17:5
++  --> $DIR/bool_assert_comparison.rs:69:5
 +   |
 +LL |     assert_eq!("a".is_empty(), false);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
 +   |
 +   = note: `-D clippy::bool-assert-comparison` implied by `-D warnings`
 +
 +error: used `assert_eq!` with a literal bool
-   --> $DIR/bool_assert_comparison.rs:18:5
++  --> $DIR/bool_assert_comparison.rs:70:5
 +   |
 +LL |     assert_eq!("".is_empty(), true);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
 +
 +error: used `assert_eq!` with a literal bool
-   --> $DIR/bool_assert_comparison.rs:24:5
++  --> $DIR/bool_assert_comparison.rs:71:5
 +   |
 +LL |     assert_eq!(true, "".is_empty());
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
 +
++error: used `assert_eq!` with a literal bool
++  --> $DIR/bool_assert_comparison.rs:76:5
++   |
++LL |     assert_eq!(b, true);
++   |     ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
++
 +error: used `assert_ne!` with a literal bool
-   --> $DIR/bool_assert_comparison.rs:25:5
++  --> $DIR/bool_assert_comparison.rs:79:5
 +   |
 +LL |     assert_ne!("a".is_empty(), false);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
 +
 +error: used `assert_ne!` with a literal bool
-   --> $DIR/bool_assert_comparison.rs:26:5
++  --> $DIR/bool_assert_comparison.rs:80:5
 +   |
 +LL |     assert_ne!("".is_empty(), true);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
 +
 +error: used `assert_ne!` with a literal bool
-   --> $DIR/bool_assert_comparison.rs:32:5
++  --> $DIR/bool_assert_comparison.rs:81:5
 +   |
 +LL |     assert_ne!(true, "".is_empty());
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
 +
++error: used `assert_ne!` with a literal bool
++  --> $DIR/bool_assert_comparison.rs:86:5
++   |
++LL |     assert_ne!(b, true);
++   |     ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
++
 +error: used `debug_assert_eq!` with a literal bool
-   --> $DIR/bool_assert_comparison.rs:33:5
++  --> $DIR/bool_assert_comparison.rs:89:5
 +   |
 +LL |     debug_assert_eq!("a".is_empty(), false);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 +
 +error: used `debug_assert_eq!` with a literal bool
-   --> $DIR/bool_assert_comparison.rs:34:5
++  --> $DIR/bool_assert_comparison.rs:90:5
 +   |
 +LL |     debug_assert_eq!("".is_empty(), true);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 +
 +error: used `debug_assert_eq!` with a literal bool
-   --> $DIR/bool_assert_comparison.rs:40:5
++  --> $DIR/bool_assert_comparison.rs:91:5
 +   |
 +LL |     debug_assert_eq!(true, "".is_empty());
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 +
++error: used `debug_assert_eq!` with a literal bool
++  --> $DIR/bool_assert_comparison.rs:96:5
++   |
++LL |     debug_assert_eq!(b, true);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
++
 +error: used `debug_assert_ne!` with a literal bool
-   --> $DIR/bool_assert_comparison.rs:41:5
++  --> $DIR/bool_assert_comparison.rs:99:5
 +   |
 +LL |     debug_assert_ne!("a".is_empty(), false);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 +
 +error: used `debug_assert_ne!` with a literal bool
-   --> $DIR/bool_assert_comparison.rs:42:5
++  --> $DIR/bool_assert_comparison.rs:100:5
 +   |
 +LL |     debug_assert_ne!("".is_empty(), true);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 +
 +error: used `debug_assert_ne!` with a literal bool
-   --> $DIR/bool_assert_comparison.rs:50:5
++  --> $DIR/bool_assert_comparison.rs:101:5
 +   |
 +LL |     debug_assert_ne!(true, "".is_empty());
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 +
++error: used `debug_assert_ne!` with a literal bool
++  --> $DIR/bool_assert_comparison.rs:106:5
++   |
++LL |     debug_assert_ne!(b, true);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
++
 +error: used `assert_eq!` with a literal bool
-   --> $DIR/bool_assert_comparison.rs:51:5
++  --> $DIR/bool_assert_comparison.rs:111:5
 +   |
 +LL |     assert_eq!("a".is_empty(), false, "tadam {}", 1);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
 +
 +error: used `assert_eq!` with a literal bool
-   --> $DIR/bool_assert_comparison.rs:52:5
++  --> $DIR/bool_assert_comparison.rs:112:5
 +   |
 +LL |     assert_eq!("a".is_empty(), false, "tadam {}", true);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
 +
 +error: used `assert_eq!` with a literal bool
-   --> $DIR/bool_assert_comparison.rs:56:5
++  --> $DIR/bool_assert_comparison.rs:113:5
 +   |
 +LL |     assert_eq!(false, "a".is_empty(), "tadam {}", true);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
 +
 +error: used `debug_assert_eq!` with a literal bool
-   --> $DIR/bool_assert_comparison.rs:57:5
++  --> $DIR/bool_assert_comparison.rs:118:5
 +   |
 +LL |     debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 +
 +error: used `debug_assert_eq!` with a literal bool
-   --> $DIR/bool_assert_comparison.rs:58:5
++  --> $DIR/bool_assert_comparison.rs:119:5
 +   |
 +LL |     debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 +
 +error: used `debug_assert_eq!` with a literal bool
- error: aborting due to 18 previous errors
++  --> $DIR/bool_assert_comparison.rs:120:5
 +   |
 +LL |     debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 +
++error: aborting due to 22 previous errors
 +
index 87b67c23704c9e71ba4cec847a89cef943d8498e,0000000000000000000000000000000000000000..1d6366972dacfb529ce1e2295934d95adab58878
mode 100644,000000..100644
--- /dev/null
@@@ -1,32 -1,0 +1,36 @@@
- #![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
- #![allow(clippy::blacklisted_name)]
 +#![warn(clippy::all)]
- pub fn test(foo: Box<Vec<bool>>) {
-     println!("{:?}", foo.get(0))
- }
++#![allow(
++    clippy::boxed_local,
++    clippy::needless_pass_by_value,
++    clippy::blacklisted_name,
++    unused
++)]
 +
 +macro_rules! boxit {
 +    ($init:expr, $x:ty) => {
 +        let _: Box<$x> = Box::new($init);
 +    };
 +}
 +
 +fn test_macro() {
 +    boxit!(Vec::new(), Vec<u8>);
 +}
- pub fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
++fn test(foo: Box<Vec<bool>>) {}
 +
- pub fn test_local_not_linted() {
++fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
 +    // pass if #31 is fixed
 +    foo(vec![1, 2, 3])
 +}
 +
- fn main() {
-     test(Box::new(Vec::new()));
-     test2(Box::new(|v| println!("{:?}", v)));
-     test_macro();
-     test_local_not_linted();
++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 9b789334baeecb8db138d49f08db25a68509af78,0000000000000000000000000000000000000000..58c1f13fb877b1ba77deda63048b0ed41bf36314
mode 100644,000000..100644
--- /dev/null
@@@ -1,11 -1,0 +1,11 @@@
-   --> $DIR/box_vec.rs:14:18
 +error: you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`
- LL | pub fn test(foo: Box<Vec<bool>>) {
-    |                  ^^^^^^^^^^^^^^
++  --> $DIR/box_vec.rs:18:14
 +   |
++LL | fn test(foo: Box<Vec<bool>>) {}
++   |              ^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::box-vec` implied by `-D warnings`
 +   = help: `Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation
 +
 +error: aborting due to previous error
 +
index 33bb5136ef8e70ff8c5a24fd0a1f90fb9b75ac27,0000000000000000000000000000000000000000..46c6f69708eb798b8973c0db01b8424dd3b9f102
mode 100644,000000..100644
--- /dev/null
@@@ -1,192 -1,0 +1,211 @@@
- error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
++error: called `unwrap` on `x` after checking its variant with `is_ok`
 +  --> $DIR/complex_conditionals.rs:8:9
 +   |
 +LL |     if x.is_ok() && y.is_err() {
 +   |        --------- the check is happening here
 +LL |         x.unwrap(); // unnecessary
 +   |         ^^^^^^^^^^
 +   |
 +note: the lint level is defined here
 +  --> $DIR/complex_conditionals.rs:1:35
 +   |
 +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
 +   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
++   = help: try using `if let` or `match`
 +
 +error: this call to `unwrap_err()` will always panic
 +  --> $DIR/complex_conditionals.rs:9:9
 +   |
 +LL |     if x.is_ok() && y.is_err() {
 +   |        --------- because of this check
 +LL |         x.unwrap(); // unnecessary
 +LL |         x.unwrap_err(); // will panic
 +   |         ^^^^^^^^^^^^^^
 +   |
 +note: the lint level is defined here
 +  --> $DIR/complex_conditionals.rs:1:9
 +   |
 +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: this call to `unwrap()` will always panic
 +  --> $DIR/complex_conditionals.rs:10:9
 +   |
 +LL |     if x.is_ok() && y.is_err() {
 +   |                     ---------- because of this check
 +...
 +LL |         y.unwrap(); // will panic
 +   |         ^^^^^^^^^^
 +
- error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
++error: called `unwrap_err` on `y` after checking its variant with `is_err`
 +  --> $DIR/complex_conditionals.rs:11:9
 +   |
 +LL |     if x.is_ok() && y.is_err() {
 +   |                     ---------- the check is happening here
 +...
 +LL |         y.unwrap_err(); // unnecessary
 +   |         ^^^^^^^^^^^^^^
++   |
++   = help: try using `if let` or `match`
 +
 +error: this call to `unwrap()` will always panic
 +  --> $DIR/complex_conditionals.rs:25:9
 +   |
 +LL |     if x.is_ok() || y.is_ok() {
 +   |        --------- because of this check
 +...
 +LL |         x.unwrap(); // will panic
 +   |         ^^^^^^^^^^
 +
- error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
++error: called `unwrap_err` on `x` after checking its variant with `is_ok`
 +  --> $DIR/complex_conditionals.rs:26:9
 +   |
 +LL |     if x.is_ok() || y.is_ok() {
 +   |        --------- the check is happening here
 +...
 +LL |         x.unwrap_err(); // unnecessary
 +   |         ^^^^^^^^^^^^^^
++   |
++   = help: try using `if let` or `match`
 +
 +error: this call to `unwrap()` will always panic
 +  --> $DIR/complex_conditionals.rs:27:9
 +   |
 +LL |     if x.is_ok() || y.is_ok() {
 +   |                     --------- because of this check
 +...
 +LL |         y.unwrap(); // will panic
 +   |         ^^^^^^^^^^
 +
- error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
++error: called `unwrap_err` on `y` after checking its variant with `is_ok`
 +  --> $DIR/complex_conditionals.rs:28:9
 +   |
 +LL |     if x.is_ok() || y.is_ok() {
 +   |                     --------- the check is happening here
 +...
 +LL |         y.unwrap_err(); // unnecessary
 +   |         ^^^^^^^^^^^^^^
++   |
++   = help: try using `if let` or `match`
 +
- error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
++error: called `unwrap` on `x` after checking its variant with `is_ok`
 +  --> $DIR/complex_conditionals.rs:32:9
 +   |
 +LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
 +   |        --------- the check is happening here
 +LL |         x.unwrap(); // unnecessary
 +   |         ^^^^^^^^^^
++   |
++   = help: try using `if let` or `match`
 +
 +error: this call to `unwrap_err()` will always panic
 +  --> $DIR/complex_conditionals.rs:33:9
 +   |
 +LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
 +   |        --------- because of this check
 +LL |         x.unwrap(); // unnecessary
 +LL |         x.unwrap_err(); // will panic
 +   |         ^^^^^^^^^^^^^^
 +
 +error: this call to `unwrap()` will always panic
 +  --> $DIR/complex_conditionals.rs:34:9
 +   |
 +LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
 +   |                       --------- because of this check
 +...
 +LL |         y.unwrap(); // will panic
 +   |         ^^^^^^^^^^
 +
- error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
++error: called `unwrap_err` on `y` after checking its variant with `is_ok`
 +  --> $DIR/complex_conditionals.rs:35:9
 +   |
 +LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
 +   |                       --------- the check is happening here
 +...
 +LL |         y.unwrap_err(); // unnecessary
 +   |         ^^^^^^^^^^^^^^
++   |
++   = help: try using `if let` or `match`
 +
- error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
++error: called `unwrap` on `z` after checking its variant with `is_err`
 +  --> $DIR/complex_conditionals.rs:36:9
 +   |
 +LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
 +   |                                    ---------- the check is happening here
 +...
 +LL |         z.unwrap(); // unnecessary
 +   |         ^^^^^^^^^^
++   |
++   = help: try using `if let` or `match`
 +
 +error: this call to `unwrap_err()` will always panic
 +  --> $DIR/complex_conditionals.rs:37:9
 +   |
 +LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
 +   |                                    ---------- because of this check
 +...
 +LL |         z.unwrap_err(); // will panic
 +   |         ^^^^^^^^^^^^^^
 +
 +error: this call to `unwrap()` will always panic
 +  --> $DIR/complex_conditionals.rs:45:9
 +   |
 +LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
 +   |        --------- because of this check
 +...
 +LL |         x.unwrap(); // will panic
 +   |         ^^^^^^^^^^
 +
- error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
++error: called `unwrap_err` on `x` after checking its variant with `is_ok`
 +  --> $DIR/complex_conditionals.rs:46:9
 +   |
 +LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
 +   |        --------- the check is happening here
 +...
 +LL |         x.unwrap_err(); // unnecessary
 +   |         ^^^^^^^^^^^^^^
++   |
++   = help: try using `if let` or `match`
 +
- error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
++error: called `unwrap` on `y` after checking its variant with `is_ok`
 +  --> $DIR/complex_conditionals.rs:47:9
 +   |
 +LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
 +   |                       --------- the check is happening here
 +...
 +LL |         y.unwrap(); // unnecessary
 +   |         ^^^^^^^^^^
++   |
++   = help: try using `if let` or `match`
 +
 +error: this call to `unwrap_err()` will always panic
 +  --> $DIR/complex_conditionals.rs:48:9
 +   |
 +LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
 +   |                       --------- because of this check
 +...
 +LL |         y.unwrap_err(); // will panic
 +   |         ^^^^^^^^^^^^^^
 +
 +error: this call to `unwrap()` will always panic
 +  --> $DIR/complex_conditionals.rs:49:9
 +   |
 +LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
 +   |                                    ---------- because of this check
 +...
 +LL |         z.unwrap(); // will panic
 +   |         ^^^^^^^^^^
 +
- error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
++error: called `unwrap_err` on `z` after checking its variant with `is_err`
 +  --> $DIR/complex_conditionals.rs:50:9
 +   |
 +LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
 +   |                                    ---------- the check is happening here
 +...
 +LL |         z.unwrap_err(); // unnecessary
 +   |         ^^^^^^^^^^^^^^
++   |
++   = help: try using `if let` or `match`
 +
 +error: aborting due to 20 previous errors
 +
index a01f7f956f629c80ac8affb2f702a1b148b794d2,0000000000000000000000000000000000000000..542ab53300c029d44641bcb1b78d34752f8c1698
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,31 @@@
- error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
++error: called `unwrap` on `x` after checking its variant with `is_some`
 +  --> $DIR/complex_conditionals_nested.rs:8:13
 +   |
 +LL |         if x.is_some() {
-    |            ----------- the check is happening here
++   |         -------------- help: try: `if let Some(..) = x`
 +LL |             x.unwrap(); // unnecessary
 +   |             ^^^^^^^^^^
 +   |
 +note: the lint level is defined here
 +  --> $DIR/complex_conditionals_nested.rs:1:35
 +   |
 +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
 +   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: this call to `unwrap()` will always panic
 +  --> $DIR/complex_conditionals_nested.rs:10:13
 +   |
 +LL |         if x.is_some() {
 +   |            ----------- because of this check
 +...
 +LL |             x.unwrap(); // will panic
 +   |             ^^^^^^^^^^
 +   |
 +note: the lint level is defined here
 +  --> $DIR/complex_conditionals_nested.rs:1:9
 +   |
 +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: aborting due to 2 previous errors
 +
index 8f23fb28827a23a8841a70f4743dca2067c0ee6e,0000000000000000000000000000000000000000..ee3fdfabe9d8a8e255d5966ce924cb87f2744ae7
mode 100644,000000..100644
--- /dev/null
@@@ -1,82 -1,0 +1,86 @@@
 +#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
 +#![allow(clippy::if_same_then_else, clippy::branches_sharing_code)]
 +
 +macro_rules! m {
 +    ($a:expr) => {
 +        if $a.is_some() {
 +            $a.unwrap(); // unnecessary
 +        }
 +    };
 +}
 +
 +macro_rules! checks_in_param {
 +    ($a:expr, $b:expr) => {
 +        if $a {
 +            $b;
 +        }
 +    };
 +}
 +
 +macro_rules! checks_unwrap {
 +    ($a:expr, $b:expr) => {
 +        if $a.is_some() {
 +            $b;
 +        }
 +    };
 +}
 +
 +macro_rules! checks_some {
 +    ($a:expr, $b:expr) => {
 +        if $a {
 +            $b.unwrap();
 +        }
 +    };
 +}
 +
 +fn main() {
 +    let x = Some(());
 +    if x.is_some() {
 +        x.unwrap(); // unnecessary
++        x.expect("an error message"); // unnecessary
 +    } else {
 +        x.unwrap(); // will panic
++        x.expect("an error message"); // will panic
 +    }
 +    if x.is_none() {
 +        x.unwrap(); // will panic
 +    } else {
 +        x.unwrap(); // unnecessary
 +    }
 +    m!(x);
 +    checks_in_param!(x.is_some(), x.unwrap()); // ok
 +    checks_unwrap!(x, x.unwrap()); // ok
 +    checks_some!(x.is_some(), x); // ok
 +    let mut x: Result<(), ()> = Ok(());
 +    if x.is_ok() {
 +        x.unwrap(); // unnecessary
++        x.expect("an error message"); // unnecessary
 +        x.unwrap_err(); // will panic
 +    } else {
 +        x.unwrap(); // will panic
++        x.expect("an error message"); // will panic
 +        x.unwrap_err(); // unnecessary
 +    }
 +    if x.is_err() {
 +        x.unwrap(); // will panic
 +        x.unwrap_err(); // unnecessary
 +    } else {
 +        x.unwrap(); // unnecessary
 +        x.unwrap_err(); // will panic
 +    }
 +    if x.is_ok() {
 +        x = Err(());
 +        // not unnecessary because of mutation of x
 +        // it will always panic but the lint is not smart enough to see this (it only
 +        // checks if conditions).
 +        x.unwrap();
 +    } else {
 +        x = Ok(());
 +        // not unnecessary because of mutation of x
 +        // it will always panic but the lint is not smart enough to see this (it
 +        // only checks if conditions).
 +        x.unwrap_err();
 +    }
 +
 +    assert!(x.is_ok(), "{:?}", x.unwrap_err()); // ok, it's a common test pattern
 +}
index a4bc058fe2020b2263e5323c55160e56de9750ae,0000000000000000000000000000000000000000..82f269543800f3dfc62b006bd79a8e141270fca6
mode 100644,000000..100644
--- /dev/null
@@@ -1,131 -1,0 +1,167 @@@
- error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
++error: called `unwrap` on `x` after checking its variant with `is_some`
 +  --> $DIR/simple_conditionals.rs:39:9
 +   |
 +LL |     if x.is_some() {
-    |        ----------- the check is happening here
++   |     -------------- help: try: `if let Some(..) = x`
 +LL |         x.unwrap(); // unnecessary
 +   |         ^^^^^^^^^^
 +   |
 +note: the lint level is defined here
 +  --> $DIR/simple_conditionals.rs:1:35
 +   |
 +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
 +   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
++error: called `expect` on `x` after checking its variant with `is_some`
++  --> $DIR/simple_conditionals.rs:40:9
++   |
++LL |     if x.is_some() {
++   |     -------------- help: try: `if let Some(..) = x`
++LL |         x.unwrap(); // unnecessary
++LL |         x.expect("an error message"); // unnecessary
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
 +error: this call to `unwrap()` will always panic
-   --> $DIR/simple_conditionals.rs:41:9
++  --> $DIR/simple_conditionals.rs:42:9
 +   |
 +LL |     if x.is_some() {
 +   |        ----------- because of this check
 +...
 +LL |         x.unwrap(); // will panic
 +   |         ^^^^^^^^^^
 +   |
 +note: the lint level is defined here
 +  --> $DIR/simple_conditionals.rs:1:9
 +   |
 +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^
 +
++error: this call to `expect()` will always panic
++  --> $DIR/simple_conditionals.rs:43:9
++   |
++LL |     if x.is_some() {
++   |        ----------- because of this check
++...
++LL |         x.expect("an error message"); // will panic
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
 +error: this call to `unwrap()` will always panic
-   --> $DIR/simple_conditionals.rs:44:9
++  --> $DIR/simple_conditionals.rs:46:9
 +   |
 +LL |     if x.is_none() {
 +   |        ----------- because of this check
 +LL |         x.unwrap(); // will panic
 +   |         ^^^^^^^^^^
 +
- error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
-   --> $DIR/simple_conditionals.rs:46:9
++error: called `unwrap` on `x` after checking its variant with `is_none`
++  --> $DIR/simple_conditionals.rs:48:9
 +   |
 +LL |     if x.is_none() {
-    |        ----------- the check is happening here
++   |     -------------- help: try: `if let Some(..) = x`
 +...
 +LL |         x.unwrap(); // unnecessary
 +   |         ^^^^^^^^^^
 +
- error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
++error: called `unwrap` on `x` after checking its variant with `is_some`
 +  --> $DIR/simple_conditionals.rs:7:13
 +   |
 +LL |         if $a.is_some() {
-    |            ------------ the check is happening here
++   |         --------------- help: try: `if let Some(..) = x`
 +LL |             $a.unwrap(); // unnecessary
 +   |             ^^^^^^^^^^^
 +...
 +LL |     m!(x);
 +   |     ------ in this macro invocation
 +   |
 +   = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
- error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
-   --> $DIR/simple_conditionals.rs:54:9
++error: called `unwrap` on `x` after checking its variant with `is_ok`
++  --> $DIR/simple_conditionals.rs:56:9
 +   |
 +LL |     if x.is_ok() {
-    |        --------- the check is happening here
++   |     ------------ help: try: `if let Ok(..) = x`
 +LL |         x.unwrap(); // unnecessary
 +   |         ^^^^^^^^^^
 +
++error: called `expect` on `x` after checking its variant with `is_ok`
++  --> $DIR/simple_conditionals.rs:57:9
++   |
++LL |     if x.is_ok() {
++   |     ------------ help: try: `if let Ok(..) = x`
++LL |         x.unwrap(); // unnecessary
++LL |         x.expect("an error message"); // unnecessary
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
 +error: this call to `unwrap_err()` will always panic
-   --> $DIR/simple_conditionals.rs:55:9
++  --> $DIR/simple_conditionals.rs:58:9
 +   |
 +LL |     if x.is_ok() {
 +   |        --------- because of this check
- LL |         x.unwrap(); // unnecessary
++...
 +LL |         x.unwrap_err(); // will panic
 +   |         ^^^^^^^^^^^^^^
 +
 +error: this call to `unwrap()` will always panic
-   --> $DIR/simple_conditionals.rs:57:9
++  --> $DIR/simple_conditionals.rs:60:9
 +   |
 +LL |     if x.is_ok() {
 +   |        --------- because of this check
 +...
 +LL |         x.unwrap(); // will panic
 +   |         ^^^^^^^^^^
 +
- error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
-   --> $DIR/simple_conditionals.rs:58:9
++error: this call to `expect()` will always panic
++  --> $DIR/simple_conditionals.rs:61:9
++   |
++LL |     if x.is_ok() {
++   |        --------- because of this check
++...
++LL |         x.expect("an error message"); // will panic
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: called `unwrap_err` on `x` after checking its variant with `is_ok`
++  --> $DIR/simple_conditionals.rs:62:9
 +   |
 +LL |     if x.is_ok() {
-    |        --------- the check is happening here
++   |     ------------ help: try: `if let Err(..) = x`
 +...
 +LL |         x.unwrap_err(); // unnecessary
 +   |         ^^^^^^^^^^^^^^
 +
 +error: this call to `unwrap()` will always panic
-   --> $DIR/simple_conditionals.rs:61:9
++  --> $DIR/simple_conditionals.rs:65:9
 +   |
 +LL |     if x.is_err() {
 +   |        ---------- because of this check
 +LL |         x.unwrap(); // will panic
 +   |         ^^^^^^^^^^
 +
- error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
-   --> $DIR/simple_conditionals.rs:62:9
++error: called `unwrap_err` on `x` after checking its variant with `is_err`
++  --> $DIR/simple_conditionals.rs:66:9
 +   |
 +LL |     if x.is_err() {
-    |        ---------- the check is happening here
++   |     ------------- help: try: `if let Err(..) = x`
 +LL |         x.unwrap(); // will panic
 +LL |         x.unwrap_err(); // unnecessary
 +   |         ^^^^^^^^^^^^^^
 +
- error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
-   --> $DIR/simple_conditionals.rs:64:9
++error: called `unwrap` on `x` after checking its variant with `is_err`
++  --> $DIR/simple_conditionals.rs:68:9
 +   |
 +LL |     if x.is_err() {
-    |        ---------- the check is happening here
++   |     ------------- help: try: `if let Ok(..) = x`
 +...
 +LL |         x.unwrap(); // unnecessary
 +   |         ^^^^^^^^^^
 +
 +error: this call to `unwrap_err()` will always panic
-   --> $DIR/simple_conditionals.rs:65:9
++  --> $DIR/simple_conditionals.rs:69:9
 +   |
 +LL |     if x.is_err() {
 +   |        ---------- because of this check
 +...
 +LL |         x.unwrap_err(); // will panic
 +   |         ^^^^^^^^^^^^^^
 +
- error: aborting due to 13 previous errors
++error: aborting due to 17 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..336a743de726bf009387beaf5982b7a5c5a37285
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,170 @@@
++use std::collections::HashMap;
++
++struct FooDefault<'a> {
++    a: bool,
++    b: i32,
++    c: u64,
++    d: Vec<i32>,
++    e: FooND1,
++    f: FooND2,
++    g: HashMap<i32, i32>,
++    h: (i32, Vec<i32>),
++    i: [Vec<i32>; 3],
++    j: [i32; 5],
++    k: Option<i32>,
++    l: &'a [i32],
++}
++
++impl std::default::Default for FooDefault<'_> {
++    fn default() -> Self {
++        Self {
++            a: false,
++            b: 0,
++            c: 0u64,
++            d: vec![],
++            e: Default::default(),
++            f: FooND2::default(),
++            g: HashMap::new(),
++            h: (0, vec![]),
++            i: [vec![], vec![], vec![]],
++            j: [0; 5],
++            k: None,
++            l: &[],
++        }
++    }
++}
++
++struct TupleDefault(bool, i32, u64);
++
++impl std::default::Default for TupleDefault {
++    fn default() -> Self {
++        Self(false, 0, 0u64)
++    }
++}
++
++struct FooND1 {
++    a: bool,
++}
++
++impl std::default::Default for FooND1 {
++    fn default() -> Self {
++        Self { a: true }
++    }
++}
++
++struct FooND2 {
++    a: i32,
++}
++
++impl std::default::Default for FooND2 {
++    fn default() -> Self {
++        Self { a: 5 }
++    }
++}
++
++struct FooNDNew {
++    a: bool,
++}
++
++impl FooNDNew {
++    fn new() -> Self {
++        Self { a: true }
++    }
++}
++
++impl Default for FooNDNew {
++    fn default() -> Self {
++        Self::new()
++    }
++}
++
++struct FooNDVec(Vec<i32>);
++
++impl Default for FooNDVec {
++    fn default() -> Self {
++        Self(vec![5, 12])
++    }
++}
++
++struct StrDefault<'a>(&'a str);
++
++impl Default for StrDefault<'_> {
++    fn default() -> Self {
++        Self("")
++    }
++}
++
++#[derive(Default)]
++struct AlreadyDerived(i32, bool);
++
++macro_rules! mac {
++    () => {
++        0
++    };
++    ($e:expr) => {
++        struct X(u32);
++        impl Default for X {
++            fn default() -> Self {
++                Self($e)
++            }
++        }
++    };
++}
++
++mac!(0);
++
++struct Y(u32);
++impl Default for Y {
++    fn default() -> Self {
++        Self(mac!())
++    }
++}
++
++struct RustIssue26925<T> {
++    a: Option<T>,
++}
++
++// We should watch out for cases where a manual impl is needed because a
++// derive adds different type bounds (https://github.com/rust-lang/rust/issues/26925).
++// For example, a struct with Option<T> does not require T: Default, but a derive adds
++// that type bound anyways. So until #26925 get fixed we should disable lint
++// for the following case
++impl<T> Default for RustIssue26925<T> {
++    fn default() -> Self {
++        Self { a: None }
++    }
++}
++
++struct SpecializedImpl<A, B> {
++    a: A,
++    b: B,
++}
++
++impl<T: Default> Default for SpecializedImpl<T, T> {
++    fn default() -> Self {
++        Self {
++            a: T::default(),
++            b: T::default(),
++        }
++    }
++}
++
++struct WithoutSelfCurly {
++    a: bool,
++}
++
++impl Default for WithoutSelfCurly {
++    fn default() -> Self {
++        WithoutSelfCurly { a: false }
++    }
++}
++
++struct WithoutSelfParan(bool);
++
++impl Default for WithoutSelfParan {
++    fn default() -> Self {
++        WithoutSelfParan(false)
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4ed64fade026d1379667ccbff05bb1b7c20038fe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,77 @@@
++error: this `impl` can be derived
++  --> $DIR/derivable_impls.rs:18:1
++   |
++LL | / impl std::default::Default for FooDefault<'_> {
++LL | |     fn default() -> Self {
++LL | |         Self {
++LL | |             a: false,
++...  |
++LL | |     }
++LL | | }
++   | |_^
++   |
++   = note: `-D clippy::derivable-impls` implied by `-D warnings`
++   = help: try annotating `FooDefault` with `#[derive(Default)]`
++
++error: this `impl` can be derived
++  --> $DIR/derivable_impls.rs:39:1
++   |
++LL | / impl std::default::Default for TupleDefault {
++LL | |     fn default() -> Self {
++LL | |         Self(false, 0, 0u64)
++LL | |     }
++LL | | }
++   | |_^
++   |
++   = help: try annotating `TupleDefault` with `#[derive(Default)]`
++
++error: this `impl` can be derived
++  --> $DIR/derivable_impls.rs:91:1
++   |
++LL | / impl Default for StrDefault<'_> {
++LL | |     fn default() -> Self {
++LL | |         Self("")
++LL | |     }
++LL | | }
++   | |_^
++   |
++   = help: try annotating `StrDefault` with `#[derive(Default)]`
++
++error: this `impl` can be derived
++  --> $DIR/derivable_impls.rs:117:1
++   |
++LL | / impl Default for Y {
++LL | |     fn default() -> Self {
++LL | |         Self(mac!())
++LL | |     }
++LL | | }
++   | |_^
++   |
++   = help: try annotating `Y` with `#[derive(Default)]`
++
++error: this `impl` can be derived
++  --> $DIR/derivable_impls.rs:156:1
++   |
++LL | / impl Default for WithoutSelfCurly {
++LL | |     fn default() -> Self {
++LL | |         WithoutSelfCurly { a: false }
++LL | |     }
++LL | | }
++   | |_^
++   |
++   = help: try annotating `WithoutSelfCurly` with `#[derive(Default)]`
++
++error: this `impl` can be derived
++  --> $DIR/derivable_impls.rs:164:1
++   |
++LL | / impl Default for WithoutSelfParan {
++LL | |     fn default() -> Self {
++LL | |         WithoutSelfParan(false)
++LL | |     }
++LL | | }
++   | |_^
++   |
++   = help: try annotating `WithoutSelfParan` with `#[derive(Default)]`
++
++error: aborting due to 6 previous errors
++
index cfad3090ba38d2bc7a0a5482c0c97084757f4fee,0000000000000000000000000000000000000000..8a36ec833d76d3aa331b120d356507efe46168e1
mode 100644,000000..100644
--- /dev/null
@@@ -1,155 -1,0 +1,154 @@@
- use std::collections::{BTreeMap, HashMap};
 +// run-rustfix
 +
 +#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
 +#![warn(clippy::map_entry)]
 +#![feature(asm)]
 +
- }
++use std::collections::HashMap;
 +use std::hash::Hash;
 +
 +macro_rules! m {
 +    ($e:expr) => {{ $e }};
 +}
 +
 +macro_rules! insert {
 +    ($map:expr, $key:expr, $val:expr) => {
 +        $map.insert($key, $val)
 +    };
 +}
 +
 +fn foo() {}
 +
 +fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMap<K, V>, k: K, k2: K, v: V, v2: V) {
 +    // or_insert(v)
 +    m.entry(k).or_insert(v);
 +
 +    // semicolon on insert, use or_insert_with(..)
 +    m.entry(k).or_insert_with(|| {
 +        if true {
 +            v
 +        } else {
 +            v2
 +        }
 +    });
 +
 +    // semicolon on if, use or_insert_with(..)
 +    m.entry(k).or_insert_with(|| {
 +        if true {
 +            v
 +        } else {
 +            v2
 +        }
 +    });
 +
 +    // early return, use if let
 +    if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
 +        if true {
 +            e.insert(v);
 +        } else {
 +            e.insert(v2);
 +            return;
 +        }
 +    }
 +
 +    // use or_insert_with(..)
 +    m.entry(k).or_insert_with(|| {
 +        foo();
 +        v
 +    });
 +
 +    // semicolon on insert and match, use or_insert_with(..)
 +    m.entry(k).or_insert_with(|| {
 +        match 0 {
 +            1 if true => {
 +                v
 +            },
 +            _ => {
 +                v2
 +            },
 +        }
 +    });
 +
 +    // one branch doesn't insert, use if let
 +    if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
 +        match 0 {
 +            0 => foo(),
 +            _ => {
 +                e.insert(v2);
 +            },
 +        };
 +    }
 +
 +    // use or_insert_with
 +    m.entry(k).or_insert_with(|| {
 +        foo();
 +        match 0 {
 +            0 if false => {
 +                v
 +            },
 +            1 => {
 +                foo();
 +                v
 +            },
 +            2 | 3 => {
 +                for _ in 0..2 {
 +                    foo();
 +                }
 +                if true {
 +                    v
 +                } else {
 +                    v2
 +                }
 +            },
 +            _ => {
 +                v2
 +            },
 +        }
 +    });
 +
 +    // ok, insert in loop
 +    if !m.contains_key(&k) {
 +        for _ in 0..2 {
 +            m.insert(k, v);
 +        }
 +    }
 +
 +    // macro_expansion test, use or_insert(..)
 +    m.entry(m!(k)).or_insert_with(|| m!(v));
 +
 +    // ok, map used before insertion
 +    if !m.contains_key(&k) {
 +        let _ = m.len();
 +        m.insert(k, v);
 +    }
 +
 +    // ok, inline asm
 +    if !m.contains_key(&k) {
 +        unsafe { asm!("nop") }
 +        m.insert(k, v);
 +    }
 +
 +    // ok, different keys.
 +    if !m.contains_key(&k) {
 +        m.insert(k2, v);
 +    }
 +
 +    // ok, different maps
 +    if !m.contains_key(&k) {
 +        m2.insert(k, v);
 +    }
 +
 +    // ok, insert in macro
 +    if !m.contains_key(&k) {
 +        insert!(m, k, v);
 +    }
- fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V, v2: V) {
-     // insert then do something, use if let
-     if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
-         e.insert(v);
-         foo();
-     }
 +
++    // or_insert_with. Partial move of a local declared in the closure is ok.
++    m.entry(k).or_insert_with(|| {
++        let x = (String::new(), String::new());
++        let _ = x.0;
++        v
++    });
 +}
 +
 +fn main() {}
index fa9280b58de11cd513d6ced8c6ef0a8fe81b5cdf,0000000000000000000000000000000000000000..d972a201ad76460e401c6a0ff613eab5e4f53af9
mode 100644,000000..100644
--- /dev/null
@@@ -1,159 -1,0 +1,158 @@@
- use std::collections::{BTreeMap, HashMap};
 +// run-rustfix
 +
 +#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
 +#![warn(clippy::map_entry)]
 +#![feature(asm)]
 +
- }
++use std::collections::HashMap;
 +use std::hash::Hash;
 +
 +macro_rules! m {
 +    ($e:expr) => {{ $e }};
 +}
 +
 +macro_rules! insert {
 +    ($map:expr, $key:expr, $val:expr) => {
 +        $map.insert($key, $val)
 +    };
 +}
 +
 +fn foo() {}
 +
 +fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMap<K, V>, k: K, k2: K, v: V, v2: V) {
 +    // or_insert(v)
 +    if !m.contains_key(&k) {
 +        m.insert(k, v);
 +    }
 +
 +    // semicolon on insert, use or_insert_with(..)
 +    if !m.contains_key(&k) {
 +        if true {
 +            m.insert(k, v);
 +        } else {
 +            m.insert(k, v2);
 +        }
 +    }
 +
 +    // semicolon on if, use or_insert_with(..)
 +    if !m.contains_key(&k) {
 +        if true {
 +            m.insert(k, v)
 +        } else {
 +            m.insert(k, v2)
 +        };
 +    }
 +
 +    // early return, use if let
 +    if !m.contains_key(&k) {
 +        if true {
 +            m.insert(k, v);
 +        } else {
 +            m.insert(k, v2);
 +            return;
 +        }
 +    }
 +
 +    // use or_insert_with(..)
 +    if !m.contains_key(&k) {
 +        foo();
 +        m.insert(k, v);
 +    }
 +
 +    // semicolon on insert and match, use or_insert_with(..)
 +    if !m.contains_key(&k) {
 +        match 0 {
 +            1 if true => {
 +                m.insert(k, v);
 +            },
 +            _ => {
 +                m.insert(k, v2);
 +            },
 +        };
 +    }
 +
 +    // one branch doesn't insert, use if let
 +    if !m.contains_key(&k) {
 +        match 0 {
 +            0 => foo(),
 +            _ => {
 +                m.insert(k, v2);
 +            },
 +        };
 +    }
 +
 +    // use or_insert_with
 +    if !m.contains_key(&k) {
 +        foo();
 +        match 0 {
 +            0 if false => {
 +                m.insert(k, v);
 +            },
 +            1 => {
 +                foo();
 +                m.insert(k, v);
 +            },
 +            2 | 3 => {
 +                for _ in 0..2 {
 +                    foo();
 +                }
 +                if true {
 +                    m.insert(k, v);
 +                } else {
 +                    m.insert(k, v2);
 +                };
 +            },
 +            _ => {
 +                m.insert(k, v2);
 +            },
 +        }
 +    }
 +
 +    // ok, insert in loop
 +    if !m.contains_key(&k) {
 +        for _ in 0..2 {
 +            m.insert(k, v);
 +        }
 +    }
 +
 +    // macro_expansion test, use or_insert(..)
 +    if !m.contains_key(&m!(k)) {
 +        m.insert(m!(k), m!(v));
 +    }
 +
 +    // ok, map used before insertion
 +    if !m.contains_key(&k) {
 +        let _ = m.len();
 +        m.insert(k, v);
 +    }
 +
 +    // ok, inline asm
 +    if !m.contains_key(&k) {
 +        unsafe { asm!("nop") }
 +        m.insert(k, v);
 +    }
 +
 +    // ok, different keys.
 +    if !m.contains_key(&k) {
 +        m.insert(k2, v);
 +    }
 +
 +    // ok, different maps
 +    if !m.contains_key(&k) {
 +        m2.insert(k, v);
 +    }
 +
 +    // ok, insert in macro
 +    if !m.contains_key(&k) {
 +        insert!(m, k, v);
 +    }
- fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V, v2: V) {
-     // insert then do something, use if let
 +
-         foo();
++    // or_insert_with. Partial move of a local declared in the closure is ok.
 +    if !m.contains_key(&k) {
++        let x = (String::new(), String::new());
++        let _ = x.0;
 +        m.insert(k, v);
 +    }
 +}
 +
 +fn main() {}
index 8f2e383d675da8fdd43c49910f2529093eeeed09,0000000000000000000000000000000000000000..1076500498d32fe321a2de82c68e91514cae9ac8
mode 100644,000000..100644
--- /dev/null
@@@ -1,186 -1,0 +1,188 @@@
- error: usage of `contains_key` followed by `insert` on a `BTreeMap`
-   --> $DIR/entry.rs:153:5
 +error: usage of `contains_key` followed by `insert` on a `HashMap`
 +  --> $DIR/entry.rs:24:5
 +   |
 +LL | /     if !m.contains_key(&k) {
 +LL | |         m.insert(k, v);
 +LL | |     }
 +   | |_____^ help: try this: `m.entry(k).or_insert(v);`
 +   |
 +   = note: `-D clippy::map-entry` implied by `-D warnings`
 +
 +error: usage of `contains_key` followed by `insert` on a `HashMap`
 +  --> $DIR/entry.rs:29:5
 +   |
 +LL | /     if !m.contains_key(&k) {
 +LL | |         if true {
 +LL | |             m.insert(k, v);
 +LL | |         } else {
 +LL | |             m.insert(k, v2);
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +help: try this
 +   |
 +LL ~     m.entry(k).or_insert_with(|| {
 +LL +         if true {
 +LL +             v
 +LL +         } else {
 +LL +             v2
 +LL +         }
 + ...
 +
 +error: usage of `contains_key` followed by `insert` on a `HashMap`
 +  --> $DIR/entry.rs:38:5
 +   |
 +LL | /     if !m.contains_key(&k) {
 +LL | |         if true {
 +LL | |             m.insert(k, v)
 +LL | |         } else {
 +LL | |             m.insert(k, v2)
 +LL | |         };
 +LL | |     }
 +   | |_____^
 +   |
 +help: try this
 +   |
 +LL ~     m.entry(k).or_insert_with(|| {
 +LL +         if true {
 +LL +             v
 +LL +         } else {
 +LL +             v2
 +LL +         }
 + ...
 +
 +error: usage of `contains_key` followed by `insert` on a `HashMap`
 +  --> $DIR/entry.rs:47:5
 +   |
 +LL | /     if !m.contains_key(&k) {
 +LL | |         if true {
 +LL | |             m.insert(k, v);
 +LL | |         } else {
 +...  |
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +help: try this
 +   |
 +LL ~     if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
 +LL +         if true {
 +LL +             e.insert(v);
 +LL +         } else {
 +LL +             e.insert(v2);
 +LL +             return;
 + ...
 +
 +error: usage of `contains_key` followed by `insert` on a `HashMap`
 +  --> $DIR/entry.rs:57:5
 +   |
 +LL | /     if !m.contains_key(&k) {
 +LL | |         foo();
 +LL | |         m.insert(k, v);
 +LL | |     }
 +   | |_____^
 +   |
 +help: try this
 +   |
 +LL ~     m.entry(k).or_insert_with(|| {
 +LL +         foo();
 +LL +         v
 +LL +     });
 +   |
 +
 +error: usage of `contains_key` followed by `insert` on a `HashMap`
 +  --> $DIR/entry.rs:63:5
 +   |
 +LL | /     if !m.contains_key(&k) {
 +LL | |         match 0 {
 +LL | |             1 if true => {
 +LL | |                 m.insert(k, v);
 +...  |
 +LL | |         };
 +LL | |     }
 +   | |_____^
 +   |
 +help: try this
 +   |
 +LL ~     m.entry(k).or_insert_with(|| {
 +LL +         match 0 {
 +LL +             1 if true => {
 +LL +                 v
 +LL +             },
 +LL +             _ => {
 + ...
 +
 +error: usage of `contains_key` followed by `insert` on a `HashMap`
 +  --> $DIR/entry.rs:75:5
 +   |
 +LL | /     if !m.contains_key(&k) {
 +LL | |         match 0 {
 +LL | |             0 => foo(),
 +LL | |             _ => {
 +...  |
 +LL | |         };
 +LL | |     }
 +   | |_____^
 +   |
 +help: try this
 +   |
 +LL ~     if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
 +LL +         match 0 {
 +LL +             0 => foo(),
 +LL +             _ => {
 +LL +                 e.insert(v2);
 +LL +             },
 + ...
 +
 +error: usage of `contains_key` followed by `insert` on a `HashMap`
 +  --> $DIR/entry.rs:85:5
 +   |
 +LL | /     if !m.contains_key(&k) {
 +LL | |         foo();
 +LL | |         match 0 {
 +LL | |             0 if false => {
 +...  |
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +help: try this
 +   |
 +LL ~     m.entry(k).or_insert_with(|| {
 +LL +         foo();
 +LL +         match 0 {
 +LL +             0 if false => {
 +LL +                 v
 +LL +             },
 + ...
 +
 +error: usage of `contains_key` followed by `insert` on a `HashMap`
 +  --> $DIR/entry.rs:119:5
 +   |
 +LL | /     if !m.contains_key(&m!(k)) {
 +LL | |         m.insert(m!(k), m!(v));
 +LL | |     }
 +   | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));`
 +
- LL | |         foo();
++error: usage of `contains_key` followed by `insert` on a `HashMap`
++  --> $DIR/entry.rs:151:5
 +   |
 +LL | /     if !m.contains_key(&k) {
++LL | |         let x = (String::new(), String::new());
++LL | |         let _ = x.0;
 +LL | |         m.insert(k, v);
- LL ~     if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
- LL +         e.insert(v);
- LL +         foo();
- LL +     }
 +LL | |     }
 +   | |_____^
 +   |
 +help: try this
 +   |
++LL ~     m.entry(k).or_insert_with(|| {
++LL +         let x = (String::new(), String::new());
++LL +         let _ = x.0;
++LL +         v
++LL +     });
 +   |
 +
 +error: aborting due to 10 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..94979104556bc882267d230b41c535319d712bd8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++// run-rustfix
++
++#![warn(clippy::map_entry)]
++#![allow(dead_code)]
++
++use std::collections::BTreeMap;
++
++fn foo() {}
++
++fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V) {
++    // insert then do something, use if let
++    if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
++        e.insert(v);
++        foo();
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..080c1d959e89425d326ae8c05e8053128a347425
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++// run-rustfix
++
++#![warn(clippy::map_entry)]
++#![allow(dead_code)]
++
++use std::collections::BTreeMap;
++
++fn foo() {}
++
++fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V) {
++    // insert then do something, use if let
++    if !m.contains_key(&k) {
++        m.insert(k, v);
++        foo();
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5c6fcdf1a28c01f1ba0cbdd646bfb48453cde0e3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++error: usage of `contains_key` followed by `insert` on a `BTreeMap`
++  --> $DIR/entry_btree.rs:12:5
++   |
++LL | /     if !m.contains_key(&k) {
++LL | |         m.insert(k, v);
++LL | |         foo();
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::map-entry` implied by `-D warnings`
++help: try this
++   |
++LL ~     if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
++LL +         e.insert(v);
++LL +         foo();
++LL +     }
++   |
++
++error: aborting due to previous error
++
index 2c3b25cd45e80552789e3f7c34de5cbf5fd7b43f,0000000000000000000000000000000000000000..690ea810a6214072f1a1614304624f22cb30befc
mode 100644,000000..100644
--- /dev/null
@@@ -1,43 -1,0 +1,48 @@@
- #![allow(dead_code, clippy::needless_pass_by_value)]
 +#![feature(associated_type_defaults)]
 +#![warn(clippy::linkedlist)]
- struct Bar;
++#![allow(unused, dead_code, clippy::needless_pass_by_value)]
 +
 +extern crate alloc;
 +use alloc::collections::linked_list::LinkedList;
 +
 +const C: LinkedList<i32> = LinkedList::new();
 +static S: LinkedList<i32> = LinkedList::new();
 +
 +trait Foo {
 +    type Baz = LinkedList<u8>;
 +    fn foo(_: LinkedList<u8>);
 +    const BAR: Option<LinkedList<u8>>;
 +}
 +
 +// Ok, we don’t want to warn for implementations; see issue #605.
 +impl Foo for LinkedList<u8> {
 +    fn foo(_: LinkedList<u8>) {}
 +    const BAR: Option<LinkedList<u8>> = None;
 +}
 +
- pub fn test(my_favourite_linked_list: LinkedList<u8>) {
-     println!("{:?}", my_favourite_linked_list)
- }
- pub fn test_ret() -> Option<LinkedList<u8>> {
-     unimplemented!();
++pub struct Bar {
++    priv_linked_list_field: LinkedList<u8>,
++    pub pub_linked_list_field: LinkedList<u8>,
++}
 +impl Bar {
 +    fn foo(_: LinkedList<u8>) {}
 +}
 +
- pub fn test_local_not_linted() {
++// All of these test should be trigger the lint because they are not
++// part of the public api
++fn test(my_favorite_linked_list: LinkedList<u8>) {}
++fn test_ret() -> Option<LinkedList<u8>> {
++    None
 +}
- fn main() {
-     test(LinkedList::new());
-     test_local_not_linted();
++fn test_local_not_linted() {
 +    let _: LinkedList<u8>;
 +}
 +
++// 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(the_most_awesome_linked_list: LinkedList<u8>) {}
++pub fn pub_test_ret() -> Option<LinkedList<u8>> {
++    None
 +}
++
++fn main() {}
index 38ae71714d6624a638cdfc497e78c20667830e91,0000000000000000000000000000000000000000..51327df13211879da10fb77801a1c6a2e44be1c2
mode 100644,000000..100644
--- /dev/null
@@@ -1,67 -1,0 +1,75 @@@
-   --> $DIR/linkedlist.rs:25:15
 +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
 +  --> $DIR/linkedlist.rs:8:10
 +   |
 +LL | const C: LinkedList<i32> = LinkedList::new();
 +   |          ^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::linkedlist` implied by `-D warnings`
 +   = help: a `VecDeque` might work
 +
 +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
 +  --> $DIR/linkedlist.rs:9:11
 +   |
 +LL | static S: LinkedList<i32> = LinkedList::new();
 +   |           ^^^^^^^^^^^^^^^
 +   |
 +   = help: a `VecDeque` might work
 +
 +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
 +  --> $DIR/linkedlist.rs:12:16
 +   |
 +LL |     type Baz = LinkedList<u8>;
 +   |                ^^^^^^^^^^^^^^
 +   |
 +   = help: a `VecDeque` might work
 +
 +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
 +  --> $DIR/linkedlist.rs:13:15
 +   |
 +LL |     fn foo(_: LinkedList<u8>);
 +   |               ^^^^^^^^^^^^^^
 +   |
 +   = help: a `VecDeque` might work
 +
 +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
 +  --> $DIR/linkedlist.rs:14:23
 +   |
 +LL |     const BAR: Option<LinkedList<u8>>;
 +   |                       ^^^^^^^^^^^^^^
 +   |
 +   = help: a `VecDeque` might work
 +
 +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
-   --> $DIR/linkedlist.rs:28:39
++  --> $DIR/linkedlist.rs:24:29
++   |
++LL |     priv_linked_list_field: LinkedList<u8>,
++   |                             ^^^^^^^^^^^^^^
++   |
++   = help: a `VecDeque` might work
++
++error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
++  --> $DIR/linkedlist.rs:28:15
 +   |
 +LL |     fn foo(_: LinkedList<u8>) {}
 +   |               ^^^^^^^^^^^^^^
 +   |
 +   = help: a `VecDeque` might work
 +
 +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
- LL | pub fn test(my_favourite_linked_list: LinkedList<u8>) {
-    |                                       ^^^^^^^^^^^^^^
++  --> $DIR/linkedlist.rs:33:34
 +   |
-   --> $DIR/linkedlist.rs:32:29
++LL | fn test(my_favorite_linked_list: LinkedList<u8>) {}
++   |                                  ^^^^^^^^^^^^^^
 +   |
 +   = help: a `VecDeque` might work
 +
 +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
- LL | pub fn test_ret() -> Option<LinkedList<u8>> {
-    |                             ^^^^^^^^^^^^^^
++  --> $DIR/linkedlist.rs:34:25
 +   |
- error: aborting due to 8 previous errors
++LL | fn test_ret() -> Option<LinkedList<u8>> {
++   |                         ^^^^^^^^^^^^^^
 +   |
 +   = help: a `VecDeque` might work
 +
++error: aborting due to 9 previous errors
 +
index b5bd35a68785a9242e67f9b964b4822ef0d9eaae,0000000000000000000000000000000000000000..7db6b730963c94f6399e4ff2a69921b2a0d3ff61
mode 100644,000000..100644
--- /dev/null
@@@ -1,98 -1,0 +1,111 @@@
 +#![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
 +    // Note: if `clippy::manual_flatten` is made autofixable, this case will
 +    // lead to a follow-up lint `clippy::into_iter_on_ref`
 +    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);
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8cc12149403d32aa22aaba2618c187eb65ea5c20
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++// run-rustfix
++
++#![warn(clippy::manual_map)]
++#![allow(clippy::toplevel_ref_arg)]
++
++fn main() {
++    // Lint. `y` is declared within the arm, so it isn't captured by the map closure
++    let _ = Some(0).map(|x| {
++            let y = (String::new(), String::new());
++            (x, y.0)
++        });
++
++    // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map
++    // closure
++    let s = Some(String::new());
++    let _ = match &s {
++        Some(x) => Some((x.clone(), s)),
++        None => None,
++    };
++
++    // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map
++    // closure
++    let s = Some(String::new());
++    let _ = match &s {
++        Some(x) => Some({
++            let clone = x.clone();
++            let s = || s;
++            (clone, s())
++        }),
++        None => None,
++    };
++
++    // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured as a mutable
++    // reference by the map closure
++    let mut s = Some(String::new());
++    let _ = match &s {
++        Some(x) => Some({
++            let clone = x.clone();
++            let ref mut s = s;
++            (clone, s)
++        }),
++        None => None,
++    };
++
++    // Lint. `s` is captured by reference, so no lifetime issues.
++    let s = Some(String::new());
++    let _ = s.as_ref().map(|x| {
++            if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
++        });
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0862b201ead4b7290f600e6b27454a8a1b72dc4a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++// run-rustfix
++
++#![warn(clippy::manual_map)]
++#![allow(clippy::toplevel_ref_arg)]
++
++fn main() {
++    // Lint. `y` is declared within the arm, so it isn't captured by the map closure
++    let _ = match Some(0) {
++        Some(x) => Some({
++            let y = (String::new(), String::new());
++            (x, y.0)
++        }),
++        None => None,
++    };
++
++    // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map
++    // closure
++    let s = Some(String::new());
++    let _ = match &s {
++        Some(x) => Some((x.clone(), s)),
++        None => None,
++    };
++
++    // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map
++    // closure
++    let s = Some(String::new());
++    let _ = match &s {
++        Some(x) => Some({
++            let clone = x.clone();
++            let s = || s;
++            (clone, s())
++        }),
++        None => None,
++    };
++
++    // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured as a mutable
++    // reference by the map closure
++    let mut s = Some(String::new());
++    let _ = match &s {
++        Some(x) => Some({
++            let clone = x.clone();
++            let ref mut s = s;
++            (clone, s)
++        }),
++        None => None,
++    };
++
++    // Lint. `s` is captured by reference, so no lifetime issues.
++    let s = Some(String::new());
++    let _ = match &s {
++        Some(x) => Some({
++            if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
++        }),
++        None => None,
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..711ff6c4a4b095dfc5da5efd75116e4b2fa5445d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++error: manual implementation of `Option::map`
++  --> $DIR/manual_map_option_2.rs:8:13
++   |
++LL |       let _ = match Some(0) {
++   |  _____________^
++LL | |         Some(x) => Some({
++LL | |             let y = (String::new(), String::new());
++LL | |             (x, y.0)
++LL | |         }),
++LL | |         None => None,
++LL | |     };
++   | |_____^
++   |
++   = note: `-D clippy::manual-map` implied by `-D warnings`
++help: try this
++   |
++LL ~     let _ = Some(0).map(|x| {
++LL +             let y = (String::new(), String::new());
++LL +             (x, y.0)
++LL ~         });
++   |
++
++error: manual implementation of `Option::map`
++  --> $DIR/manual_map_option_2.rs:50:13
++   |
++LL |       let _ = match &s {
++   |  _____________^
++LL | |         Some(x) => Some({
++LL | |             if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
++LL | |         }),
++LL | |         None => None,
++LL | |     };
++   | |_____^
++   |
++help: try this
++   |
++LL ~     let _ = s.as_ref().map(|x| {
++LL +             if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
++LL ~         });
++   |
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3a0332939d409a8237572e97d489bce948b44817
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++// run-rustfix
++
++#![feature(custom_inner_attributes)]
++#![warn(clippy::manual_split_once)]
++#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)]
++
++extern crate itertools;
++
++#[allow(unused_imports)]
++use itertools::Itertools;
++
++fn main() {
++    let _ = Some("key=value".split_once('=').map_or("key=value", |x| x.0));
++    let _ = "key=value".splitn(2, '=').nth(2);
++    let _ = "key=value".split_once('=').map_or("key=value", |x| x.0);
++    let _ = "key=value".split_once('=').map_or("key=value", |x| x.0);
++    let _ = "key=value".split_once('=').unwrap().1;
++    let _ = "key=value".split_once('=').unwrap().1;
++    let (_, _) = "key=value".split_once('=').unwrap();
++
++    let s = String::from("key=value");
++    let _ = s.split_once('=').map_or(&*s, |x| x.0);
++
++    let s = Box::<str>::from("key=value");
++    let _ = s.split_once('=').map_or(&*s, |x| x.0);
++
++    let s = &"key=value";
++    let _ = s.split_once('=').map_or(*s, |x| x.0);
++
++    fn _f(s: &str) -> Option<&str> {
++        let _ = s.split_once("key=value").map_or(s, |x| x.0);
++        let _ = s.split_once("key=value")?.1;
++        let _ = s.split_once("key=value")?.1;
++        None
++    }
++
++    // Don't lint, slices don't have `split_once`
++    let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
++}
++
++fn _msrv_1_51() {
++    #![clippy::msrv = "1.51"]
++    // `str::split_once` was stabilized in 1.16. Do not lint this
++    let _ = "key=value".splitn(2, '=').nth(1).unwrap();
++}
++
++fn _msrv_1_52() {
++    #![clippy::msrv = "1.52"]
++    let _ = "key=value".split_once('=').unwrap().1;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e6093b63fe8d428e348760d6a496a3ae4d433090
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++// run-rustfix
++
++#![feature(custom_inner_attributes)]
++#![warn(clippy::manual_split_once)]
++#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)]
++
++extern crate itertools;
++
++#[allow(unused_imports)]
++use itertools::Itertools;
++
++fn main() {
++    let _ = "key=value".splitn(2, '=').next();
++    let _ = "key=value".splitn(2, '=').nth(2);
++    let _ = "key=value".splitn(2, '=').next().unwrap();
++    let _ = "key=value".splitn(2, '=').nth(0).unwrap();
++    let _ = "key=value".splitn(2, '=').nth(1).unwrap();
++    let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
++    let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
++
++    let s = String::from("key=value");
++    let _ = s.splitn(2, '=').next().unwrap();
++
++    let s = Box::<str>::from("key=value");
++    let _ = s.splitn(2, '=').nth(0).unwrap();
++
++    let s = &"key=value";
++    let _ = s.splitn(2, '=').skip(0).next().unwrap();
++
++    fn _f(s: &str) -> Option<&str> {
++        let _ = s.splitn(2, "key=value").next()?;
++        let _ = s.splitn(2, "key=value").nth(1)?;
++        let _ = s.splitn(2, "key=value").skip(1).next()?;
++        None
++    }
++
++    // Don't lint, slices don't have `split_once`
++    let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
++}
++
++fn _msrv_1_51() {
++    #![clippy::msrv = "1.51"]
++    // `str::split_once` was stabilized in 1.16. Do not lint this
++    let _ = "key=value".splitn(2, '=').nth(1).unwrap();
++}
++
++fn _msrv_1_52() {
++    #![clippy::msrv = "1.52"]
++    let _ = "key=value".splitn(2, '=').nth(1).unwrap();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4f15196b469e1f73d713f527edca2c57b22af889
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,82 @@@
++error: manual implementation of `split_once`
++  --> $DIR/manual_split_once.rs:13:13
++   |
++LL |     let _ = "key=value".splitn(2, '=').next();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some("key=value".split_once('=').map_or("key=value", |x| x.0))`
++   |
++   = note: `-D clippy::manual-split-once` implied by `-D warnings`
++
++error: manual implementation of `split_once`
++  --> $DIR/manual_split_once.rs:15:13
++   |
++LL |     let _ = "key=value".splitn(2, '=').next().unwrap();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)`
++
++error: manual implementation of `split_once`
++  --> $DIR/manual_split_once.rs:16:13
++   |
++LL |     let _ = "key=value".splitn(2, '=').nth(0).unwrap();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)`
++
++error: manual implementation of `split_once`
++  --> $DIR/manual_split_once.rs:17:13
++   |
++LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
++
++error: manual implementation of `split_once`
++  --> $DIR/manual_split_once.rs:18:13
++   |
++LL |     let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
++
++error: manual implementation of `split_once`
++  --> $DIR/manual_split_once.rs:19:18
++   |
++LL |     let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
++   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')`
++
++error: manual implementation of `split_once`
++  --> $DIR/manual_split_once.rs:22:13
++   |
++LL |     let _ = s.splitn(2, '=').next().unwrap();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)`
++
++error: manual implementation of `split_once`
++  --> $DIR/manual_split_once.rs:25:13
++   |
++LL |     let _ = s.splitn(2, '=').nth(0).unwrap();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)`
++
++error: manual implementation of `split_once`
++  --> $DIR/manual_split_once.rs:28:13
++   |
++LL |     let _ = s.splitn(2, '=').skip(0).next().unwrap();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(*s, |x| x.0)`
++
++error: manual implementation of `split_once`
++  --> $DIR/manual_split_once.rs:31:17
++   |
++LL |         let _ = s.splitn(2, "key=value").next()?;
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value").map_or(s, |x| x.0)`
++
++error: manual implementation of `split_once`
++  --> $DIR/manual_split_once.rs:32:17
++   |
++LL |         let _ = s.splitn(2, "key=value").nth(1)?;
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
++
++error: manual implementation of `split_once`
++  --> $DIR/manual_split_once.rs:33:17
++   |
++LL |         let _ = s.splitn(2, "key=value").skip(1).next()?;
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
++
++error: manual implementation of `split_once`
++  --> $DIR/manual_split_once.rs:49:13
++   |
++LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
++
++error: aborting due to 13 previous errors
++
index 3b6224254a0a72d6a1b868acbd695bf7bdd79075,0000000000000000000000000000000000000000..b609ba659467f527f4bcc0b54120c75d5bf66791
mode 100644,000000..100644
--- /dev/null
@@@ -1,59 -1,0 +1,79 @@@
 +// run-rustfix
 +#![allow(unused_imports)]
 +#![warn(
 +    clippy::all,
 +    clippy::style,
 +    clippy::mem_replace_option_with_none,
 +    clippy::mem_replace_with_default
 +)]
 +
 +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
 +use std::mem;
 +
 +fn replace_option_with_none() {
 +    let mut an_option = Some(1);
 +    let _ = an_option.take();
 +    let an_option = &mut Some(1);
 +    let _ = an_option.take();
 +}
 +
 +fn replace_with_default() {
 +    let mut s = String::from("foo");
 +    let _ = std::mem::take(&mut s);
 +
 +    let s = &mut String::from("foo");
 +    let _ = std::mem::take(s);
 +    let _ = std::mem::take(s);
 +
 +    let mut v = vec![123];
 +    let _ = std::mem::take(&mut v);
 +    let _ = std::mem::take(&mut v);
 +    let _ = std::mem::take(&mut v);
 +    let _ = std::mem::take(&mut v);
 +
 +    let mut hash_map: HashMap<i32, i32> = HashMap::new();
 +    let _ = std::mem::take(&mut hash_map);
 +
 +    let mut btree_map: BTreeMap<i32, i32> = BTreeMap::new();
 +    let _ = std::mem::take(&mut btree_map);
 +
 +    let mut vd: VecDeque<i32> = VecDeque::new();
 +    let _ = std::mem::take(&mut vd);
 +
 +    let mut hash_set: HashSet<&str> = HashSet::new();
 +    let _ = std::mem::take(&mut hash_set);
 +
 +    let mut btree_set: BTreeSet<&str> = BTreeSet::new();
 +    let _ = std::mem::take(&mut btree_set);
 +
 +    let mut list: LinkedList<i32> = LinkedList::new();
 +    let _ = std::mem::take(&mut list);
 +
 +    let mut binary_heap: BinaryHeap<i32> = BinaryHeap::new();
 +    let _ = std::mem::take(&mut binary_heap);
++
++    let mut tuple = (vec![1, 2], BinaryHeap::<i32>::new());
++    let _ = std::mem::take(&mut tuple);
++
++    let mut refstr = "hello";
++    let _ = std::mem::take(&mut refstr);
++
++    let mut slice: &[i32] = &[1, 2, 3];
++    let _ = std::mem::take(&mut slice);
++}
++
++// lint is disabled for primitives because in this case `take`
++// has no clear benefit over `replace` and sometimes is harder to read
++fn dont_lint_primitive() {
++    let mut pbool = true;
++    let _ = std::mem::replace(&mut pbool, false);
++
++    let mut pint = 5;
++    let _ = std::mem::replace(&mut pint, 0);
 +}
 +
 +fn main() {
 +    replace_option_with_none();
 +    replace_with_default();
++    dont_lint_primitive();
 +}
index 0a36db9e92159ad6f0dadb1fa7fd57b92b47ae20,0000000000000000000000000000000000000000..93f6dcdec83b96b3a8ad49ce9d8c043e24bb71e6
mode 100644,000000..100644
--- /dev/null
@@@ -1,59 -1,0 +1,79 @@@
 +// run-rustfix
 +#![allow(unused_imports)]
 +#![warn(
 +    clippy::all,
 +    clippy::style,
 +    clippy::mem_replace_option_with_none,
 +    clippy::mem_replace_with_default
 +)]
 +
 +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
 +use std::mem;
 +
 +fn replace_option_with_none() {
 +    let mut an_option = Some(1);
 +    let _ = mem::replace(&mut an_option, None);
 +    let an_option = &mut Some(1);
 +    let _ = mem::replace(an_option, None);
 +}
 +
 +fn replace_with_default() {
 +    let mut s = String::from("foo");
 +    let _ = std::mem::replace(&mut s, String::default());
 +
 +    let s = &mut String::from("foo");
 +    let _ = std::mem::replace(s, String::default());
 +    let _ = std::mem::replace(s, Default::default());
 +
 +    let mut v = vec![123];
 +    let _ = std::mem::replace(&mut v, Vec::default());
 +    let _ = std::mem::replace(&mut v, Default::default());
 +    let _ = std::mem::replace(&mut v, Vec::new());
 +    let _ = std::mem::replace(&mut v, vec![]);
 +
 +    let mut hash_map: HashMap<i32, i32> = HashMap::new();
 +    let _ = std::mem::replace(&mut hash_map, HashMap::new());
 +
 +    let mut btree_map: BTreeMap<i32, i32> = BTreeMap::new();
 +    let _ = std::mem::replace(&mut btree_map, BTreeMap::new());
 +
 +    let mut vd: VecDeque<i32> = VecDeque::new();
 +    let _ = std::mem::replace(&mut vd, VecDeque::new());
 +
 +    let mut hash_set: HashSet<&str> = HashSet::new();
 +    let _ = std::mem::replace(&mut hash_set, HashSet::new());
 +
 +    let mut btree_set: BTreeSet<&str> = BTreeSet::new();
 +    let _ = std::mem::replace(&mut btree_set, BTreeSet::new());
 +
 +    let mut list: LinkedList<i32> = LinkedList::new();
 +    let _ = std::mem::replace(&mut list, LinkedList::new());
 +
 +    let mut binary_heap: BinaryHeap<i32> = BinaryHeap::new();
 +    let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new());
++
++    let mut tuple = (vec![1, 2], BinaryHeap::<i32>::new());
++    let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new()));
++
++    let mut refstr = "hello";
++    let _ = std::mem::replace(&mut refstr, "");
++
++    let mut slice: &[i32] = &[1, 2, 3];
++    let _ = std::mem::replace(&mut slice, &[]);
++}
++
++// lint is disabled for primitives because in this case `take`
++// has no clear benefit over `replace` and sometimes is harder to read
++fn dont_lint_primitive() {
++    let mut pbool = true;
++    let _ = std::mem::replace(&mut pbool, false);
++
++    let mut pint = 5;
++    let _ = std::mem::replace(&mut pint, 0);
 +}
 +
 +fn main() {
 +    replace_option_with_none();
 +    replace_with_default();
++    dont_lint_primitive();
 +}
index f8aa1538bffa87769567e4790ea5d147caaf20e4,0000000000000000000000000000000000000000..90dc6c95f858295cef40d0fc6f27fa6b60fa7e85
mode 100644,000000..100644
--- /dev/null
@@@ -1,102 -1,0 +1,120 @@@
- error: aborting due to 16 previous errors
 +error: replacing an `Option` with `None`
 +  --> $DIR/mem_replace.rs:15:13
 +   |
 +LL |     let _ = mem::replace(&mut an_option, None);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
 +   |
 +   = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings`
 +
 +error: replacing an `Option` with `None`
 +  --> $DIR/mem_replace.rs:17:13
 +   |
 +LL |     let _ = mem::replace(an_option, None);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
 +  --> $DIR/mem_replace.rs:22:13
 +   |
 +LL |     let _ = std::mem::replace(&mut s, String::default());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
 +   |
 +   = note: `-D clippy::mem-replace-with-default` implied by `-D warnings`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
 +  --> $DIR/mem_replace.rs:25:13
 +   |
 +LL |     let _ = std::mem::replace(s, String::default());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
 +  --> $DIR/mem_replace.rs:26:13
 +   |
 +LL |     let _ = std::mem::replace(s, Default::default());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
 +  --> $DIR/mem_replace.rs:29:13
 +   |
 +LL |     let _ = std::mem::replace(&mut v, Vec::default());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
 +  --> $DIR/mem_replace.rs:30:13
 +   |
 +LL |     let _ = std::mem::replace(&mut v, Default::default());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
 +  --> $DIR/mem_replace.rs:31:13
 +   |
 +LL |     let _ = std::mem::replace(&mut v, Vec::new());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
 +  --> $DIR/mem_replace.rs:32:13
 +   |
 +LL |     let _ = std::mem::replace(&mut v, vec![]);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
 +  --> $DIR/mem_replace.rs:35:13
 +   |
 +LL |     let _ = std::mem::replace(&mut hash_map, HashMap::new());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
 +  --> $DIR/mem_replace.rs:38:13
 +   |
 +LL |     let _ = std::mem::replace(&mut btree_map, BTreeMap::new());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
 +  --> $DIR/mem_replace.rs:41:13
 +   |
 +LL |     let _ = std::mem::replace(&mut vd, VecDeque::new());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
 +  --> $DIR/mem_replace.rs:44:13
 +   |
 +LL |     let _ = std::mem::replace(&mut hash_set, HashSet::new());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
 +  --> $DIR/mem_replace.rs:47:13
 +   |
 +LL |     let _ = std::mem::replace(&mut btree_set, BTreeSet::new());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
 +  --> $DIR/mem_replace.rs:50:13
 +   |
 +LL |     let _ = std::mem::replace(&mut list, LinkedList::new());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)`
 +
 +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
 +  --> $DIR/mem_replace.rs:53:13
 +   |
 +LL |     let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)`
 +
++error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
++  --> $DIR/mem_replace.rs:56:13
++   |
++LL |     let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new()));
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)`
++
++error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
++  --> $DIR/mem_replace.rs:59:13
++   |
++LL |     let _ = std::mem::replace(&mut refstr, "");
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)`
++
++error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
++  --> $DIR/mem_replace.rs:62:13
++   |
++LL |     let _ = std::mem::replace(&mut slice, &[]);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)`
++
++error: aborting due to 19 previous errors
 +
index 513d930e0568758783426279201dfa1bbfe9d8b7,0000000000000000000000000000000000000000..c441b35b9920390c89edfc6ac504716cf25eae2f
mode 100644,000000..100644
--- /dev/null
@@@ -1,138 -1,0 +1,141 @@@
- use option_helpers::IteratorFalsePositives;
 +// aux-build:option_helpers.rs
 +// edition:2018
 +
 +#![warn(clippy::all, clippy::pedantic)]
 +#![allow(
 +    clippy::blacklisted_name,
 +    clippy::default_trait_access,
 +    clippy::missing_docs_in_private_items,
 +    clippy::missing_safety_doc,
 +    clippy::non_ascii_literal,
 +    clippy::new_without_default,
 +    clippy::needless_pass_by_value,
 +    clippy::needless_lifetimes,
 +    clippy::print_stdout,
 +    clippy::must_use_candidate,
 +    clippy::use_self,
 +    clippy::useless_format,
 +    clippy::wrong_self_convention,
 +    clippy::unused_self,
 +    unused
 +)]
 +
 +#[macro_use]
 +extern crate option_helpers;
 +
 +use std::collections::BTreeMap;
 +use std::collections::HashMap;
 +use std::collections::HashSet;
 +use std::collections::VecDeque;
 +use std::iter::FromIterator;
 +use std::ops::Mul;
 +use std::rc::{self, Rc};
 +use std::sync::{self, Arc};
 +
++use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives};
 +
 +struct Lt<'a> {
 +    foo: &'a u32,
 +}
 +
 +impl<'a> Lt<'a> {
 +    // The lifetime is different, but that’s irrelevant; see issue #734.
 +    #[allow(clippy::needless_lifetimes)]
 +    pub fn new<'b>(s: &'b str) -> Lt<'b> {
 +        unimplemented!()
 +    }
 +}
 +
 +struct Lt2<'a> {
 +    foo: &'a u32,
 +}
 +
 +impl<'a> Lt2<'a> {
 +    // The lifetime is different, but that’s irrelevant; see issue #734.
 +    pub fn new(s: &str) -> Lt2 {
 +        unimplemented!()
 +    }
 +}
 +
 +struct Lt3<'a> {
 +    foo: &'a u32,
 +}
 +
 +impl<'a> Lt3<'a> {
 +    // The lifetime is different, but that’s irrelevant; see issue #734.
 +    pub fn new() -> Lt3<'static> {
 +        unimplemented!()
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +struct U;
 +
 +impl U {
 +    fn new() -> Self {
 +        U
 +    }
 +    // Ok because `U` is `Copy`.
 +    fn to_something(self) -> u32 {
 +        0
 +    }
 +}
 +
 +struct V<T> {
 +    _dummy: T,
 +}
 +
 +impl<T> V<T> {
 +    fn new() -> Option<V<T>> {
 +        None
 +    }
 +}
 +
 +struct AsyncNew;
 +
 +impl AsyncNew {
 +    async fn new() -> Option<Self> {
 +        None
 +    }
 +}
 +
 +struct BadNew;
 +
 +impl BadNew {
 +    fn new() -> i32 {
 +        0
 +    }
 +}
 +
 +struct T;
 +
 +impl Mul<T> for T {
 +    type Output = T;
 +    // No error, obviously.
 +    fn mul(self, other: T) -> T {
 +        self
 +    }
 +}
 +
 +/// Checks implementation of `FILTER_NEXT` lint.
 +#[rustfmt::skip]
 +fn filter_next() {
 +    let v = vec![3, 2, 1, 0, -1, -2, -3];
 +
 +    // Multi-line case.
 +    let _ = v.iter().filter(|&x| {
 +                                *x < 0
 +                            }
 +                   ).next();
 +
 +    // Check that we don't lint if the caller is not an `Iterator`.
 +    let foo = IteratorFalsePositives { foo: 0 };
 +    let _ = foo.filter().next();
++
++    let foo = IteratorMethodFalsePositives {};
++    let _ = foo.filter(42).next();
 +}
 +
 +fn main() {
 +    filter_next();
 +}
index 7f9f7ddc53575ca57a21cc0840df551218571da1,0000000000000000000000000000000000000000..8d9fc5a864d751bf30b27da1990348ab0eb5c205
mode 100644,000000..100644
--- /dev/null
@@@ -1,187 -1,0 +1,192 @@@
 +#![allow(clippy::redundant_clone)]
 +#![feature(custom_inner_attributes)]
 +#![clippy::msrv = "1.0.0"]
 +
 +use std::ops::{Deref, RangeFrom};
 +
++fn approx_const() {
++    let log2_10 = 3.321928094887362;
++    let log10_2 = 0.301029995663981;
++}
++
 +fn cloned_instead_of_copied() {
 +    let _ = [1].iter().cloned();
 +}
 +
 +fn option_as_ref_deref() {
 +    let mut opt = Some(String::from("123"));
 +
 +    let _ = opt.as_ref().map(String::as_str);
 +    let _ = opt.as_ref().map(|x| x.as_str());
 +    let _ = opt.as_mut().map(String::as_mut_str);
 +    let _ = opt.as_mut().map(|x| x.as_mut_str());
 +}
 +
 +fn match_like_matches() {
 +    let _y = match Some(5) {
 +        Some(0) => true,
 +        _ => false,
 +    };
 +}
 +
 +fn match_same_arms() {
 +    match (1, 2, 3) {
 +        (1, .., 3) => 42,
 +        (.., 3) => 42, //~ ERROR match arms have same body
 +        _ => 0,
 +    };
 +}
 +
 +fn match_same_arms2() {
 +    let _ = match Some(42) {
 +        Some(_) => 24,
 +        None => 24, //~ ERROR match arms have same body
 +    };
 +}
 +
 +pub fn manual_strip_msrv() {
 +    let s = "hello, world!";
 +    if s.starts_with("hello, ") {
 +        assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
 +    }
 +}
 +
 +pub fn redundant_fieldnames() {
 +    let start = 0;
 +    let _ = RangeFrom { start: start };
 +}
 +
 +pub fn redundant_static_lifetime() {
 +    const VAR_ONE: &'static str = "Test constant #1";
 +}
 +
 +pub fn checked_conversion() {
 +    let value: i64 = 42;
 +    let _ = value <= (u32::max_value() as i64) && value >= 0;
 +    let _ = value <= (u32::MAX as i64) && value >= 0;
 +}
 +
 +pub struct FromOverInto(String);
 +
 +impl Into<FromOverInto> for String {
 +    fn into(self) -> FromOverInto {
 +        FromOverInto(self)
 +    }
 +}
 +
 +pub fn filter_map_next() {
 +    let a = ["1", "lol", "3", "NaN", "5"];
 +
 +    #[rustfmt::skip]
 +    let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
 +        .into_iter()
 +        .filter_map(|x| {
 +            if x == 2 {
 +                Some(x * 2)
 +            } else {
 +                None
 +            }
 +        })
 +        .next();
 +}
 +
 +#[allow(clippy::no_effect)]
 +#[allow(clippy::short_circuit_statement)]
 +#[allow(clippy::unnecessary_operation)]
 +pub fn manual_range_contains() {
 +    let x = 5;
 +    x >= 8 && x < 12;
 +}
 +
 +pub fn use_self() {
 +    struct Foo {}
 +
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +        fn test() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +fn replace_with_default() {
 +    let mut s = String::from("foo");
 +    let _ = std::mem::replace(&mut s, String::default());
 +}
 +
 +fn map_unwrap_or() {
 +    let opt = Some(1);
 +
 +    // Check for `option.map(_).unwrap_or(_)` use.
 +    // Single line case.
 +    let _ = opt
 +        .map(|x| x + 1)
 +        // Should lint even though this call is on a separate line.
 +        .unwrap_or(0);
 +}
 +
 +// Could be const
 +fn missing_const_for_fn() -> i32 {
 +    1
 +}
 +
 +fn unnest_or_patterns() {
 +    struct TS(u8, u8);
 +    if let TS(0, x) | TS(1, x) = TS(0, 0) {}
 +}
 +
 +fn main() {
 +    filter_map_next();
 +    checked_conversion();
 +    redundant_fieldnames();
 +    redundant_static_lifetime();
 +    option_as_ref_deref();
 +    match_like_matches();
 +    match_same_arms();
 +    match_same_arms2();
 +    manual_strip_msrv();
 +    manual_range_contains();
 +    use_self();
 +    replace_with_default();
 +    map_unwrap_or();
 +    missing_const_for_fn();
 +    unnest_or_patterns();
 +}
 +
 +mod meets_msrv {
 +    #![feature(custom_inner_attributes)]
 +    #![clippy::msrv = "1.45.0"]
 +
 +    fn main() {
 +        let s = "hello, world!";
 +        if s.starts_with("hello, ") {
 +            assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
 +        }
 +    }
 +}
 +
 +mod just_under_msrv {
 +    #![feature(custom_inner_attributes)]
 +    #![clippy::msrv = "1.46.0"]
 +
 +    fn main() {
 +        let s = "hello, world!";
 +        if s.starts_with("hello, ") {
 +            assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
 +        }
 +    }
 +}
 +
 +mod just_above_msrv {
 +    #![feature(custom_inner_attributes)]
 +    #![clippy::msrv = "1.44.0"]
 +
 +    fn main() {
 +        let s = "hello, world!";
 +        if s.starts_with("hello, ") {
 +            assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
 +        }
 +    }
 +}
index a2e4e86ed6b8ca853f2a29adc978f74d54183c34,0000000000000000000000000000000000000000..360dcfb230c6563a6c5de5d5b6e322f70473dac5
mode 100644,000000..100644
--- /dev/null
@@@ -1,37 -1,0 +1,37 @@@
-   --> $DIR/min_rust_version_attr.rs:160:24
 +error: stripping a prefix manually
-   --> $DIR/min_rust_version_attr.rs:159:9
++  --> $DIR/min_rust_version_attr.rs:165:24
 +   |
 +LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
 +   |                        ^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::manual-strip` implied by `-D warnings`
 +note: the prefix was tested here
-   --> $DIR/min_rust_version_attr.rs:172:24
++  --> $DIR/min_rust_version_attr.rs:164:9
 +   |
 +LL |         if s.starts_with("hello, ") {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +help: try using the `strip_prefix` method
 +   |
 +LL ~         if let Some(<stripped>) = s.strip_prefix("hello, ") {
 +LL ~             assert_eq!(<stripped>.to_uppercase(), "WORLD!");
 +   |
 +
 +error: stripping a prefix manually
-   --> $DIR/min_rust_version_attr.rs:171:9
++  --> $DIR/min_rust_version_attr.rs:177:24
 +   |
 +LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
 +   |                        ^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: the prefix was tested here
++  --> $DIR/min_rust_version_attr.rs:176:9
 +   |
 +LL |         if s.starts_with("hello, ") {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +help: try using the `strip_prefix` method
 +   |
 +LL ~         if let Some(<stripped>) = s.strip_prefix("hello, ") {
 +LL ~             assert_eq!(<stripped>.to_uppercase(), "WORLD!");
 +   |
 +
 +error: aborting due to 2 previous errors
 +
index 1348dd2a3d8bb87a12c6a3af0008c6ae517cb93c,0000000000000000000000000000000000000000..e1ae1ef928223bf748cd6c21c5bf2af27d962a43
mode 100644,000000..100644
--- /dev/null
@@@ -1,63 -1,0 +1,84 @@@
- fn main() {
-     mut_range_bound_upper();
-     mut_range_bound_lower();
-     mut_range_bound_both();
-     mut_range_bound_no_mutation();
-     immut_range_bound();
-     mut_borrow_range_bound();
-     immut_borrow_range_bound();
- }
 +#![allow(unused)]
 +
++fn main() {}
 +
 +fn mut_range_bound_upper() {
 +    let mut m = 4;
 +    for i in 0..m {
 +        m = 5;
 +    } // warning
 +}
 +
 +fn mut_range_bound_lower() {
 +    let mut m = 4;
 +    for i in m..10 {
 +        m *= 2;
 +    } // warning
 +}
 +
 +fn mut_range_bound_both() {
 +    let mut m = 4;
 +    let mut n = 6;
 +    for i in m..n {
 +        m = 5;
 +        n = 7;
 +    } // warning (1 for each mutated bound)
 +}
 +
 +fn mut_range_bound_no_mutation() {
 +    let mut m = 4;
 +    for i in 0..m {
 +        continue;
 +    } // no warning
 +}
 +
 +fn mut_borrow_range_bound() {
 +    let mut m = 4;
 +    for i in 0..m {
 +        let n = &mut m; // warning
 +        *n += 1;
 +    }
 +}
 +
 +fn immut_borrow_range_bound() {
 +    let mut m = 4;
 +    for i in 0..m {
 +        let n = &m; // should be no warning?
 +    }
 +}
 +
 +fn immut_range_bound() {
 +    let m = 4;
 +    for i in 0..m {
 +        continue;
 +    } // no warning
 +}
++
++fn mut_range_bound_break() {
++    let mut m = 4;
++    for i in 0..m {
++        if m == 4 {
++            m = 5; // no warning because of immediate break
++            break;
++        }
++    }
++}
++
++fn mut_range_bound_no_immediate_break() {
++    let mut m = 4;
++    for i in 0..m {
++        m = 2; // warning because it is not immediately followed by break
++        if m == 4 {
++            break;
++        }
++    }
++
++    let mut n = 3;
++    for i in n..10 {
++        if n == 4 {
++            n = 1; // FIXME: warning because is is not immediately followed by break
++            let _ = 2;
++            break;
++        }
++    }
++}
index 0eeb76e0ec5fd713656843aa48b6000fdd98803f,0000000000000000000000000000000000000000..4b5a3fc1e418cded8f6b05a5b5c80ffb162ca4e5
mode 100644,000000..100644
--- /dev/null
@@@ -1,34 -1,0 +1,59 @@@
- error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
-   --> $DIR/mut_range_bound.rs:16:9
++error: attempt to mutate range bound within loop
++  --> $DIR/mut_range_bound.rs:8:9
 +   |
 +LL |         m = 5;
 +   |         ^
 +   |
 +   = note: `-D clippy::mut-range-bound` implied by `-D warnings`
++   = note: the range of the loop is unchanged
 +
- error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
-   --> $DIR/mut_range_bound.rs:23:9
++error: attempt to mutate range bound within loop
++  --> $DIR/mut_range_bound.rs:15:9
 +   |
 +LL |         m *= 2;
 +   |         ^
++   |
++   = note: the range of the loop is unchanged
 +
- error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
-   --> $DIR/mut_range_bound.rs:31:9
++error: attempt to mutate range bound within loop
++  --> $DIR/mut_range_bound.rs:23:9
 +   |
 +LL |         m = 5;
 +   |         ^
++   |
++   = note: the range of the loop is unchanged
 +
- error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
-   --> $DIR/mut_range_bound.rs:32:9
++error: attempt to mutate range bound within loop
++  --> $DIR/mut_range_bound.rs:24:9
 +   |
 +LL |         n = 7;
 +   |         ^
++   |
++   = note: the range of the loop is unchanged
 +
- error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
-   --> $DIR/mut_range_bound.rs:46:22
++error: attempt to mutate range bound within loop
++  --> $DIR/mut_range_bound.rs:38:22
 +   |
 +LL |         let n = &mut m; // warning
 +   |                      ^
++   |
++   = note: the range of the loop is unchanged
++
++error: attempt to mutate range bound within loop
++  --> $DIR/mut_range_bound.rs:70:9
++   |
++LL |         m = 2; // warning because it is not immediately followed by break
++   |         ^
++   |
++   = note: the range of the loop is unchanged
++
++error: attempt to mutate range bound within loop
++  --> $DIR/mut_range_bound.rs:79:13
++   |
++LL |             n = 1; // FIXME: warning because is is not immediately followed by break
++   |             ^
++   |
++   = note: the range of the loop is unchanged
 +
- error: aborting due to 5 previous errors
++error: aborting due to 7 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d721452ae88803431fd289feb9e223100b16f3ea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++// run-rustfix
++
++#[warn(clippy::needless_option_as_deref)]
++
++fn main() {
++    // should lint
++    let _: Option<&usize> = Some(&1);
++    let _: Option<&mut usize> = Some(&mut 1);
++
++    // should not lint
++    let _ = Some(Box::new(1)).as_deref();
++    let _ = Some(Box::new(1)).as_deref_mut();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bb15512adf6aa6c656bba664d822be00aa44364b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++// run-rustfix
++
++#[warn(clippy::needless_option_as_deref)]
++
++fn main() {
++    // should lint
++    let _: Option<&usize> = Some(&1).as_deref();
++    let _: Option<&mut usize> = Some(&mut 1).as_deref_mut();
++
++    // should not lint
++    let _ = Some(Box::new(1)).as_deref();
++    let _ = Some(Box::new(1)).as_deref_mut();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5dd507b4a71e8fef8207e5ac6b1b579cac4f9b28
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: derefed type is same as origin
++  --> $DIR/needless_option_as_deref.rs:7:29
++   |
++LL |     let _: Option<&usize> = Some(&1).as_deref();
++   |                             ^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&1)`
++   |
++   = note: `-D clippy::needless-option-as-deref` implied by `-D warnings`
++
++error: derefed type is same as origin
++  --> $DIR/needless_option_as_deref.rs:8:33
++   |
++LL |     let _: Option<&mut usize> = Some(&mut 1).as_deref_mut();
++   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&mut 1)`
++
++error: aborting due to 2 previous errors
++
index 769ccc14bc1e4c2ba27f1e0a21c46799f052a3cc,0000000000000000000000000000000000000000..d1815d0aec331692f66945f584d298fdb9e8d839
mode 100644,000000..100644
--- /dev/null
@@@ -1,89 -1,0 +1,151 @@@
++// edition:2018
 +// run-rustfix
 +#![warn(clippy::option_if_let_else)]
 +#![allow(clippy::redundant_closure)]
 +#![allow(clippy::ref_option_ref)]
 +
 +fn bad1(string: Option<&str>) -> (bool, &str) {
 +    string.map_or((false, "hello"), |x| (true, x))
 +}
 +
 +fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
 +    if string.is_none() {
 +        None
 +    } else if let Some(x) = string {
 +        Some((true, x))
 +    } else {
 +        Some((false, ""))
 +    }
 +}
 +
 +fn unop_bad(string: &Option<&str>, mut num: Option<i32>) {
 +    let _ = string.map_or(0, |s| s.len());
 +    let _ = num.as_ref().map_or(&0, |s| s);
 +    let _ = num.as_mut().map_or(&mut 0, |s| {
 +        *s += 1;
 +        s
 +    });
 +    let _ = num.as_ref().map_or(&0, |s| s);
 +    let _ = num.map_or(0, |mut s| {
 +        s += 1;
 +        s
 +    });
 +    let _ = num.as_mut().map_or(&mut 0, |s| {
 +        *s += 1;
 +        s
 +    });
 +}
 +
 +fn longer_body(arg: Option<u32>) -> u32 {
 +    arg.map_or(13, |x| {
 +        let y = x * x;
 +        y * y
 +    })
 +}
 +
 +fn impure_else(arg: Option<i32>) {
 +    let side_effect = || {
 +        println!("return 1");
 +        1
 +    };
 +    let _ = arg.map_or_else(|| side_effect(), |x| x);
 +}
 +
 +fn test_map_or_else(arg: Option<u32>) {
 +    let _ = arg.map_or_else(|| {
 +        let mut y = 1;
 +        y = (y + 2 / y) / 2;
 +        y = (y + 2 / y) / 2;
 +        y
 +    }, |x| x * x * x * x);
 +}
 +
 +fn negative_tests(arg: Option<u32>) -> u32 {
 +    let _ = if let Some(13) = arg { "unlucky" } else { "lucky" };
 +    for _ in 0..10 {
 +        let _ = if let Some(x) = arg {
 +            x
 +        } else {
 +            continue;
 +        };
 +    }
 +    let _ = if let Some(x) = arg {
 +        return x;
 +    } else {
 +        5
 +    };
 +    7
 +}
 +
 +fn main() {
 +    let optional = Some(5);
 +    let _ = optional.map_or(5, |x| x + 2);
 +    let _ = bad1(None);
 +    let _ = else_if_option(None);
 +    unop_bad(&None, None);
 +    let _ = longer_body(None);
 +    test_map_or_else(None);
 +    let _ = negative_tests(None);
 +    let _ = impure_else(None);
++
++    let _ = Some(0).map_or(0, |x| loop {
++            if x == 0 {
++                break x;
++            }
++        });
++
++    // #7576
++    const fn _f(x: Option<u32>) -> u32 {
++        // Don't lint, `map_or` isn't const
++        if let Some(x) = x { x } else { 10 }
++    }
++
++    // #5822
++    let s = String::new();
++    // Don't lint, `Some` branch consumes `s`, but else branch uses `s`
++    let _ = if let Some(x) = Some(0) {
++        let s = s;
++        s.len() + x
++    } else {
++        s.len()
++    };
++
++    let s = String::new();
++    // Lint, both branches immutably borrow `s`.
++    let _ = Some(0).map_or_else(|| s.len(), |x| s.len() + x);
++
++    let s = String::new();
++    // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
++    let _ = Some(0).map_or(1, |x| {
++        let s = s;
++        s.len() + x
++    });
++
++    let s = Some(String::new());
++    // Don't lint, `Some` branch borrows `s`, but else branch consumes `s`
++    let _ = if let Some(x) = &s {
++        x.len()
++    } else {
++        let _s = s;
++        10
++    };
++
++    let mut s = Some(String::new());
++    // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows  `s`
++    let _ = if let Some(x) = &mut s {
++        x.push_str("test");
++        x.len()
++    } else {
++        let _s = &s;
++        10
++    };
++
++    async fn _f1(x: u32) -> u32 {
++        x
++    }
++
++    async fn _f2() {
++        // Don't lint. `await` can't be moved into a closure.
++        let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
++    }
 +}
index e2f8dec3b930c898bafc52bf82acbf6852c48d7c,0000000000000000000000000000000000000000..a15627338cb4aaeaa8460c83d03f5e30f5c86c2d
mode 100644,000000..100644
--- /dev/null
@@@ -1,108 -1,0 +1,176 @@@
++// edition:2018
 +// run-rustfix
 +#![warn(clippy::option_if_let_else)]
 +#![allow(clippy::redundant_closure)]
 +#![allow(clippy::ref_option_ref)]
 +
 +fn bad1(string: Option<&str>) -> (bool, &str) {
 +    if let Some(x) = string {
 +        (true, x)
 +    } else {
 +        (false, "hello")
 +    }
 +}
 +
 +fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
 +    if string.is_none() {
 +        None
 +    } else if let Some(x) = string {
 +        Some((true, x))
 +    } else {
 +        Some((false, ""))
 +    }
 +}
 +
 +fn unop_bad(string: &Option<&str>, mut num: Option<i32>) {
 +    let _ = if let Some(s) = *string { s.len() } else { 0 };
 +    let _ = if let Some(s) = &num { s } else { &0 };
 +    let _ = if let Some(s) = &mut num {
 +        *s += 1;
 +        s
 +    } else {
 +        &mut 0
 +    };
 +    let _ = if let Some(ref s) = num { s } else { &0 };
 +    let _ = if let Some(mut s) = num {
 +        s += 1;
 +        s
 +    } else {
 +        0
 +    };
 +    let _ = if let Some(ref mut s) = num {
 +        *s += 1;
 +        s
 +    } else {
 +        &mut 0
 +    };
 +}
 +
 +fn longer_body(arg: Option<u32>) -> u32 {
 +    if let Some(x) = arg {
 +        let y = x * x;
 +        y * y
 +    } else {
 +        13
 +    }
 +}
 +
 +fn impure_else(arg: Option<i32>) {
 +    let side_effect = || {
 +        println!("return 1");
 +        1
 +    };
 +    let _ = if let Some(x) = arg {
 +        x
 +    } else {
 +        // map_or_else must be suggested
 +        side_effect()
 +    };
 +}
 +
 +fn test_map_or_else(arg: Option<u32>) {
 +    let _ = if let Some(x) = arg {
 +        x * x * x * x
 +    } else {
 +        let mut y = 1;
 +        y = (y + 2 / y) / 2;
 +        y = (y + 2 / y) / 2;
 +        y
 +    };
 +}
 +
 +fn negative_tests(arg: Option<u32>) -> u32 {
 +    let _ = if let Some(13) = arg { "unlucky" } else { "lucky" };
 +    for _ in 0..10 {
 +        let _ = if let Some(x) = arg {
 +            x
 +        } else {
 +            continue;
 +        };
 +    }
 +    let _ = if let Some(x) = arg {
 +        return x;
 +    } else {
 +        5
 +    };
 +    7
 +}
 +
 +fn main() {
 +    let optional = Some(5);
 +    let _ = if let Some(x) = optional { x + 2 } else { 5 };
 +    let _ = bad1(None);
 +    let _ = else_if_option(None);
 +    unop_bad(&None, None);
 +    let _ = longer_body(None);
 +    test_map_or_else(None);
 +    let _ = negative_tests(None);
 +    let _ = impure_else(None);
++
++    let _ = if let Some(x) = Some(0) {
++        loop {
++            if x == 0 {
++                break x;
++            }
++        }
++    } else {
++        0
++    };
++
++    // #7576
++    const fn _f(x: Option<u32>) -> u32 {
++        // Don't lint, `map_or` isn't const
++        if let Some(x) = x { x } else { 10 }
++    }
++
++    // #5822
++    let s = String::new();
++    // Don't lint, `Some` branch consumes `s`, but else branch uses `s`
++    let _ = if let Some(x) = Some(0) {
++        let s = s;
++        s.len() + x
++    } else {
++        s.len()
++    };
++
++    let s = String::new();
++    // Lint, both branches immutably borrow `s`.
++    let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
++
++    let s = String::new();
++    // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
++    let _ = if let Some(x) = Some(0) {
++        let s = s;
++        s.len() + x
++    } else {
++        1
++    };
++
++    let s = Some(String::new());
++    // Don't lint, `Some` branch borrows `s`, but else branch consumes `s`
++    let _ = if let Some(x) = &s {
++        x.len()
++    } else {
++        let _s = s;
++        10
++    };
++
++    let mut s = Some(String::new());
++    // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows  `s`
++    let _ = if let Some(x) = &mut s {
++        x.push_str("test");
++        x.len()
++    } else {
++        let _s = &s;
++        10
++    };
++
++    async fn _f1(x: u32) -> u32 {
++        x
++    }
++
++    async fn _f2() {
++        // Don't lint. `await` can't be moved into a closure.
++        let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
++    }
 +}
index 099e49ef8e3dc5e9b7762645ba8a0e400ada3405,0000000000000000000000000000000000000000..ed748ee8b39e4422c034f9a96982316dbb256745
mode 100644,000000..100644
--- /dev/null
@@@ -1,152 -1,0 +1,200 @@@
-   --> $DIR/option_if_let_else.rs:7:5
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:25:13
++  --> $DIR/option_if_let_else.rs:8:5
 +   |
 +LL | /     if let Some(x) = string {
 +LL | |         (true, x)
 +LL | |     } else {
 +LL | |         (false, "hello")
 +LL | |     }
 +   | |_____^ help: try: `string.map_or((false, "hello"), |x| (true, x))`
 +   |
 +   = note: `-D clippy::option-if-let-else` implied by `-D warnings`
 +
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:26:13
++  --> $DIR/option_if_let_else.rs:26:13
 +   |
 +LL |     let _ = if let Some(s) = *string { s.len() } else { 0 };
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())`
 +
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:27:13
++  --> $DIR/option_if_let_else.rs:27:13
 +   |
 +LL |     let _ = if let Some(s) = &num { s } else { &0 };
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
 +
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:33:13
++  --> $DIR/option_if_let_else.rs:28:13
 +   |
 +LL |       let _ = if let Some(s) = &mut num {
 +   |  _____________^
 +LL | |         *s += 1;
 +LL | |         s
 +LL | |     } else {
 +LL | |         &mut 0
 +LL | |     };
 +   | |_____^
 +   |
 +help: try
 +   |
 +LL ~     let _ = num.as_mut().map_or(&mut 0, |s| {
 +LL +         *s += 1;
 +LL +         s
 +LL ~     });
 +   |
 +
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:34:13
++  --> $DIR/option_if_let_else.rs:34:13
 +   |
 +LL |     let _ = if let Some(ref s) = num { s } else { &0 };
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
 +
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:40:13
++  --> $DIR/option_if_let_else.rs:35:13
 +   |
 +LL |       let _ = if let Some(mut s) = num {
 +   |  _____________^
 +LL | |         s += 1;
 +LL | |         s
 +LL | |     } else {
 +LL | |         0
 +LL | |     };
 +   | |_____^
 +   |
 +help: try
 +   |
 +LL ~     let _ = num.map_or(0, |mut s| {
 +LL +         s += 1;
 +LL +         s
 +LL ~     });
 +   |
 +
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:49:5
++  --> $DIR/option_if_let_else.rs:41:13
 +   |
 +LL |       let _ = if let Some(ref mut s) = num {
 +   |  _____________^
 +LL | |         *s += 1;
 +LL | |         s
 +LL | |     } else {
 +LL | |         &mut 0
 +LL | |     };
 +   | |_____^
 +   |
 +help: try
 +   |
 +LL ~     let _ = num.as_mut().map_or(&mut 0, |s| {
 +LL +         *s += 1;
 +LL +         s
 +LL ~     });
 +   |
 +
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:62:13
++  --> $DIR/option_if_let_else.rs:50:5
 +   |
 +LL | /     if let Some(x) = arg {
 +LL | |         let y = x * x;
 +LL | |         y * y
 +LL | |     } else {
 +LL | |         13
 +LL | |     }
 +   | |_____^
 +   |
 +help: try
 +   |
 +LL ~     arg.map_or(13, |x| {
 +LL +         let y = x * x;
 +LL +         y * y
 +LL +     })
 +   |
 +
 +error: use Option::map_or_else instead of an if let/else
-   --> $DIR/option_if_let_else.rs:71:13
++  --> $DIR/option_if_let_else.rs:63:13
 +   |
 +LL |       let _ = if let Some(x) = arg {
 +   |  _____________^
 +LL | |         x
 +LL | |     } else {
 +LL | |         // map_or_else must be suggested
 +LL | |         side_effect()
 +LL | |     };
 +   | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)`
 +
 +error: use Option::map_or_else instead of an if let/else
-   --> $DIR/option_if_let_else.rs:100:13
++  --> $DIR/option_if_let_else.rs:72:13
 +   |
 +LL |       let _ = if let Some(x) = arg {
 +   |  _____________^
 +LL | |         x * x * x * x
 +LL | |     } else {
 +LL | |         let mut y = 1;
 +...  |
 +LL | |         y
 +LL | |     };
 +   | |_____^
 +   |
 +help: try
 +   |
 +LL ~     let _ = arg.map_or_else(|| {
 +LL +         let mut y = 1;
 +LL +         y = (y + 2 / y) / 2;
 +LL +         y = (y + 2 / y) / 2;
 +LL +         y
 +LL ~     }, |x| x * x * x * x);
 +   |
 +
 +error: use Option::map_or instead of an if let/else
- error: aborting due to 11 previous errors
++  --> $DIR/option_if_let_else.rs:101:13
 +   |
 +LL |     let _ = if let Some(x) = optional { x + 2 } else { 5 };
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
 +
++error: use Option::map_or instead of an if let/else
++  --> $DIR/option_if_let_else.rs:110:13
++   |
++LL |       let _ = if let Some(x) = Some(0) {
++   |  _____________^
++LL | |         loop {
++LL | |             if x == 0 {
++LL | |                 break x;
++...  |
++LL | |         0
++LL | |     };
++   | |_____^
++   |
++help: try
++   |
++LL ~     let _ = Some(0).map_or(0, |x| loop {
++LL +             if x == 0 {
++LL +                 break x;
++LL +             }
++LL ~         });
++   |
++
++error: use Option::map_or_else instead of an if let/else
++  --> $DIR/option_if_let_else.rs:138:13
++   |
++LL |     let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or_else(|| s.len(), |x| s.len() + x)`
++
++error: use Option::map_or instead of an if let/else
++  --> $DIR/option_if_let_else.rs:142:13
++   |
++LL |       let _ = if let Some(x) = Some(0) {
++   |  _____________^
++LL | |         let s = s;
++LL | |         s.len() + x
++LL | |     } else {
++LL | |         1
++LL | |     };
++   | |_____^
++   |
++help: try
++   |
++LL ~     let _ = Some(0).map_or(1, |x| {
++LL +         let s = s;
++LL +         s.len() + x
++LL ~     });
++   |
++
++error: aborting due to 14 previous errors
 +
index 872cbc66af622669d21daa5ae1883d0ac4b97c40,0000000000000000000000000000000000000000..48fd58c9a49332cef60a8961fea442a958846978
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,11 @@@
- error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
++error: approximate value of `f{32, 64}::consts::PI` found
 +  --> $DIR/proc_macro.rs:10:14
 +   |
 +LL |     let _x = 3.14;
 +   |              ^^^^
 +   |
 +   = note: `#[deny(clippy::approx_constant)]` on by default
++   = help: consider using the constant directly
 +
 +error: aborting due to previous error
 +
index 657a3ecf6a006146575f6fe4d8b71c6a979a12f9,0000000000000000000000000000000000000000..18e8a2e01e0227cc02478346c42822a45e4b1314
mode 100644,000000..100644
--- /dev/null
@@@ -1,34 -1,0 +1,36 @@@
- #![allow(clippy::blacklisted_name)]
 +#![warn(clippy::rc_mutex)]
- pub struct MyStruct {
++#![allow(unused, clippy::blacklisted_name)]
 +
 +use std::rc::Rc;
 +use std::sync::Mutex;
 +
- pub fn test1<T>(foo: Rc<Mutex<T>>) {}
- pub fn test2(foo: Rc<Mutex<MyEnum>>) {}
++pub struct MyStructWithPrivItem {
 +    foo: Rc<Mutex<i32>>,
 +}
 +
++pub struct MyStructWithPubItem {
++    pub foo: Rc<Mutex<i32>>,
++}
++
 +pub struct SubT<T> {
 +    foo: T,
 +}
 +
 +pub enum MyEnum {
 +    One,
 +    Two,
 +}
 +
- pub fn test3(foo: Rc<Mutex<SubT<usize>>>) {}
++// All of these test should be trigger the lint because they are not
++// part of the public api
++fn test1<T>(foo: Rc<Mutex<T>>) {}
++fn test2(foo: Rc<Mutex<MyEnum>>) {}
++fn test3(foo: Rc<Mutex<SubT<usize>>>) {}
 +
- fn main() {
-     test1(Rc::new(Mutex::new(1)));
-     test2(Rc::new(Mutex::new(MyEnum::One)));
-     test3(Rc::new(Mutex::new(SubT { foo: 1 })));
-     let _my_struct = MyStruct {
-         foo: Rc::new(Mutex::new(1)),
-     };
- }
++// 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_test1<T>(foo: Rc<Mutex<T>>) {}
++pub fn pub_test2(foo: Rc<Mutex<MyEnum>>) {}
++pub fn pub_test3(foo: Rc<Mutex<SubT<usize>>>) {}
 +
++fn main() {}
index 8e58e2bc2d0bf1b5a66658cc4471dd1ea3ac8650,0000000000000000000000000000000000000000..fe84361d781622b746522ed52e138bac237b2f46
mode 100644,000000..100644
--- /dev/null
@@@ -1,28 -1,0 +1,35 @@@
- error: found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
++error: usage of `Rc<Mutex<_>>`
 +  --> $DIR/rc_mutex.rs:8:10
 +   |
 +LL |     foo: Rc<Mutex<i32>>,
 +   |          ^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::rc-mutex` implied by `-D warnings`
++   = help: consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
 +
- error: found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
-   --> $DIR/rc_mutex.rs:20:22
++error: usage of `Rc<Mutex<_>>`
++  --> $DIR/rc_mutex.rs:26:18
 +   |
- LL | pub fn test1<T>(foo: Rc<Mutex<T>>) {}
-    |                      ^^^^^^^^^^^^
++LL | fn test1<T>(foo: Rc<Mutex<T>>) {}
++   |                  ^^^^^^^^^^^^
++   |
++   = help: consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
 +
- error: found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
-   --> $DIR/rc_mutex.rs:22:19
++error: usage of `Rc<Mutex<_>>`
++  --> $DIR/rc_mutex.rs:27:15
++   |
++LL | fn test2(foo: Rc<Mutex<MyEnum>>) {}
++   |               ^^^^^^^^^^^^^^^^^
 +   |
- LL | pub fn test2(foo: Rc<Mutex<MyEnum>>) {}
-    |                   ^^^^^^^^^^^^^^^^^
++   = help: consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
 +
- error: found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
-   --> $DIR/rc_mutex.rs:24:19
++error: usage of `Rc<Mutex<_>>`
++  --> $DIR/rc_mutex.rs:28:15
++   |
++LL | fn test3(foo: Rc<Mutex<SubT<usize>>>) {}
++   |               ^^^^^^^^^^^^^^^^^^^^^^
 +   |
- LL | pub fn test3(foo: Rc<Mutex<SubT<usize>>>) {}
-    |                   ^^^^^^^^^^^^^^^^^^^^^^
++   = help: consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
 +
 +error: aborting due to 4 previous errors
 +
index 1b4f2a66c705ec088d5396bc3dbed187e12579df,0000000000000000000000000000000000000000..52fbc91e325555a9c808f57a47e76cc48e893363
mode 100644,000000..100644
--- /dev/null
@@@ -1,80 -1,0 +1,100 @@@
 +#![warn(clippy::all)]
 +#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
 +#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
 +#![allow(unused_imports)]
 +
 +pub struct MyStruct {}
 +
 +pub struct SubT<T> {
 +    foo: T,
 +}
 +
 +pub enum MyEnum {
 +    One,
 +    Two,
 +}
 +
 +mod outer_box {
 +    use crate::MyEnum;
 +    use crate::MyStruct;
 +    use crate::SubT;
 +    use std::boxed::Box;
 +    use std::rc::Rc;
 +    use std::sync::Arc;
 +
 +    pub fn box_test6<T>(foo: Box<Rc<T>>) {}
 +
 +    pub fn box_test7<T>(foo: Box<Arc<T>>) {}
 +
 +    pub fn box_test8() -> Box<Rc<SubT<usize>>> {
 +        unimplemented!();
 +    }
 +
 +    pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
 +        unimplemented!();
 +    }
 +}
 +
 +mod outer_rc {
 +    use crate::MyEnum;
 +    use crate::MyStruct;
 +    use crate::SubT;
 +    use std::boxed::Box;
 +    use std::rc::Rc;
 +    use std::sync::Arc;
 +
 +    pub fn rc_test5(a: Rc<Box<bool>>) {}
 +
 +    pub fn rc_test7(a: Rc<Arc<bool>>) {}
 +
 +    pub fn rc_test8() -> Rc<Box<SubT<usize>>> {
 +        unimplemented!();
 +    }
 +
 +    pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
 +        unimplemented!();
 +    }
 +}
 +
 +mod outer_arc {
 +    use crate::MyEnum;
 +    use crate::MyStruct;
 +    use crate::SubT;
 +    use std::boxed::Box;
 +    use std::rc::Rc;
 +    use std::sync::Arc;
 +
 +    pub fn arc_test5(a: Arc<Box<bool>>) {}
 +
 +    pub fn arc_test6(a: Arc<Rc<bool>>) {}
 +
 +    pub fn arc_test8() -> Arc<Box<SubT<usize>>> {
 +        unimplemented!();
 +    }
 +
 +    pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
 +        unimplemented!();
 +    }
 +}
 +
++// https://github.com/rust-lang/rust-clippy/issues/7487
++mod box_dyn {
++    use std::boxed::Box;
++    use std::rc::Rc;
++    use std::sync::Arc;
++
++    pub trait T {}
++
++    struct S {
++        a: Box<Box<dyn T>>,
++        b: Rc<Box<dyn T>>,
++        c: Arc<Box<dyn T>>,
++    }
++
++    pub fn test_box(_: Box<Box<dyn T>>) {}
++    pub fn test_rc(_: Rc<Box<dyn T>>) {}
++    pub fn test_arc(_: Arc<Box<dyn T>>) {}
++    pub fn test_rc_box(_: Rc<Box<Box<dyn T>>>) {}
++}
++
 +fn main() {}
index fdab74eb538e3e7c88cc341707db01439c91395b,0000000000000000000000000000000000000000..c3b10e5f5e679df879418117c4840aa6268b07dd
mode 100644,000000..100644
--- /dev/null
@@@ -1,138 -1,0 +1,147 @@@
- error: aborting due to 15 previous errors
 +error: usage of `Box<Rc<T>>`
 +  --> $DIR/redundant_allocation.rs:25:30
 +   |
 +LL |     pub fn box_test6<T>(foo: Box<Rc<T>>) {}
 +   |                              ^^^^^^^^^^
 +   |
 +   = note: `-D clippy::redundant-allocation` implied by `-D warnings`
 +   = note: `Rc<T>` is already on the heap, `Box<Rc<T>>` makes an extra allocation
 +   = help: consider using just `Box<T>` or `Rc<T>`
 +
 +error: usage of `Box<Arc<T>>`
 +  --> $DIR/redundant_allocation.rs:27:30
 +   |
 +LL |     pub fn box_test7<T>(foo: Box<Arc<T>>) {}
 +   |                              ^^^^^^^^^^^
 +   |
 +   = note: `Arc<T>` is already on the heap, `Box<Arc<T>>` makes an extra allocation
 +   = help: consider using just `Box<T>` or `Arc<T>`
 +
 +error: usage of `Box<Rc<SubT<usize>>>`
 +  --> $DIR/redundant_allocation.rs:29:27
 +   |
 +LL |     pub fn box_test8() -> Box<Rc<SubT<usize>>> {
 +   |                           ^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `Rc<SubT<usize>>` is already on the heap, `Box<Rc<SubT<usize>>>` makes an extra allocation
 +   = help: consider using just `Box<SubT<usize>>` or `Rc<SubT<usize>>`
 +
 +error: usage of `Box<Arc<T>>`
 +  --> $DIR/redundant_allocation.rs:33:30
 +   |
 +LL |     pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
 +   |                              ^^^^^^^^^^^
 +   |
 +   = note: `Arc<T>` is already on the heap, `Box<Arc<T>>` makes an extra allocation
 +   = help: consider using just `Box<T>` or `Arc<T>`
 +
 +error: usage of `Box<Arc<SubT<T>>>`
 +  --> $DIR/redundant_allocation.rs:33:46
 +   |
 +LL |     pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
 +   |                                              ^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `Arc<SubT<T>>` is already on the heap, `Box<Arc<SubT<T>>>` makes an extra allocation
 +   = help: consider using just `Box<SubT<T>>` or `Arc<SubT<T>>`
 +
 +error: usage of `Rc<Box<bool>>`
 +  --> $DIR/redundant_allocation.rs:46:24
 +   |
 +LL |     pub fn rc_test5(a: Rc<Box<bool>>) {}
 +   |                        ^^^^^^^^^^^^^
 +   |
 +   = note: `Box<bool>` is already on the heap, `Rc<Box<bool>>` makes an extra allocation
 +   = help: consider using just `Rc<bool>` or `Box<bool>`
 +
 +error: usage of `Rc<Arc<bool>>`
 +  --> $DIR/redundant_allocation.rs:48:24
 +   |
 +LL |     pub fn rc_test7(a: Rc<Arc<bool>>) {}
 +   |                        ^^^^^^^^^^^^^
 +   |
 +   = note: `Arc<bool>` is already on the heap, `Rc<Arc<bool>>` makes an extra allocation
 +   = help: consider using just `Rc<bool>` or `Arc<bool>`
 +
 +error: usage of `Rc<Box<SubT<usize>>>`
 +  --> $DIR/redundant_allocation.rs:50:26
 +   |
 +LL |     pub fn rc_test8() -> Rc<Box<SubT<usize>>> {
 +   |                          ^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `Box<SubT<usize>>` is already on the heap, `Rc<Box<SubT<usize>>>` makes an extra allocation
 +   = help: consider using just `Rc<SubT<usize>>` or `Box<SubT<usize>>`
 +
 +error: usage of `Rc<Arc<T>>`
 +  --> $DIR/redundant_allocation.rs:54:29
 +   |
 +LL |     pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
 +   |                             ^^^^^^^^^^
 +   |
 +   = note: `Arc<T>` is already on the heap, `Rc<Arc<T>>` makes an extra allocation
 +   = help: consider using just `Rc<T>` or `Arc<T>`
 +
 +error: usage of `Rc<Arc<SubT<T>>>`
 +  --> $DIR/redundant_allocation.rs:54:44
 +   |
 +LL |     pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
 +   |                                            ^^^^^^^^^^^^^^^^
 +   |
 +   = note: `Arc<SubT<T>>` is already on the heap, `Rc<Arc<SubT<T>>>` makes an extra allocation
 +   = help: consider using just `Rc<SubT<T>>` or `Arc<SubT<T>>`
 +
 +error: usage of `Arc<Box<bool>>`
 +  --> $DIR/redundant_allocation.rs:67:25
 +   |
 +LL |     pub fn arc_test5(a: Arc<Box<bool>>) {}
 +   |                         ^^^^^^^^^^^^^^
 +   |
 +   = note: `Box<bool>` is already on the heap, `Arc<Box<bool>>` makes an extra allocation
 +   = help: consider using just `Arc<bool>` or `Box<bool>`
 +
 +error: usage of `Arc<Rc<bool>>`
 +  --> $DIR/redundant_allocation.rs:69:25
 +   |
 +LL |     pub fn arc_test6(a: Arc<Rc<bool>>) {}
 +   |                         ^^^^^^^^^^^^^
 +   |
 +   = note: `Rc<bool>` is already on the heap, `Arc<Rc<bool>>` makes an extra allocation
 +   = help: consider using just `Arc<bool>` or `Rc<bool>`
 +
 +error: usage of `Arc<Box<SubT<usize>>>`
 +  --> $DIR/redundant_allocation.rs:71:27
 +   |
 +LL |     pub fn arc_test8() -> Arc<Box<SubT<usize>>> {
 +   |                           ^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `Box<SubT<usize>>` is already on the heap, `Arc<Box<SubT<usize>>>` makes an extra allocation
 +   = help: consider using just `Arc<SubT<usize>>` or `Box<SubT<usize>>`
 +
 +error: usage of `Arc<Rc<T>>`
 +  --> $DIR/redundant_allocation.rs:75:30
 +   |
 +LL |     pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
 +   |                              ^^^^^^^^^^
 +   |
 +   = note: `Rc<T>` is already on the heap, `Arc<Rc<T>>` makes an extra allocation
 +   = help: consider using just `Arc<T>` or `Rc<T>`
 +
 +error: usage of `Arc<Rc<SubT<T>>>`
 +  --> $DIR/redundant_allocation.rs:75:45
 +   |
 +LL |     pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
 +   |                                             ^^^^^^^^^^^^^^^^
 +   |
 +   = note: `Rc<SubT<T>>` is already on the heap, `Arc<Rc<SubT<T>>>` makes an extra allocation
 +   = help: consider using just `Arc<SubT<T>>` or `Rc<SubT<T>>`
 +
++error: usage of `Rc<Box<Box<dyn T>>>`
++  --> $DIR/redundant_allocation.rs:97:27
++   |
++LL |     pub fn test_rc_box(_: Rc<Box<Box<dyn T>>>) {}
++   |                           ^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `Box<Box<dyn T>>` is already on the heap, `Rc<Box<Box<dyn T>>>` makes an extra allocation
++   = help: consider using just `Rc<Box<dyn T>>` or `Box<Box<dyn T>>`
++
++error: aborting due to 16 previous errors
 +
index 2fca96c4cd556654db75f3f0b58ab24c0c9d028a,0000000000000000000000000000000000000000..bf0ec8deb3458bccdc65a4151a8237241f6a0626
mode 100644,000000..100644
--- /dev/null
@@@ -1,79 -1,0 +1,79 @@@
-     [42, 55];get_usize();
 +// run-rustfix
 +
 +#![feature(box_syntax)]
 +#![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)]
 +#![warn(clippy::unnecessary_operation)]
 +
 +struct Tuple(i32);
 +struct Struct {
 +    field: i32,
 +}
 +enum Enum {
 +    Tuple(i32),
 +    Struct { field: i32 },
 +}
 +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,
 +}
 +
 +fn get_number() -> i32 {
 +    0
 +}
 +
 +fn get_usize() -> usize {
 +    0
 +}
 +fn get_struct() -> Struct {
 +    Struct { field: 0 }
 +}
 +fn get_drop_struct() -> DropStruct {
 +    DropStruct { field: 0 }
 +}
 +
 +fn main() {
 +    get_number();
 +    get_number();
 +    get_struct();
 +    get_number();
 +    get_number();
 +    5;get_number();
 +    get_number();
 +    get_number();
 +    5;6;get_number();
 +    get_number();
 +    get_number();
 +    get_number();
 +    5;get_number();
 +    42;get_number();
-     [42; 55];get_usize();
++    assert!([42, 55].len() > get_usize());
 +    42;get_number();
 +    get_number();
++    assert!([42; 55].len() > get_usize());
 +    get_number();
 +    String::from("blah");
 +
 +    // Do not warn
 +    DropTuple(get_number());
 +    DropStruct { field: get_number() };
 +    DropStruct { field: get_number() };
 +    DropStruct { ..get_drop_struct() };
 +    DropEnum::Tuple(get_number());
 +    DropEnum::Struct { field: get_number() };
 +}
index f88c9f9908beabdf4c2bef4afaebf708ee533059,0000000000000000000000000000000000000000..f66d08ecb8281bb9f5cd357b6482ebb8b9f9d714
mode 100644,000000..100644
--- /dev/null
@@@ -1,128 -1,0 +1,128 @@@
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:51:5
 +   |
 +LL |     Tuple(get_number());
-    |     ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++   |     ^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 +   |
 +   = note: `-D clippy::unnecessary-operation` implied by `-D warnings`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:52:5
 +   |
 +LL |     Struct { field: get_number() };
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:53:5
 +   |
 +LL |     Struct { ..get_struct() };
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_struct();`
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_struct();`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:54:5
 +   |
 +LL |     Enum::Tuple(get_number());
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:55:5
 +   |
 +LL |     Enum::Struct { field: get_number() };
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:56:5
 +   |
 +LL |     5 + get_number();
-    |     ^^^^^^^^^^^^^^^^^ help: replace it with: `5;get_number();`
++   |     ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:57:5
 +   |
 +LL |     *&get_number();
-    |     ^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++   |     ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:58:5
 +   |
 +LL |     &get_number();
-    |     ^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++   |     ^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:59:5
 +   |
 +LL |     (5, 6, get_number());
-    |     ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `5;6;get_number();`
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;6;get_number();`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:60:5
 +   |
 +LL |     box get_number();
-    |     ^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++   |     ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:61:5
 +   |
 +LL |     get_number()..;
-    |     ^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++   |     ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:62:5
 +   |
 +LL |     ..get_number();
-    |     ^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++   |     ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:63:5
 +   |
 +LL |     5..get_number();
-    |     ^^^^^^^^^^^^^^^^ help: replace it with: `5;get_number();`
++   |     ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:64:5
 +   |
 +LL |     [42, get_number()];
-    |     ^^^^^^^^^^^^^^^^^^^ help: replace it with: `42;get_number();`
++   |     ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:65:5
 +   |
 +LL |     [42, 55][get_usize()];
-    |     ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `[42, 55];get_usize();`
++   |     ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:66:5
 +   |
 +LL |     (42, get_number()).1;
-    |     ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `42;get_number();`
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:67:5
 +   |
 +LL |     [get_number(); 55];
-    |     ^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++   |     ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:68:5
 +   |
 +LL |     [42; 55][get_usize()];
-    |     ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `[42; 55];get_usize();`
++   |     ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42; 55].len() > get_usize());`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:69:5
 +   |
 +LL | /     {
 +LL | |         get_number()
 +LL | |     };
-    | |______^ help: replace it with: `get_number();`
++   | |______^ help: statement can be reduced to: `get_number();`
 +
- error: statement can be reduced
++error: unnecessary operation
 +  --> $DIR/unnecessary_operation.rs:72:5
 +   |
 +LL | /     FooString {
 +LL | |         s: String::from("blah"),
 +LL | |     };
-    | |______^ help: replace it with: `String::from("blah");`
++   | |______^ help: statement can be reduced to: `String::from("blah");`
 +
 +error: aborting due to 20 previous errors
 +
index 1eaec4a50a6a6396f51a1d15969be3cd580c3190,0000000000000000000000000000000000000000..77102b8cac0c977e9621dc9bcc1bf1c6bcb5c11d
mode 100644,000000..100644
--- /dev/null
@@@ -1,88 -1,0 +1,91 @@@
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++#![warn(rust_2018_idioms, unused_lifetimes)]
 +#![allow(clippy::single_match_else)]
++
 +use rustc_tools_util::VersionInfo;
 +
 +#[test]
 +fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() {
 +    // do not run this test inside the upstream rustc repo:
 +    // https://github.com/rust-lang/rust-clippy/issues/6683
 +    if option_env!("RUSTC_TEST_SUITE").is_some() {
 +        return;
 +    }
 +
 +    let clippy_meta = cargo_metadata::MetadataCommand::new()
 +        .no_deps()
 +        .exec()
 +        .expect("could not obtain cargo metadata");
 +
 +    for krate in &["clippy_lints", "clippy_utils"] {
 +        let krate_meta = cargo_metadata::MetadataCommand::new()
 +            .current_dir(std::env::current_dir().unwrap().join(krate))
 +            .no_deps()
 +            .exec()
 +            .expect("could not obtain cargo metadata");
 +        assert_eq!(krate_meta.packages[0].version, clippy_meta.packages[0].version);
 +        for package in &clippy_meta.packages[0].dependencies {
 +            if package.name == *krate {
 +                assert!(package.req.matches(&krate_meta.packages[0].version));
 +                break;
 +            }
 +        }
 +    }
 +}
 +
 +#[test]
 +fn check_that_clippy_has_the_same_major_version_as_rustc() {
 +    // do not run this test inside the upstream rustc repo:
 +    // https://github.com/rust-lang/rust-clippy/issues/6683
 +    if option_env!("RUSTC_TEST_SUITE").is_some() {
 +        return;
 +    }
 +
 +    let clippy_version = rustc_tools_util::get_version_info!();
 +    let clippy_major = clippy_version.major;
 +    let clippy_minor = clippy_version.minor;
 +    let clippy_patch = clippy_version.patch;
 +
 +    // get the rustc version either from the rustc installed with the toolchain file or from
 +    // `RUSTC_REAL` if Clippy is build in the Rust repo with `./x.py`.
 +    let rustc = std::env::var("RUSTC_REAL").unwrap_or_else(|_| "rustc".to_string());
 +    let rustc_version = String::from_utf8(
 +        std::process::Command::new(&rustc)
 +            .arg("--version")
 +            .output()
 +            .expect("failed to run `rustc --version`")
 +            .stdout,
 +    )
 +    .unwrap();
 +    // extract "1 XX 0" from "rustc 1.XX.0-nightly (<commit> <date>)"
 +    let vsplit: Vec<&str> = rustc_version
 +        .split(' ')
 +        .nth(1)
 +        .unwrap()
 +        .split('-')
 +        .next()
 +        .unwrap()
 +        .split('.')
 +        .collect();
 +    match vsplit.as_slice() {
 +        [rustc_major, rustc_minor, _rustc_patch] => {
 +            // clippy 0.1.XX should correspond to rustc 1.XX.0
 +            assert_eq!(clippy_major, 0); // this will probably stay the same for a long time
 +            assert_eq!(
 +                clippy_minor.to_string(),
 +                *rustc_major,
 +                "clippy minor version does not equal rustc major version"
 +            );
 +            assert_eq!(
 +                clippy_patch.to_string(),
 +                *rustc_minor,
 +                "clippy patch version does not equal rustc minor version"
 +            );
 +            // do not check rustc_patch because when a stable-patch-release is made (like 1.50.2),
 +            // we don't want our tests failing suddenly
 +        },
 +        _ => {
 +            panic!("Failed to parse rustc version: {:?}", vsplit);
 +        },
 +    };
 +}